Enhanced Drag '& Drop
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