首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

MFC 分割視窗拖放實作:樹狀檢視與列表檢視跨區域拖曳

     編輯:VC 教學

架構設計

應用程式層級 (CMyApp) 保存拖放共享狀態,左右兩側視圖 (CMyTreeView, CMyListView) 各自處理自身拖放訊息,透過 App 物件交換拖放資訊。

共享狀態變數 (CMyApp)


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。

關鍵設計重點

  1. 不呼叫 SetCapture/ReleaseCapture,讓滑鼠自然跨越視窗邊界。
  2. 共享狀態置於 App 層,雙向視圖皆可存取完整拖放上下文。
  3. CImageList 拖曳 API (BeginDrag/DragMove/DragEnter/DragLeave/EndDrag) 提供系統級平滑拖曳影像。
  4. DragShowNolock 暫時隱藏拖曳影像以便目標視窗繪製 Drop Highlight。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved