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

VC++ TreeCtrl 多選拖放功能完整實作:CBitmapTree 擴充版深度解析

     編輯:關於VC++

針對 CTreeCtrl 難以掌握的拖放特性,本文基於 CBitmapTree 擴充類別,詳細解析多選拖放的核心機制:可拖動項目標記、拖放圖示自訂、拖動中視覺回饋、自動捲動、放下處理與回呼函數架構。

可拖動/可放下項目識別體系

以位元遮罩 m_draggable(32-bit)記錄前 32 個影像索引的拖放屬性:

  • SetDraggable(index) / IsDraggable(item, imgIdx):設定/查詢可拖動屬性。
  • SetDropTarget(index) / IsDropTarget(item, imgIdx):設定/查詢可放下目標屬性。
  • 虛擬函數設計,衍生類可覆寫實作複雜判斷邏輯(如依節點資料動態決定)。

拖動前置準備 SetDrag 與 OnBeginDrag

  • SetDrag(callback, dragBmpResID):註冊放下回呼函數(預設 MoveTreeItemCB),可選提供 3 狀態拖放圖示位圖(超出範圍/單項/多項)。
  • TVN_BEGINDRAG 通知經 ON_NOTIFY_REFLECT_EX 攔截,OnBeginDrag 執行:
    1. 多選模式下呼叫 DoForAllSelected(PrepareDrag) 取消不可拖動項選取。
    2. 單選模式強制選取被拖動項。
    3. 檢查 IsDraggable,不可拖動則中止。
    4. 建立/取得拖放影像清單 m_dragList,呼叫 BeginDrag 起始拖動(熱點座標 0,0)。
    5. DragEnter 進入拖動模式,隱藏游標、捕捉滑鼠、啟動自動捲動定時器。

拖動過程視覺回饋 OnMouseMove

  • 螢幕座標轉換,呼叫 GetItemUnderMouse 取得游標下項目。
  • 向上遍歷父項尋找第一個 IsDropTarget 為真者,呼叫 SelectDropTarget 高亮顯示。
  • 若無有效目標,呼叫 SelectDropTarget(NULL) 移除高亮。
  • 依游標位置切換拖放游標圖示:有效區域顯示單項/多項圖示,無效區域顯示「禁止」圖示(SetDragCursorImage)。
  • 每次更新前呼叫 DragLeave,更新後 DragEnter 重新進入。

自動捲動 OnTimer (EVENT_DRAG_SCROLL)

定時器間隔 250ms,檢查游標是否接近樹狀控制項上下邊緣,呼叫 SendMessage(WM_VSCROLL, SB_LINEUP/LINEDOWN) 實現邊緣捲動,參考 MSDN TREESCR 範例可進一步依距離調整捲動速度。

拖動結束 OnLButtonUp

  • 清理:停止定時器、釋放捕捉、EndDrag、恢復游標、清空 m_dragList。
  • 取得高亮目標項 GetDropHilightItem,若存在則呼叫 DoForAllSelected(callback, target) 執行放下邏輯。
  • 選取目標項,清除多選狀態。

回呼函數架構

預設 MoveTreeItemCB 實作單項移動(複製節點屬性、插入目標下、刪除來源)。開發者可自訂回呼函數處理:多項批次移動/複製、跨樹控制項拖放、資料庫同步更新、撤銷/重做支援等複雜業務邏輯。

關鍵代碼片段

// OnBeginDrag 核心邏輯
BOOL CBitmapTree::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    HTREEITEM h = pNMTreeView->itemNew.hItem;
    if ((m_dragFunction == NULL) || (h == NULL)) return FALSE;

    if (m_multiSelected) DoForAllSelected(PrepareDrag, NULL, 0);
    else SelectItem(h);

    int img, simg; GetItemImage(h, img, simg);
    if (!(IsDraggable(h, img))) return FALSE;

    if (m_ilDrag.m_hImageList != NULL) m_dragList = &m_ilDrag;
    else { m_dragList = CreateDragImage(h); if (!m_dragList) return FALSE; }

    if (m_multiSelected) m_dragList->BeginDrag(m_multiDrag, CPoint(0,0));
    else m_dragList->BeginDrag(m_docDrag, CPoint(0,0));

    MapWindowPoints(NULL, &pNMTreeView->ptDrag, 1);
    m_dragList->DragEnter(NULL, pNMTreeView->ptDrag);
    ShowCursor(FALSE); SetCapture();
    SetTimer(EVENT_DRAG_SCROLL, 250, NULL);
    *pResult = 0; return FALSE;
}

總結

CBitmapTree 將拖放複雜度封裝於類別內部,暴露簡潔的屬性設定與回呼介面。掌握 TVN_BEGINDRAG、CImageList 拖放影像、SelectDropTarget 高亮、定時器自動捲動、回呼解耦等核心技術點,即可輕鬆在專案中實現專業級樹狀控制項拖放體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved