Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Drag and Drop Listbox Items

4.97/5 (12 votes)
1 Dec 2005CPOL2 min read 1   3.5K  
Rearrange listbox items using drag and drop without OLE.

Drag and Drop Listbox

Introduction

This listbox class demonstrates how to rearrange listbox items using Drag and Drop without the overhead of OLE. The advantage of this method is that you don't have to create bulky global memory handles to simply move data within the same window in the same application. The concept is very simple: keep track of the item that is being dragged, indicate where the drop will be when the user is dragging the item around, and finally, insert the item in its new location once the user releases the mouse button.

To accomplish this task, you will need to catch three messages for your listbox window: WM_LBUTTONDOWN, WM_MOUSEMOVE, and WM_LBUTTONUP.

The WM_LBUTTONDOWN handler method simply initialized the drag and drop process by finding the item that the user clicked on.

void CDragAndDropListBox::OnLButtonDown(UINT nFlags, CPoint point)
{
   CListBox::OnLButtonDown(nFlags, point);

   //clear all the flags
   m_MovingIndex = LB_ERR;
   m_MoveToIndex = LB_ERR;
   m_Captured = FALSE;
   m_Interval = 0;
   
   BOOL Outside;
   //find the item that they want to drag
   //and keep track of it. Later in the mouse move
   //we will capture the mouse if this value is set
   int Index = ItemFromPoint(point,Outside);
   if (Index != LB_ERR && !Outside)
   {
      m_MovingIndex = Index;
      SetCurSel(Index);
   }
}

The WM_WMMOUSEMOVE handler method does three things. One is to capture the mouse if it hasn't done so. Two is to scroll the listbox if the user has dragged the mouse above or below the listbox window (it does this with the help of timers). And finally, to draw a line above the item where the dragged item will be dropped, it also keeps track of the item's index value so that WM_LBUTTONUP does not have to find the information again.

void CDragAndDropListBox::OnMouseMove(UINT nFlags, CPoint point)
{
   CListBox::OnMouseMove(nFlags, point);
   if (nFlags & MK_LBUTTON)
   {
      if (m_MovingIndex != LB_ERR && !m_Captured)
      {
         SetCapture();
         m_Captured = TRUE;
      }
      BOOL Outside;
      int Index = ItemFromPoint(point,Outside);
      //if they our not on a particular item
      if (Outside)
      {
         CRect ClientRect;
         GetClientRect(&ClientRect);

         //if they are still within the listbox window, then
         //simply select the last item as the drop point
         //else if they are outside the window then scroll the items
         if (ClientRect.PtInRect(point))
         {
            KillTimer(TID_SCROLLDOWN);
            KillTimer(TID_SCROLLUP);
            m_Interval = 0;
            //indicates that the user wants to drop the item
            //at the end of the list
            Index = LB_ERR;
            Outside = FALSE;
         }
         else
         {
            DoTheScrolling(point,ClientRect);
         }
      }
      else
      {
         KillTimer(TID_SCROLLDOWN);
         KillTimer(TID_SCROLLUP);
         m_Interval = 0;
      }
      
      if (Index != m_MoveToIndex && !Outside)
      {
         DrawTheLines(Index);
      }
   }
}

Next, the WM_LBUTTONUP message handler will do the actual drop operation. It first checks to make sure that there was a item being dragged to begin with. Next it checks to make sure that the button was released within the listbox window. If it wasn't then there is nothing to do. If on the other hand it was released within the window, then simply insert the item into the listbox at the index indicated by the WM_MOUSEMOVE handler.

void CDragAndDropListBox::OnLButtonUp(UINT nFlags, CPoint point)
{
   if (m_MovingIndex != LB_ERR && m_Captured)
   {
      KillTimer(TID_SCROLLDOWN);
      KillTimer(TID_SCROLLUP);
      m_Interval = 0;
      m_Captured = FALSE;
      ReleaseCapture();

      CRect Rect;
      GetClientRect(&Rect);
      //if they are still within the listbox window
      if (Rect.PtInRect(point))
      {
         InsertDraggedItem();
      }
      else
      {
         Invalidate();
         UpdateWindow();
      }
      m_MovingIndex = LB_ERR;
      m_MoveToIndex = LB_ERR;
   }

   CListBox::OnLButtonUp(nFlags, point);
}

Using the code

To use this class, simply attach a variable (subclass) to the listbox control on your window, and then change the variable from CListBox to CDragAndDropListBox. That's all.

// CDragDropListBoxSampleDlg dialog
class CDragDropListBoxSampleDlg : public CDialog
{
......

// Implementation
protected:
   CDragAndDropListBox  m_ListBox;
   
......
};

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)