DragToolBar - Drag items from Toolbar and drop into view

The simplest way

The main problem preventing us to drag an item from toolbar is that toolbar does not respond to drag action. Therefore, we should hand and send correspondent messages ourself. To do this, we need derive a new class CDragToolBar from CToolBar, then hand the LButtonDown event as following:

void CDragToolBar::OnLButtonDown(UINT nFlags, CPoint point) 
{
   int nCount = GetToolBarCtrl().GetButtonCount();
   for (int i=0; i<nCount; i++) {
   CRect rect;
      GetItemRect(i, rect);
      if (rect.PtInRect(point)) {
         PostMessage(WM_COMMAND, GetItemID(i));
         return;
      }
   }
      
   CToolBar::OnLButtonDown(nFlags, point);
}

Now, in the view, we capture all events of toolbar and set program into drag mode:

void CDTBView::OnDragToolBar(UINT nCmdID)
{
   m_dragBtn = nCmdID;
   BeginDrag ();
}

In my application, I implement drag and drop via CImageList.

That method is work fine. However, the problem is that the drag button of toolbar is not repainted correctly because of disturbing of drag cursor (it captures the picture of button before updating and then repaints it). We need a little improvement.


Further improvement

Instead of entering drag mode right after receiving the messages, program just records the drag button event, and let little time for toolbar to update the button status. To ensure that, we capture the mousemove event and check if the mouse moves far enough. If everything is ok, send another message to tell program to enter the drag mode:

void CDragToolBar::OnLButtonDown(UINT nFlags, CPoint point) 
{
   int nCount = GetToolBarCtrl().GetButtonCount();
   for (int i=0; i<nCount; i++) {
      CRect rect;
      GetItemRect(i, rect);
      if (rect.PtInRect(point)) {
         PostMessage(WM_COMMAND, GetItemID(i));
         m_dragMode = TRUE;
         m_point = point;
         return;
      }
   }
      
   CToolBar::OnLButtonDown(nFlags, point);
}

void CDragToolBar::OnMouseMove(UINT nFlags, CPoint point) 
{
   if (m_dragMode && 
             abs(m_point.x-point.x)+abs(m_point.y-point.y)>=3) {
      PostMessage(WM_COMMAND, ID_START_DRAG);
      m_dragMode = FALSE;
   }
   CToolBar::OnMouseMove(nFlags, point);
}

And on the view, make some changes:

void CDTBView::OnDragToolBar(UINT nCmdID)
{
   m_dragBtn = nCmdID;
}

void CDTBView::OnStartDrag() 
{
   BeginDrag();
}

You should add more codes to make your toolbar work smarter.

void CDragToolBar::OnLButtonDown(UINT nFlags, CPoint point) 
{
   int nCount = GetToolBarCtrl().GetButtonCount();
   for (int i=0; i<nCount; i++) {
      CRect rect;
      GetItemRect(i, rect);
      if (rect.PtInRect(point)) {
         PostMessage(WM_COMMAND, GetItemID(i));
         m_dragMode = TRUE;
         m_point = point;
         return;
      }
   }
      
   CToolBar::OnLButtonDown(nFlags, point);
}

void CDragToolBar::OnMouseMove(UINT nFlags, CPoint point) 
{
   if (m_dragMode && 
             abs(m_point.x-point.x)+abs(m_point.y-point.y)>=3) {
      PostMessage(WM_COMMAND, ID_START_DRAG);
      m_dragMode = FALSE;
   }
   CToolBar::OnMouseMove(nFlags, point);
}

And on the view, make some changes:

void CDTBView::OnDragToolBar(UINT nCmdID)
{
   m_dragBtn = nCmdID;
}

void CDTBView::OnStartDrag() 
{
   BeginDrag();
}

You should add more codes to make your toolbar work smarter.

Downloads

Download demo project - 30 Kb
Download source - 2 Kb