Drag and Drop Listbox Items Without OLE

This listbox class demonstrates how to rearrange listbox items using Drag and Drop without the overhead of OLE. (If you are trying to drag items from one ListBox to another, refer to the OLE verion of this article.)

MFC has a CDragListBox that virtually does the same thing. The advantage of this class over MFC's CDragListBox is that it allows the user to insert the item at the end of the list, whereas CDragListBox does not. 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 initializes 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:

  1. Capture the mouse if it hasn't done so.
  2. Scroll the listbox if the user has dragged the mouse above or below the listbox window (it does this with the help of timers).
  3. 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, simply
         //select the last item as the drop point; else, if
         //they are outside the window, 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, there is nothing to do. If, on the other hand, it was released within the window, 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;
   
......
};


About the Author

Ali Rafiee

Ali Rafiee has been developing windows applications for the past 14 years using Visual C++, and he hasn't looked back since. Ali has been a software development consultant for must of his career, but he has finally settled down and has been working for an educational software company for the past 7 years. While he is not working, he is either learning C#, flying airplanes, playing with his daughter, or answering peoples question on newsgroups, he finds that to be a great learning tool for him (He is always trying to learn something new).

Downloads

Comments

  • drag and drop listbox items

    Posted by uday on 01/16/2013 10:35pm

    don't we have any other option like without creating extra class for this.

    Reply
  • Fatal error

    Posted by satishmanohar on 01/23/2008 02:02am

    fatal error C1083: Cannot open include file: 'stdafx.h': No such file or directory Error executing cl.exe. DragAndDropListBox.exe - 1 error(s), 0 warning(s) I compiled using visual studio 6.0.. can u suggest a remedy for that???

    • Re: Fatal error

      Posted by AliRafiee on 01/23/2008 02:40am

      Stdafx.h is the header file used for precompiled headers. My sample project is a visual c++ 2003 project. If you want to port it to 6.0 the easiest thing would be to create a new MFC project, and then add .cpp .h and .rc files from my project to that project. It is a little tricky with the rc file. You might want to simply copy and paste the code for the dialog box from the sample projects rc file to your rc file instead of copying the entire file.

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Enterprises today must focus on digital transformation to remain competitive or disrupt their industries. The foundation for successful transformation is the adoption of a cloud-first mindset. However, IT organizations must first address legacy infrastructure and fragmented management tools that were not designed for the speed and flexibility of the cloud and digital era. Read this IDC Technology Spotlight paper to explore: Why digital transformation is driving a shift to a cloud-centric enterprise Key …

  • Microsoft® Office 365 is a top choice for enterprises that want a cloud–based suite of productivity collaboration applications. With Office 365, you get access to Microsoft™ Office solutions practically anytime, anywhere, on virtually any device. It's a great option for current Microsoft users who can now build on their experience with Microsoft™ solutions while enjoying the flexibility of a cloud-based delivery. But even organizations with no previous investment in Microsoft will find that …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date