Enhanced Drag '& Drop

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

I modified the code for using drag & drop on tree controls. The idea was to add three features found on the ClassView tree control of the VC++ ide. With the following code you can disable the drag & drop interface for certain items on the tree (the root item in ClassView, for example), and you can decide whether an item is a valid drop target or not, and in case not, provide an alternative item as a drop target (behavior observed in ClassView when you drag classes between folders; only folders are drop targets). Also, I update the cursor shape to reflect the status of the drag & drop operation.

The member variables needed are:

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

Member variables m_dropCursor and m_noDropCursor must be initialized to the desired cursors.

Here is my version of OnBeginDrag:
void CTreeCtrlX::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	// TODO: Add your control notification handler code here
	*pResult = 0;

	m_hitemDrag = pNMTreeView->itemNew.hItem;
	m_hitemDrop = NULL;
	SelectItem( m_hitemDrag );
	if (!IsDropSource(m_hitemDrag))
		return;

	// get the image list for dragging 
	m_pDragImage = CreateDragImage(m_hitemDrag);  
	// 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();
}

This member function calls IsDropSource() virtual function to decide if the item can be a source of a drag & drop operation. If IsDropSource() returns FALSE, the drag is cancelled. Here is the default IsDropSource() member function:

/* virtual */ BOOL CTreeCtrlX::IsDropSource(HTREEITEM hItem)
{
	return TRUE;  // all items are valid sources
}

I also modified the OnMouseMove() handler:

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);
			m_hitemDrop = GetDropTarget(hitem);
			SelectDropTarget(m_hitemDrop);
			CImageList::DragShowNolock(TRUE);
		}
		else
			m_hitemDrop = NULL;
		if (m_hitemDrop)
			SetCursor(m_dropCursor);
		else
			SetCursor(m_noDropCursor);
	}

	CTreeCtrl::OnMouseMove(nFlags, point);
}

This version, calls GetDropTarget() virtual function to decide if the item behind the cursor is a valid drop target. This function should return NULL if the item is not a valid target for a drop. If the item can accept a drop, it should return its HTREEITEM handle. As an alternative, a different HTREEITEM can be returned to specify a different drop target. This can be useful, for example, if you don't accept drops on tree leafs, but want to direct the drop to the parent. Also I change the cursor to reflect the status of the operation (OnSetCursor is not called while mouse is captured, so the cursor can be safely changed here).

Here is the default implemmentation of the GetDropTarget() virtual function:

/* virtual */ HTREEITEM CTreeCtrlX::GetDropTarget(HTREEITEM hItem)
{
	// inhibit drop on the drop source or its parent
	if (hItem == m_hitemDrag || hItem == GetParentItem(m_hitemDrag))
		return NULL;
	return hItem;
}

Finally, here is the OnLButtonUp() handler:

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 || m_hitemDrop == NULL)
			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 );
	}
}



Comments

  • There are no comments yet. Be the first to comment!

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

Top White Papers and Webcasts

  • Microsoft Azure® is a leading choice for businesses looking to take advantage of the cloud. Azure is particularly appealing to businesses that have already invested in Microsoft on-premises and are now considering running these applications and other workloads in the cloud. To understand how to make this move to Azure, many businesses are turning to managed service providers (MSPs) with specific Azure expertise. Read this white paper to learn the eight key areas to focus on when considering an MSP for an …

  • The software-defined data center (SDDC) and new trends in cloud and virtualization bring increased agility, automation, and intelligent services and management to all areas of the data center. Businesses can now more easily manage the entire lifecycle of their applications and services via the SDDC. This Aberdeen analyst report examines how a strong foundation in both the cloud and internal data centers is empowering organizations to fully leverage their IT infrastructure and is also preparing them to be able …

Most Popular Programming Stories

More for Developers

RSS Feeds

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