首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

MFC CTreeCtrl節點遷移實現:將父子節點整體移動至目標節點下

     編輯:程式設計疑難解答

業務場景

模擬「回收站」功能:用戶選中一個樹節點(含其所有子孫節點),點擊「刪除」按鈕,將整個子樹移動到「回收站」節點下,保持層級結構不變,支持後續還原/徹底刪除。

核心算法:遞歸深度優先複製 + 刪除源節點

MFC `CTreeCtrl` 無內置「移動子樹」方法,需手動實現:
1. 在目標父節點下創建新節點(複製文本、圖標、數據)。
2. 遞歸遍歷源節點所有子節點,重複步驟 1。
3. 遞歸完成後,刪除源節點(會自動刪除其所有子孫)。

完整代碼實現

1. 遞歸複製輔助函數

// 將 hSrcItem 及其所有子孫遞歸複製到 hDstParent 下
HTREEITEM CopySubTree(CTreeCtrl& tree, HTREEITEM hSrcItem, HTREEITEM hDstParent) {
    if (!hSrcItem) return NULL;

    // 1. 複製當前節點屬性
    CString strText = tree.GetItemText(hSrcItem);
    int nImage = 0, nSelectedImage = 0;
    tree.GetItemImage(hSrcItem, nImage, nSelectedImage);
    DWORD_PTR dwData = tree.GetItemData(hSrcItem);

    // 2. 在目標父節點下插入新節點
    HTREEITEM hNewItem = tree.InsertItem(strText, nImage, nSelectedImage, hDstParent, TVI_LAST);
    tree.SetItemData(hNewItem, dwData); // 保持用戶數據

    // 3. 遞歸處理子節點
    HTREEITEM hChild = tree.GetChildItem(hSrcItem);
    while (hChild) {
        CopySubTree(tree, hChild, hNewItem);
        hChild = tree.GetNextSiblingItem(hChild);
    }
    return hNewItem;
}

2. 移動到回收站命令處理

void CMyTreeView::OnMoveToRecycleBin() {
    HTREEITEM hSel = m_tree.GetSelectedItem();
    if (!hSel) return;

    HTREEITEM hRecycle = FindRecycleBinItem(); // 查找或創建「回收站」根節點
    if (!hRecycle) return;

    // 防止將回收站自己移動進自己
    if (hSel == hRecycle || IsChildOf(hSel, hRecycle)) {
        AfxMessageBox(_T("不能將回收站移動到自身內部"));
        return;
    }

    // 1. 複製子樹到回收站
    HTREEITEM hNewRoot = CopySubTree(m_tree, hSel, hRecycle);

    // 2. 刪除原節點(含所有子孫)
    m_tree.DeleteItem(hSel);

    // 3. 展開回收站顯示
    m_tree.Expand(hRecycle, TVE_EXPAND);
    m_tree.SelectItem(hNewRoot);
}

3. 輔助:查找/創建回收站節點

HTREEITEM CMyTreeView::FindRecycleBinItem() {
    HTREEITEM hRoot = m_tree.GetRootItem();
    HTREEITEM hItem = m_tree.GetChildItem(hRoot);
    while (hItem) {
        if (m_tree.GetItemText(hItem) == _T("回收站")) return hItem;
        hItem = m_tree.GetNextSiblingItem(hItem);
    }
    // 不存在則創建
    return m_tree.InsertItem(_T("回收站"), 2, 2, hRoot, TVI_LAST); // 圖標索引 2
}

4. 輔助:判斷是否為子孫節點(防循環)

BOOL CMyTreeView::IsChildOf(HTREEITEM hItem, HTREEITEM hAncestor) {
    while (hItem) {
        hItem = m_tree.GetParentItem(hItem);
        if (hItem == hAncestor) return TRUE;
    }
    return FALSE;
}

關鍵細節與優化

要點說明
項目數據用 `SetItemData` 保存指針/ID,複製時同步,業務邏輯不中斷
圖標狀態複製 `nImage`/`nSelectedImage`,保持展開/折疊視覺一致
性能優化大量節點時用 `SetRedraw(FALSE)` 禁用重繪,操作完 `SetRedraw(TRUE); Invalidate();`
撤銷支持記錄「源父節點、源位置、目標節點」,實現 Undo 需反向移動
拖拽移動同理,`TVN_BEGINDRAG`/`TVN_ENDDRAG` 中計算目標,調用 `CopySubTree` + `DeleteItem`

總結

MFC `CTreeCtrl` 移動子樹的本質是 「遞歸複製 + 刪除源」。封裝通用 `CopySubTree` 函數,配合項目數據、圖標、防循環檢查,即可構建健壯的「回收站」、「拖拽排序」、「複製分支」等高級樹操作功能。

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