Drag and drop


"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 );
	}

}


Comments

  • no source code ?

    Posted by suchuhui80 on 02/14/2006 11:00pm

    where can I download your whole project ? error C2065: 'SelectDropTarget' : undeclared identifier error C2065: 'GetParentItem' : undeclared identifier

    Reply
  • set mouse position

    Posted by Legacy on 01/20/2004 12:00am

    Originally posted by: astian

    How to set the position of the mouse pointer on the screen in VC++

    Reply
  • a little improvement.

    Posted by Legacy on 06/30/2003 12:00am

    Originally posted by: johnson yang

    You can add the codes below into the OnMouseMove event to deal th scroller.(you needn't to use the Timer way described in the other essay.)
    
    

    ------
    HTREEITEM prev=GetPrevVisibleItem(hitem);
    HTREEITEM next=GetNextVisibleItem(hitem);

    EnsureVisible(prev);
    EnsureVisible(next);
    ------

    the updated code should be like this:
    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;

    HTREEITEM prev=GetPrevVisibleItem(hitem);
    HTREEITEM next=GetNextVisibleItem(hitem);

    EnsureVisible(prev);
    EnsureVisible(next);

    CImageList::DragShowNolock(TRUE);
    }
    }

    CTreeCtrl::OnMouseMove(nFlags, point);
    }

    Reply
  • Drag Image problems

    Posted by Legacy on 05/12/2003 12:00am

    Originally posted by: Patrick Fitzpatrick

    When I use this code, the image that is created for the drag contains the text of the tree item too. If I drag a different tree item, the text does not change and always gives me the text of the first dragged item. Any Idea how to solve this?

    • CreateDragImage(m_hitemDrag) problems

      Posted by 81quang on 12/27/2005 03:55am

      This method always returns NULL value I don't know why ? Please help me

      Reply
    Reply
  • I can't find MouseMove and MouseUp events

    Posted by Legacy on 02/19/2003 12:00am

    Originally posted by: Adriana

    My program is a Dialog based app and I have included a Tree control, so I don't have a class for CTreeCtrl to create this events from. I was able to create a MouseMove and MouseUp functions but they are from my main dialog (CDialog) and it will work for a short time, then when draging the image will only show in the rest of my dialog except over the tree control.

    Does anybody have an idea?. I will really appreciate it. Thanx =D

    Reply
  • Some usefull tips

    Posted by Legacy on 10/25/2002 12:00am

    Originally posted by: Anibal Itriago

    Here you have something that can be usefull in this code.
    
    1. This part copies the old branch info complete.
    //in HEADER.h
    protected:
    HTREEITEM CopyBranch(HTREEITEM oldItem, HTREEITEM newParent, HTREEITEM iPosition);
    void SetNewParent(HTREEITEM newPosition, HTREEITEM oldPosition);
    ...

    //in CODE.cpp
    HTREEITEM CCustomTreeControl::CopyBranch(HTREEITEM oldItem, HTREEITEM newParent, HTREEITEM iPosition)
    {
    HTREEITEM newItem=InsertItem( GetItemText(oldItem), newParent,iPosition);
    SetNewParent(newItem,oldItem);
    return newItem;
    }

    void CCustomTreeControl::SetNewParent(HTREEITEM newPosition, HTREEITEM oldPosition)
    {
    HTREEITEM oldChild=GetChildItem(oldPosition);
    HTREEITEM newChild;
    while(oldChild){
    newChild=InsertItem( GetItemText(oldChild), newPosition,TVI_LAST );
    SetNewParent(newChild,oldChild);
    oldChild=GetNextItem(oldChild, TVGN_NEXT);
    }
    }
    ****************************************
    2. If you are dragging, and the drag&drop must scroll the client view... that doesn't work fine in this sample... So what I did (still with a little refresh bug) is:
    void CCustomTreeControl::OnMouseMove(UINT nFlags, CPoint point)
    {
    // TODO: Add your message handler code here and/or call default
    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);
    }
    RECT rect;
    GetClientRect( &rect );
    HTREEITEM hitem =GetItemAtPos(point);
    SelectDropTarget(hitem);
    CImageList::DragShowNolock(FALSE);
    Expand(hitem,TVE_EXPAND);//If you want to expand the entire child branch
    CImageList::DragShowNolock(TRUE);
    if( point.y < rect.top + 10 )
    {
    // We need to scroll up
    // Scroll slowly if cursor near the treeview control
    int slowscroll = 6 - (rect.top + 10 - point.y) / 20;
    CImageList::DragShowNolock(FALSE);
    SendMessage( WM_VSCROLL, SB_LINEUP);
    CImageList::DragShowNolock(TRUE);
    }
    else if( point.y > rect.bottom - 10 )
    {
    // We need to scroll down
    // Scroll slowly if cursor near the treeview control
    CImageList::DragShowNolock(FALSE);
    SendMessage( WM_VSCROLL, SB_LINEDOWN);
    CImageList::DragShowNolock(TRUE);
    }
    }
    CTreeCtrl::OnMouseMove(nFlags, point);
    }
    .........
    :) Enjoy!
    Anibal.

    Reply
  • sound work!!

    Posted by Legacy on 09/28/2002 12:00am

    Originally posted by: Ben

    working great!

    Reply
  • minor update (delete m_pDragImage / etc patch included...)

    Posted by Legacy on 08/07/2002 12:00am

    Originally posted by: Seok Heon Seop[KOREA]

    if m_pDragImage is NULL, "delete m_pDragImage" command is fatal error.

    repair!!

    included patch:
    Avoiding flicker during drag'n drop
    Minor change to the constructor


    [constructor]
    CTreeCtrlEx::CTreeCtrlEx()
    {
    .......
    .......
    m_pDragImage = NULL;
    m_bLDragging = FALSE;
    }

    void CTreeCtrlEx::OnMouseMove(UINT nFlags, CPoint point)
    {
    .......
    .......
    CImageList::DragMove(pt);
    hitem = HitTest(point, &flags);
    if (hitem != m_hitemDrop)
    {
    CImageList::DragShowNolock(FALSE);
    SelectDropTarget(hitem);
    m_hitemDrop = hitem;
    CImageList::DragShowNolock(TRUE);
    }
    .......
    .......
    }


    void CTreeCtrlEx::OnLButtonUp(UINT nFlags, CPoint point)
    {
    .......
    .......
    ReleaseCapture();

    if(m_pDragImage != NULL)
    {
    delete m_pDragImage;
    m_pDragImage = NULL;
    }
    .......
    .......
    }


    ^____________________^;;;;;;;

    Reply
  • The OnBeginDrag () function is never been called?

    Posted by Legacy on 06/03/2002 12:00am

    Originally posted by: Mihir

    HI all,

    the onBeginDrag function never got called.. I think it can be because my tree view has a function called on LButtonDown. so is this the problem?>??????????

    I dont know why it never reaches there?

    thanks,
    mihir.

    Reply
  • Works great!

    Posted by Legacy on 04/11/2002 12:00am

    Originally posted by: Ron Wolfe

    I really like the way this code works. I looked for over a week for 'how' to use dragdrop, but stumbled upon this and many thanks to the author. The only trouble I had was, being a newbie, I had a bit of trouble getting it to work, until I realized all I had to do was build a couple bitmap images for it to load. Thanks again.

    Reply
  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Businesses are moving more and more of their customer transactions to the web. Security is understandably a top concern as online transactions increase, so it is important to make sure your electronic signature provider meets the highest security standards. That means more than simply passing a security audit or obtaining a certification. This white paper provides recommendations for taking a broader view of e-signature security, and answers key questions that help identify the security requirements against …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds