Introduction
There have been already several articles about this topic. But I saw none written in C (no C++ code). For those who insist in traditional C programming, I'd like to provide a from-A-to-Z tutorial to make you easy to add this user-friendly feature to your application.
The code
All the below codes are assumed to be in your MainWndProc()
function.
When user keeps pressing the mouse button down, and moves his mouse a little, a notification message (LVN_BEGINDRAG
) will be sent to your MainWndProc()
via WM_NOTIFY
. You should handle this message by:
- Create a drag-image for all selected items.
ListView
provides a function to create a single-row drag-image. We will use this function to create multiple drag-images for each row, and merge them together.
- Call
ImageList_Begin
to initialize a drag-and-drop, then ImageList_DragEnter
to start a drag action. You must call SetCapture()
to get the subsequent mouse message inside your application.
Example code segment:
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code) {
case LVN_BEGINDRAG:
p.x = 8;
p.y = 8;
bFirst = TRUE;
iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
while (iPos != -1) {
if (bFirst) {
hDragImageList = ListView_CreateDragImage(hListView, iPos, &p);
ImageList_GetImageInfo(hDragImageList, 0, &imf);
iHeight = imf.rcImage.bottom;
bFirst = FALSE;
hOneImageList = ListView_CreateDragImage(hListView, iPos, &p);
hTempImageList = ImageList_Merge(hDragImageList,
0, hOneImageList, 0, 0, iHeight);
ImageList_Destroy(hDragImageList);
ImageList_Destroy(hOneImageList);
hDragImageList = hTempImageList;
ImageList_GetImageInfo(hDragImageList, 0, &imf);
iHeight = imf.rcImage.bottom;
}
iPos = ListView_GetNextItem(hListView, iPos, LVNI_SELECTED);
}
ImageList_BeginDrag(hDragImageList, 0, 0, 0);
pt = ((NM_LISTVIEW*) ((LPNMHDR)lParam))->ptAction;
ClientToScreen(hListView, &pt);
ImageList_DragEnter(GetDesktopWindow(), pt.x, pt.y);
bDragging = TRUE;
SetCapture(hWndMain);
break;
Then we handle WM_MOUSEMOVE
message to move the drag-image along the mouse movement. There is no need to explain the example code segment:
case WM_MOUSEMOVE:
if (!bDragging)
break;
p.x = LOWORD(lParam);
p.y = HIWORD(lParam);
ClientToScreen(hWndMain, &p);
ImageList_DragMove(p.x, p.y);
break;
OK, now we reach the final step. When user releases the mouse button, we will end the drag-and-drop action and do the rearrangement work.
- Call
ImageList_DragLeave
and Image_EndDrag
to end the drag-and-drop process. Don't forget to release the mouse capture and destroy the drag image to free the memory it uses.
- Determine the item onto where the user drops the selected items. If the dropped item is selected, you should terminate.
- Move all the selected items to the dropped item's position by copying and deleting.
Here's the example code segment:
case WM_LBUTTONUP:
bDragging = FALSE;
ImageList_DragLeave(hListView);
ImageList_EndDrag();
ImageList_Destroy(hDragImageList);
ReleaseCapture();
lvhti.pt.x = LOWORD(lParam);
lvhti.pt.y = HIWORD(lParam);
ClientToScreen(hWndMain, &lvhti.pt);
ScreenToClient(hListView, &lvhti.pt);
ListView_HitTest(hListView, &lvhti);
if (lvhti.iItem == -1)
break;
if ((lvhti.flags & LVHT_ONITEMLABEL == 0) &&
(lvhti.flags & LVHT_ONITEMSTATEICON == 0))
break;
lvi.iItem = lvhti.iItem;
lvi.iSubItem = 0;
lvi.mask = LVIF_STATE;
lvi.stateMask = LVIS_SELECTED;
ListView_GetItem(hListView, &lvi);
if (lvi.state & LVIS_SELECTED)
break;
iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
while (iPos != -1) {
lvi.iItem = iPos;
lvi.iSubItem = 0;
lvi.cchTextMax = MAX_TARGET_LEN;
lvi.pszText = buf;
lvi.stateMask = ~LVIS_SELECTED;
lvi.mask = LVIF_STATE | LVIF_IMAGE
| LVIF_INDENT | LVIF_PARAM | LVIF_TEXT;
ListView_GetItem(hListView, &lvi);
lvi.iItem = lvhti.iItem;
iRet = ListView_InsertItem(hListView, &lvi);
if (lvi.iItem < iPos)
lvhti.iItem++;
if (iRet <= iPos)
iPos++;
for (i = 1; i < JOB_WIN_COLUMN_NUM; i++) {
ListView_GetItemText(hListView, iPos,
i, buf, MAX_TARGET_LEN);
ListView_SetItemText(hListView, iRet, i, buf);
}
ListView_DeleteItem(hListView, iPos);
iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
}
break;
Here are variable definitions:
int iHeight;
static HIMAGELIST hDragImageList;
HIMAGELIST hOneImageList, hTempImageList;
LPNMHDR pnmhdr;
static BOOL bDragging;
LVHITTESTINFO lvhti;
BOOL bFirst;
IMAGEINFO imf;
And of course, hListView
and hMainWnd
are HWND
variables.
Phew ... Implementing all of these in Win32 API is not that difficult, is it?
History
- Last Updated: Aug 16, 2001
- Date Posted: Aug 10, 2001