應用程式層級 (CMyApp) 保存拖放共享狀態,左右兩側視圖 (CMyTreeView, CMyListView) 各自處理自身拖放訊息,透過 App 物件交換拖放資訊。
public:
CImageList* m_pDragImage; // 拖曳影像清單
BOOL m_bDragging; // 是否正在拖曳
CWnd* m_pDragWnd; // 來源視窗
CWnd* m_pDropWnd; // 目標視窗
HTREEITEM m_hTreeItemDrag; // 來源樹節點
HTREEITEM m_hTreeItemDrop; // 目標樹節點
int m_nListItemDrag; // 來源列表項索引
int m_nListItemDrop; // 目標列表項索引
CPoint m_ptDrop; // 放下座標
// 主框架取得左右視圖指標
CMyListView* CMainFrame::GetRightPane()
{
CWnd* pWnd = m_wndSplitter.GetPane(0, 1);
return DYNAMIC_DOWNCAST(CMyListView, pWnd);
}
CMyTreeView* CMainFrame::GetLeftPane()
{
CWnd* pWnd = m_wndSplitter.GetPane(0, 0);
return DYNAMIC_DOWNCAST(CMyTreeView, pWnd);
}
// 視圖取得 App 物件
CMyApp* CMyTreeView::GetApp() { return (CMyApp*)AfxGetApp(); }
CMyApp* CMyListView::GetApp() { return (CMyApp*)AfxGetApp(); }
ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBeginDrag)
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
void CMyTreeView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pTV = (NM_TREEVIEW*)pNMHDR;
*pResult = 0;
CTreeCtrl& tree = GetTreeCtrl();
GetApp()->m_hTreeItemDrag = pTV->itemNew.hItem;
GetApp()->m_hTreeItemDrop = NULL;
GetApp()->m_pDragImage = tree.CreateDragImage(GetApp()->m_hTreeItemDrag);
GetApp()->m_pDragImage->BeginDrag(0, CPoint(-15,-15));
CPoint pt = pTV->ptDrag;
ClientToScreen(&pt);
GetApp()->m_pDragImage->DragEnter(NULL, pt);
GetApp()->m_bDragging = TRUE;
GetApp()->m_pDragWnd = &tree;
GetApp()->m_pDropWnd = NULL;
}
void CMyTreeView::OnMouseMove(UINT nFlags, CPoint point)
{
if (GetApp()->m_bDragging)
{
CPoint pt = point; ClientToScreen(&pt);
GetApp()->m_pDragImage->DragMove(pt);
CTreeCtrl& tree = GetTreeCtrl();
UINT flags; HTREEITEM hItem = tree.HitTest(point, &flags);
if (hItem)
{
GetApp()->m_pDragImage->DragShowNolock(FALSE);
tree.SelectDropTarget(hItem);
GetApp()->m_hTreeItemDrop = hItem;
GetApp()->m_pDragImage->DragShowNolock(TRUE);
}
}
CTreeView::OnMouseMove(nFlags, point);
}
void CMyTreeView::OnLButtonUp(UINT nFlags, CPoint point)
{
CTreeView::OnLButtonUp(nFlags, point);
if (!GetApp()->m_bDragging) return;
GetApp()->m_bDragging = FALSE;
GetApp()->m_pDragImage->DragLeave(this);
GetApp()->m_pDragImage->EndDrag();
delete GetApp()->m_pDragImage;
CTreeCtrl& tree = GetTreeCtrl();
tree.SelectDropTarget(NULL);
GetApp()->m_ptDrop = point;
ClientToScreen(&GetApp()->m_ptDrop);
GetApp()->m_pDropWnd = WindowFromPoint(GetApp()->m_ptDrop);
// 判斷來源類型並執行對應邏輯
if (GetApp()->m_pDragWnd->IsKindOf(RUNTIME_CLASS(CListView)))
AfxMessageBox(_T("來源: 列表檢視"));
else if (GetApp()->m_pDragWnd->IsKindOf(RUNTIME_CLASS(CTreeView)))
AfxMessageBox(_T("來源: 樹狀檢視"));
}
CMyListView 同樣實作 LVN_BEGINDRAG、OnMouseMove、OnLButtonUp,差別在於:
m_nListItemDrag 取自 NM_LISTVIEW->iItem。CListCtrl::CreateDragImage。OnMouseMove 中透過 WindowFromPoint 直接取得目標視窗,不需 HitTest。GetTree() 取得左側樹狀檢視指標以清除其 SelectDropTarget。BeginDrag/DragMove/DragEnter/DragLeave/EndDrag) 提供系統級平滑拖曳影像。