Drag and drop between views in a splitter window

Environment: ******–>

Overwiew:

This code implements a drag & drop operation within an explorer style project
(splitter window with left hand treeView right hand listView).

How it works:

Both views have their own implmentation to handle drag & drop within the view.
By NOT setting “SetCapture/ReleaseCapture”, the drag item can be moved outside
the originator view to other targets. If the drag item (mouse cursor) leaves the
originators window the ON_WM_MOUSEMOVE()-handler of the target view gets control.
Because of that we have to ensure the views can access the same member variable –
we use the app-class.

YOUR APP CLASS:

Define some member variables at your app class:

public:
    //Drag & Drop Member:
    CImageList      *cpDragImage;
    BOOL            cDragging;
    CWnd            *cpDragWnd;
    CWnd            *cpDropWnd;
    HTREEITEM       cTreeItemDrag;
    HTREEITEM       cTreeItemDrop;
    int             cListItemDragIndex;
    int             cListItemDropIndex;
    CPoint          cDropPoint;             //list view needs global var to examine target

YOUR MAINFRAME CLASS:

To access the TreeCtrl from the ListCtrl we can use a small helper function
(if you define a “explorer style”-project the method “GetRightPane” is already
defined).

CMyListView* CMainFrame::GetRightPane()
{
	CWnd* pWnd = m_wndSplitter.GetPane(0, 1);
	CMyListView* pView = DYNAMIC_DOWNCAST(CMyListView, pWnd);
	return pView;
}


CMyTreeView* CMainFrame::GetLeftPane()
{
	CWnd* pWnd = m_wndSplitter.GetPane(0, 0);
	CMyTreeView *pTree = DYNAMIC_DOWNCAST(CMyTreeView, pWnd);
	return pTree;
}

YOUR TREE VIEW:

To access the member variables, it is useful to define a helper function to
easily access the app class:

CMyApp *CMyTreeView::GetApp()
{
    return ( (CMyApp*)AfxGetApp() );
}//GetApp

Implement handler for the three messages

	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBegindrag)

Set all elements (this view can set) on begin of drag, perhaps target is the same
view, perhaps not.

void CMyTreeView::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_TREEVIEW    *pTreeView = (NM_TREEVIEW*)pNMHDR;

	*pResult = 0;

    //Get pointer to associated tree control:
    CTreeCtrl &cTree = GetTreeCtrl();

    GetApp()->cTreeItemDrag = pTreeView->itemNew.hItem;
    GetApp()->cTreeItemDrop = NULL;

    //Create a drag image (from the source item):
    GetApp()->cpDragImage = cTree.CreateDragImage(GetApp()->cTreeItemDrag);
    GetApp()->cpDragImage->BeginDrag(0, CPoint(-15,-15));
    POINT pt = pTreeView->ptDrag;
    ClientToScreen( &pt );
    GetApp()->cpDragImage->DragEnter(NULL, pt);

    //Initialize:
    GetApp()->cDragging = TRUE;
    GetApp()->cpDragWnd = &cTree;
    GetApp()->cpDropWnd = NULL;
}//OnBegindrag



void CMyTreeView::OnMouseMove(UINT nFlags, CPoint point)
/*----------------------------------------------------------------------------s
 Beschreibung:
 Parameter:
 Rueckgabe:
 Autor/Copyright:
    (c) Christopher Frank, FRG
----------------------------------------------------------------------------e*/
{
 HTREEITEM       hitem;
 UINT            flags;

    //Get pointer to associated tree control:
    CTreeCtrl &cTree = GetTreeCtrl();

    //If dragging is active:
    if ( GetApp()->cDragging )
    {
        POINT   pt  = point;
        ClientToScreen( &pt );

        //Move the image:
        GetApp()->cpDragImage->DragMove(pt);

        //Set selection on the current (possible) target:
        if ( (hitem = cTree.HitTest(point, &flags)) != NULL )
        {
            GetApp()->cpDragImage->DragShowNolock(FALSE);
            cTree.SelectDropTarget(hitem);
            GetApp()->cTreeItemDrop = hitem;
            GetApp()->cpDragImage->DragShowNolock(TRUE);
        }
    }

    //Standardmethode:
    CTreeView::OnMouseMove(nFlags, point);
}//OnMouseMove



