Click here to Skip to main content
Click here to Skip to main content

Rearrange rows in a ListView control by drag-and-drop without MFC/C++

, 16 Aug 2001
Rate this:
Please Sign up or sign in to vote.
Teach you how to rearrange rows in a ListView control by drag-and-drop without MFC/C++

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:

  1. 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.
  2. 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:

    // You can set your customized cursor here

    p.x = 8;
    p.y = 8;

    // Ok, now we create a drag-image for all selected items
    bFirst = TRUE;
    iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
    while (iPos != -1) {
        if (bFirst) {
            // For the first selected item,
            // we simply create a single-line drag image
            hDragImageList = ListView_CreateDragImage(hListView, iPos, &p);
            ImageList_GetImageInfo(hDragImageList, 0, &imf);
            iHeight = imf.rcImage.bottom;
            bFirst = FALSE;
        }else {
            // For the rest selected items,
            // we create a single-line drag image, then
            // append it to the bottom of the complete drag image
            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);
    }

    // Now we can initialize then start the drag action
    ImageList_BeginDrag(hDragImageList, 0, 0, 0);

    pt = ((NM_LISTVIEW*) ((LPNMHDR)lParam))->ptAction;
    ClientToScreen(hListView, &pt);

    ImageList_DragEnter(GetDesktopWindow(), pt.x, pt.y);

    bDragging = TRUE;

    // Don't forget to capture the mouse
    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.

  1. 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.
  2. Determine the item onto where the user drops the selected items. If the dropped item is selected, you should terminate.
  3. Move all the selected items to the dropped item's position by copying and deleting.

Here's the example code segment:

    case WM_LBUTTONUP:

        // End the drag-and-drop process
        bDragging = FALSE;
        ImageList_DragLeave(hListView);
        ImageList_EndDrag();
        ImageList_Destroy(hDragImageList);

        ReleaseCapture();

        // Determine the dropped item
        lvhti.pt.x = LOWORD(lParam);
        lvhti.pt.y = HIWORD(lParam);
        ClientToScreen(hWndMain, &lvhti.pt);
        ScreenToClient(hListView, &lvhti.pt);
        ListView_HitTest(hListView, &lvhti);

        // Out of the ListView?
        if (lvhti.iItem == -1)
            break;
        // Not in an item?
        if ((lvhti.flags & LVHT_ONITEMLABEL == 0) && 
                  (lvhti.flags & LVHT_ONITEMSTATEICON == 0))
            break;

        // Dropped item is selected?
        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;

        // Rearrange the items
        iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
        while (iPos != -1) {
            // First, copy one item
            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;
            // Insert the main item
            iRet = ListView_InsertItem(hListView, &lvi);
            if (lvi.iItem < iPos)
                lvhti.iItem++;
            if (iRet <= iPos)
                iPos++;
            // Set the subitem text
            for (i = 1; i < JOB_WIN_COLUMN_NUM; i++) {
                ListView_GetItemText(hListView, iPos, 
                               i, buf, MAX_TARGET_LEN);
                ListView_SetItemText(hListView, iRet, i, buf);
            }
            // Delete from original position
            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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Jiang Hong

China China
No Biography provided

Comments and Discussions

 
GeneralAnd of course, hListView and hMainWnd are HWND variables. Pinmembercarabutnicolae123421-Nov-07 12:06 
GeneralSmall detail Pinmembersh33pm4n23-Jan-07 5:47 
GeneralVery helpful code Pinmembercvarak11-Sep-06 4:08 
GeneralGrid Mod PinmemberAndy_AJ17-Feb-06 7:50 
GeneralImageList_Merge requires ImageList to have masks Pinmembercodemincer3-Nov-05 11:23 
QuestionOnly the first itemtext was moved? PinmemberJohn Wong8-Sep-02 1:57 
Generalcrashes when big number of items selected Pinsusssomebody18-Jul-02 2:50 
Generaldrag & drop between two listview PinmemberMagnan Pierre Henri29-Jan-02 5:22 
GeneralLittle but critical error in WM_LBUTTONUP PinmemberAnonymous17-Aug-01 2:26 
GeneralRe: Little but critical error in WM_LBUTTONUP PinmemberJiang Hong17-Aug-01 18:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 17 Aug 2001
Article Copyright 2001 by Jiang Hong
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid