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