針對 CTreeCtrl 難以掌握的拖放特性,本文基於 CBitmapTree 擴充類別,詳細解析多選拖放的核心機制:可拖動項目標記、拖放圖示自訂、拖動中視覺回饋、自動捲動、放下處理與回呼函數架構。
以位元遮罩 m_draggable(32-bit)記錄前 32 個影像索引的拖放屬性:
SetDraggable(index) / IsDraggable(item, imgIdx):設定/查詢可拖動屬性。SetDropTarget(index) / IsDropTarget(item, imgIdx):設定/查詢可放下目標屬性。SetDrag 與 OnBeginDragSetDrag(callback, dragBmpResID):註冊放下回呼函數(預設 MoveTreeItemCB),可選提供 3 狀態拖放圖示位圖(超出範圍/單項/多項)。TVN_BEGINDRAG 通知經 ON_NOTIFY_REFLECT_EX 攔截,OnBeginDrag 執行:
DoForAllSelected(PrepareDrag) 取消不可拖動項選取。IsDraggable,不可拖動則中止。m_dragList,呼叫 BeginDrag 起始拖動(熱點座標 0,0)。DragEnter 進入拖動模式,隱藏游標、捕捉滑鼠、啟動自動捲動定時器。OnMouseMoveGetItemUnderMouse 取得游標下項目。IsDropTarget 為真者,呼叫 SelectDropTarget 高亮顯示。SelectDropTarget(NULL) 移除高亮。SetDragCursorImage)。DragLeave,更新後 DragEnter 重新進入。OnTimer (EVENT_DRAG_SCROLL)定時器間隔 250ms,檢查游標是否接近樹狀控制項上下邊緣,呼叫 SendMessage(WM_VSCROLL, SB_LINEUP/LINEDOWN) 實現邊緣捲動,參考 MSDN TREESCR 範例可進一步依距離調整捲動速度。
OnLButtonUpEndDrag、恢復游標、清空 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 高亮、定時器自動捲動、回呼解耦等核心技術點,即可輕鬆在專案中實現專業級樹狀控制項拖放體驗。