Drag and drop

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.




“An operation in which the end user uses the mouse or other pointing device
to move data to another location in the same window or another window”.
This is right off the glossary in the VC++ help files. Note that this statement
says move data but drag and drop is commonly understood to encompass
copy data as well. In this section we will cover drag and drop to move
data within the same window – the tree view control..


Step 0: Make sure that the control style supports drag and drop


If the control has the TVS_DISABLEDRAGDROP style then the TVN_BEGINDRAG
notification is not sent. So make sure that the control does not have the
TVS_DISABLEDRAGDROP style.


Step 1: Declare member variables


Add member variables to the CTreeCtrl derived class to track whether drag
and drop is in progress and the handle of the drag item and the drop location.
The m_pDragImage variable holds the image list used during the drag and
drop operation.

 

protected:
	CImageList*	m_pDragImage;
	BOOL		m_bLDragging;
	HTREEITEM	m_hitemDrag,m_hitemDrop;


Step 2: Add handler for TVN_BEGINDRAG


You can use the class wizard to add a handler for the TVN_BEGINDRAG notification.
The handler function has been named OnBeginDrag() in the listing below.
The code first sets up the member variables. It then creates an image that
will drag across the screen as the mouse moves. CreateDragImage() is a
member of the CTreeView class and it creates an image consisting of the
item image and the label text.

BeginDrag() function is called with an argument of zero and a point.
The zero indicates the first image, which is the only image in the image
list. The value we use for the second argument causes the image to be drawn
to the right lower side of the cursor. You may want to experiment with
this value if the image position does not suit your taste.

We call the DragEnter() to display the drag image. The first argument
is NULL so that the drag image will be visible even if the mouse is dragged
outside the treeview control.

 

void CTreeCtrlX::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	*pResult = 0;

	m_hitemDrag = pNMTreeView->itemNew.hItem;
	m_hitemDrop = NULL;

	m_pDragImage = CreateDragImage(m_hitemDrag);  // get the image list for dragging
	// CreateDragImage() returns NULL if no image list
	// associated with the tree view control
	if( !m_pDragImage )
		return;

	m_bLDragging = TRUE;
	m_pDragImage->BeginDrag(0, CPoint(-15,-15));
	POINT pt = pNMTreeView->ptDrag;
	ClientToScreen( &pt );
	m_pDragImage->DragEnter(NULL, pt);
	SetCapture();
}


Step 3: Add handler for WM_MOUSEMOVE to update drag image


In this handler we basically update the position of the drag image and
update the drop position. The DragMove() function moves the drag image.
We then update the drop target if the mouse is over a tree view item. Note
the calls to DragShowNolock(). The first call hides the drag image and
allows the tree view control to be updated. The second calls displays the
drag image again. We can also use a combination of DragLeave() and DragEnter()
but I found that using these functions causes some redraw problems.

 

void CTreeCtrlX::OnMouseMove(UINT nFlags, CPoint point) 
{
	HTREEITEM	hitem;
	UINT		flags;

	if (m_bLDragging)
	{
		POINT pt = point;
		ClientToScreen( &pt );
		CImageList::DragMove(pt);
		if ((hitem = HitTest(point, &flags)) != NULL)
		{
			CImageList::DragShowNolock(FALSE);
			SelectDropTarget(hitem);
			m_hitemDrop = hitem;
			CImageList::DragShowNolock(TRUE);
		}
	}

	CTreeCtrl::OnMouseMove(nFlags, point);
}


Step 4: Finally add handler for WM_LBUTTONUP


It is in the WM_LBUTTONUP handler that we complete the drag and drop process.
We first determine whether a drag – drop operation was in progress. This
is a good place to delete the image list object we created by calling CreateDragImage()
in OnBeginDrag() function. Before moving the branch we make sure that the
move is valid. The call to Expand() is normally not necessary, however,
if we implement dynamic loading, wherein the items are loaded only after
the parent is expanded then this call is important.

 

void CTreeCtrlX::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CTreeCtrl::OnLButtonUp(nFlags, point);

	if (m_bLDragging)
	{
		m_bLDragging = FALSE;
		CImageList::DragLeave(this);
		CImageList::EndDrag();
		ReleaseCapture();

		delete m_pDragImage;

		// Remove drop target highlighting
		SelectDropTarget(NULL);

		if( m_hitemDrag == m_hitemDrop )
			return;

		// If Drag item is an ancestor of Drop item then return
		HTREEITEM htiParent = m_hitemDrop;
		while( (htiParent = GetParentItem( htiParent )) != NULL )
		{
			if( htiParent == m_hitemDrag ) return;
		}

		Expand( m_hitemDrop, TVE_EXPAND ) ;

		HTREEITEM htiNew = CopyBranch( m_hitemDrag, m_hitemDrop, TVI_LAST );
		DeleteItem(m_hitemDrag);
		SelectItem( htiNew );
	}

}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read