void CMyTreeView::OnLButtonUp(UINT nFlags, CPoint point)
/*----------------------------------------------------------------------------s
 Beschreibung:
 Parameter:
 Rueckgabe:
 Autor/Copyright:
    (c) Christopher Frank, FRG
----------------------------------------------------------------------------e*/
{
    //Get pointer to associated tree control:
    CTreeCtrl &cTree = GetTreeCtrl();

    //Standardmethode:	
	CTreeView::OnLButtonUp(nFlags, point);

    //If dragging is active:
    if ( GetApp()->cDragging )
    {
        //Dragging is no longer active:
        GetApp()->cDragging = FALSE;
        GetApp()->cpDragImage->DragLeave(this);
        GetApp()->cpDragImage->EndDrag();
        delete GetApp()->cpDragImage;

        //Remove drop target highlighting
        cTree.SelectDropTarget(NULL);

        //Examine the window dragimage is dropped:
        GetApp()->cDropPoint = point;
        ClientToScreen(&GetApp()->cDropPoint);
        GetApp()->cpDropWnd = WindowFromPoint(GetApp()->cDropPoint);

        //Select the type of drag source:
        if ( GetApp()->cpDragWnd->IsKindOf(RUNTIME_CLASS(CListView)) )
        {
            AfxMessageBox("source is list view", MB_OK);
        }
        else
            if ( GetApp()->cpDragWnd->IsKindOf(RUNTIME_CLASS(CTreeView)) )
            {
                AfxMessageBox("source is treeview", MB_OK);
            }
            else
                AfxMessageBox("source is something else", MB_OK);
    }
}//OnLButtonUp

YOUR LIST VIEW:

To access the member variables, it is useful to define a helper function to easily
access the app class:

CMyApp *CMyListView::GetApp()
{
    return ( (CMyApp*)AfxGetApp() );
}//GetApp


CMainFrame *CKAIView::GetFrame()
{
    return ( ((CMainFrame*)GetParentFrame()) );
}


CMyTreeView *CMyListView::GetTree()
{
    return ( GetFrame()->GetLeftPane() );
}

Implement handler for the three messages

	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBegindrag)

Set all elements (this view can set) on begin of drag, perhaps target is the same
view, perhaps not.

void CMyListView::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

	*pResult = 0;

    //Zeiger auf den Tree-Control ermitteln:
    CListCtrl &cList = GetListCtrl();

    //set source of drag:
    GetApp()->cListItemDragIndex = ((NM_LISTVIEW *)pNMHDR)->iItem;

    //Create a drag image (from the source item):
    POINT pt;
    pt.x = pt.y = 8;
    GetApp()->cpDragImage = cList.CreateDragImage(GetApp()->cListItemDragIndex, &pt);
    GetApp()->cpDragImage->BeginDrag(0, CPoint (8, 8));
    pt = ((NM_LISTVIEW *)pNMHDR)->ptAction;
    ClientToScreen( &pt );
    GetApp()->cpDragImage->DragEnter(NULL, pt);

    //Initialize:
    GetApp()->cDragging = TRUE;
    GetApp()->cListItemDropIndex = -1;
    GetApp()->cpDragWnd = &cList;
    GetApp()->cpDropWnd = NULL;
}//OnBegindrag



void CMyListView::OnMouseMove(UINT nFlags, CPoint point)
{
    //If dragging is active:
    if( GetApp()->cDragging )
    {
        POINT   pt  = point;
        ClientToScreen(&pt);

        //Move the image:
        GetApp()->cpDragImage->DragMove(pt);

        //Get drop window:
        GetApp()->cpDragImage->DragShowNolock(FALSE);
        GetApp()->cpDropWnd = WindowFromPoint(pt);
        GetApp()->cpDropWnd->ScreenToClient(&pt);
        GetApp()->cpDragImage->DragShowNolock(TRUE);

        //Get tree:
        CTreeCtrl &cTree = GetTree()->GetTreeCtrl();

        //Remove drop target highlighting on the tree (immediate if the cursor 
        //leaves the treeView window)
        cTree.SelectDropTarget(NULL);
    }

    //Standardmethode:
	CListView::OnMouseMove(nFlags, point);
}//OnMouseMove


void CMyListView::OnLButtonUp(UINT nFlags, CPoint point)
{
    //If dragging is active:
    if( GetApp()->cDragging )
    {
        //Initialize end dragging:
        GetApp()->cDragging = FALSE;
        GetApp()->cpDragImage->DragLeave(GetDesktopWindow());
        GetApp()->cpDragImage->EndDrag();

        //GET THE WINDOW UNDER THE DROP POINT
        GetApp()->cDropPoint = point;
        ClientToScreen(&GetApp()->cDropPoint);
        GetApp()->cpDropWnd = WindowFromPoint(GetApp()->cDropPoint);

        //Cancel if source and target are same:

        //Select the type of drag source:
        if ( GetApp()->cpDragWnd->IsKindOf(RUNTIME_CLASS(CListView)) )
        {
            AfxMessageBox("source is list view", MB_OK);
        }
        else
            if ( GetApp()->cpDragWnd->IsKindOf(RUNTIME_CLASS(CTreeView)) )
            {
                AfxMessageBox("source is treeview", MB_OK);
            }
            else
                AfxMessageBox("source is something else", MB_OK);
    }

	//Standardmethode:
	CListView::OnLButtonUp(nFlags, point);
}//OnLButtonUp

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read