Drag and Drop Listbox Items






4.97/5 (11 votes)
Rearrange listbox items using drag and drop without OLE.
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; ...... };