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

C# TreeView 完整操作指南(一):資料綁定、拖曳排序與資料庫同步

     編輯:關於C語言

專案背景

企業級權限管理系統的功能維護模組,使用 `TreeView` 展示階層式功能選單,支援節點增刪改、拖曳重組、資料庫同步、明細編輯。資料來源為 Oracle 資料庫,透過 `DataTable` 快取實現離線操作與 UI 即時回饋。

核心類別結構

  • SM_FunctionMaintenance:主視窗,繼承 `Form`
  • FunctionTable:功能實體類別(ID、標識、名稱、描述、類別、父ID 等)
  • ClientDictionary:字典項目(值/顯示文字)
  • DataAccessObject:資料存取封裝,持有 `OracleConnection`

資料載入與樹狀建構

遞迴建樹演算法


private void InitTreeView(TreeNodeCollection nodes, string parentId) {
    DataView view = new DataView(funcDataTable);
    view.RowFilter = $"上級功能ID = {parentId}";

    foreach (DataRowView row in view) {
        TreeNode node = new TreeNode {
            Text = row["功能名稱"].ToString(),
            Tag = row["功能ID"].ToString()
        };
        nodes.Add(node);
        // 遞迴處理子節點
        InitTreeView(node.Nodes, node.Tag.ToString());
    }
}

載入入口


private void SM_FunctionMaintenance_Load(object sender, EventArgs e) {
    InitTreeView(trv_Function.Nodes, ROOT_NODE_VALUE);  // "0"
    trv_Function.SelectedNode = trv_Function.Nodes[0];
    trv_Function.Focus();
    trv_Function.CollapseAll();
}

節點選取與明細同步


private void trv_Function_AfterSelect(object sender, TreeViewEventArgs e) {
    DataView view = new DataView(funcDataTable);
    view.RowFilter = $"功能ID = {e.Node.Tag}";

    foreach (DataRowView row in view) {
        txt_Tag.Text = row["功能標識"].ToString();
        txt_Name.Text = row["功能名稱"].ToString();
        txt_Detail.Text = row["功能簡述"].ToString();
        txt_BelongTo.Text = row["所屬窗體"].ToString();
        cbo_Type.SelectedValue = row["功能類別"].ToString();
        txt_FunEntity.Text = row["功能體"].ToString();
        ckb_IsInterface.Checked = row["有無界面"].ToString() == "1";
    }
}

新增根節點/子節點

新增根節點


private void btn_AddRoot_Click(object sender, EventArgs e) {
    var dlg = new SM_AddNewFunction(0, DIC_funcType);  // 父ID=0
    dlg.ShowDialog();
    if (dlg.NewFuncItem.FuncID != -1) {
        // 1. 更新 DataTable 快取
        DataRow row = funcDataTable.NewRow();
        row["功能ID"] = dlg.NewFuncItem.FuncID;
        row["功能標識"] = dlg.NewFuncItem.FuncTag;
        // ... 其他欄位
        funcDataTable.Rows.Add(row);

        // 2. 更新 TreeView
        TreeNode node = new TreeNode(dlg.NewFuncItem.FuncName) { Tag = dlg.NewFuncItem.FuncID.ToString() };
        trv_Function.Nodes.Add(node);
        trv_Function.SelectedNode = node;
    }
    trv_Function.Focus();
}

新增子節點


private void btn_AddSon_Click(object sender, EventArgs e) {
    if (trv_Function.SelectedNode == null) { MessageBox.Show("請選擇父節點"); return; }
    long parentId = long.Parse(trv_Function.SelectedNode.Tag.ToString());
    var dlg = new SM_AddNewFunction(parentId, DIC_funcType);
    dlg.ShowDialog();
    if (dlg.NewFuncItem.FuncID != -1) {
        // 更新 DataTable
        DataRow row = funcDataTable.NewRow();
        // ... 填入欄位,含 上級功能ID = parentId
        funcDataTable.Rows.Add(row);

        // 更新 TreeView
        TreeNode node = new TreeNode(dlg.NewFuncItem.FuncName) { Tag = dlg.NewFuncItem.FuncID.ToString() };
        trv_Function.SelectedNode.Nodes.Add(node);
        trv_Function.SelectedNode = node;
    }
    trv_Function.Focus();
}

修改節點


private void btn_Modify_Click(object sender, EventArgs e) {
    if (trv_Function.SelectedNode == null) return;
    long id = long.Parse(trv_Function.SelectedNode.Tag.ToString());
    FunctionTable item = QueryFuncItem(id);
    var dlg = new SM_EditFunction(item, DIC_funcType);
    dlg.ShowDialog();
    if (dlg.IsEdit) {
        // 1. 更新 DataTable
        DataView view = new DataView(funcDataTable);
        view.RowFilter = $"功能ID = {id}";
        foreach (DataRowView row in view) {
            row["功能標識"] = dlg.NewFuncItem.FuncTag;
            row["功能名稱"] = dlg.NewFuncItem.FuncName;
            // ... 其他欄位
        }

        // 2. 更新 TreeView 顯示文字
        trv_Function.SelectedNode.Text = dlg.NewFuncItem.FuncName;

        // 3. 同步明細區
        txt_Name.Text = dlg.NewFuncItem.FuncName;
        // ... 其他控制項
    }
    trv_Function.Focus();
}

刪除節點(含遞迴子樹)


private void btn_Remove_Click(object sender, EventArgs e) {
    if (trv_Function.SelectedNode == null) return;
    if (MessageBox.Show("確定刪除?") != DialogResult.Yes) return;

    // 1. 決定刪除後焦點節點
    TreeNode nextFocus = CalculateNextFocusNode();

    // 2. 收集待刪除節點 ID(含所有後代)
    var ids = CollectNodeIds(trv_Function.SelectedNode);

    // 3. 執行資料庫刪除(交易)
    foreach (long id in ids) DeleteFuncItem(id);

    // 4. 更新 DataTable
    foreach (long id in ids) {
        for (int i = 0; i < funcDataTable.Rows.Count; i++) {
            if (funcDataTable.Rows[i]["功能ID"].ToString() == id.ToString()) {
                funcDataTable.Rows[i].Delete();
                break;
            }
        }
        funcDataTable.AcceptChanges();
    }

    // 5. 移除 TreeView 節點
    trv_Function.Nodes.Remove(trv_Function.SelectedNode);
    trv_Function.SelectedNode = nextFocus;
    trv_Function.Focus();
}

// 收集節點及所有後代 ID
private long[] CollectNodeIds(TreeNode node) {
    var list = new List { long.Parse(node.Tag.ToString()) };
    foreach (TreeNode child in node.Nodes)
        list.AddRange(CollectNodeIds(child));
    return list.ToArray();
}

拖曳重組(Drag & Drop)

啟動拖曳


private void trv_Function_ItemDrag(object sender, ItemDragEventArgs e) {
    if (e.Button == MouseButtons.Left)
        DoDragDrop(e.Item, DragDropEffects.Move);
}

放置判斷與執行


private void trv_Function_DragDrop(object sender, DragEventArgs e) {
    TreeNode moveNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
    Point pt = trv_Function.PointToClient(new Point(e.X, e.Y));
    TreeNode targetNode = trv_Function.GetNodeAt(pt);

    if (IsDragValid(targetNode, moveNode)) {
        if (targetNode != moveNode) {
            trv_Function.Nodes.Remove(moveNode);
            if (targetNode == null)
                trv_Function.Nodes.Insert(trv_Function.Nodes.Count, moveNode);  // 移至根
            else
                targetNode.Nodes.Add(moveNode);  // 移至目標子節點

            // 更新 DataTable
            string newParentId = targetNode?.Tag.ToString() ?? ROOT_NODE_VALUE;
            DataView view = new DataView(funcDataTable);
            view.RowFilter = $"功能ID = {moveNode.Tag}";
            foreach (DataRowView row in view)
                row["上級功能ID"] = newParentId;

            // 資料庫同步(交易)
            SyncParentIdToDatabase(moveNode.Tag.ToString(), newParentId);

            trv_Function.SelectedNode = moveNode;
        }
    }
}

// 防止拖曳到自己的子孫節點(會造成循環)
private bool IsDragValid(TreeNode target, TreeNode source) {
    while (target != null) {
        if (target == source) return false;
        target = target.Parent;
    }
    return true;
}

拖曳視覺回饋


private void trv_Function_DragEnter(object sender, DragEventArgs e) => e.Effect = DragDropEffects.Move;

private void trv_Function_DragOver(object sender, DragEventArgs e) {
    Point pt = trv_Function.PointToClient(new Point(e.X, e.Y));
    TreeNode node = trv_Function.GetNodeAt(pt);
    trv_Function.SelectedNode = node;
    trv_Function.Focus();
}

資料庫同步與交易處理

刪除交易(含關聯表級聯)


private void DeleteFuncItem(long funcId, string funcName) {
    using (var conn = new OracleConnection(connStr))
    using (var tran = conn.BeginTransaction()) {
        try {
            var cmd = conn.CreateCommand();
            cmd.Transaction = tran;
            cmd.CommandText = $"DELETE FROM 功能 WHERE 功能ID = {funcId}";
            cmd.ExecuteNonQuery();
            cmd.CommandText = $"DELETE FROM 角色功能 WHERE 功能ID = {funcId}";
            cmd.ExecuteNonQuery();
            cmd.CommandText = $"DELETE FROM 操作員授權 WHERE 功能ID = {funcId}";
            cmd.ExecuteNonQuery();
            cmd.CommandText = $"UPDATE 界面定制 SET 功能ID=NULL, 節點名稱='{funcName}(已收回)' WHERE 功能ID={funcId}";
            cmd.ExecuteNonQuery();
            tran.Commit();
        } catch {
            tran.Rollback();
            throw;
        }
    }
}

父節點變更同步


private void SyncParentIdToDatabase(string funcId, string newParentId) {
    using (var conn = new OracleConnection(connStr))
    using (var tran = conn.BeginTransaction()) {
        try {
            var cmd = conn.CreateCommand();
            cmd.Transaction = tran;
            cmd.CommandText = $"UPDATE 功能 SET 上級功能ID={newParentId} WHERE 功能ID={funcId}";
            cmd.ExecuteNonQuery();
            tran.Commit();
        } catch {
            tran.Rollback();
            throw;
        }
    }
}

關鍵設計模式總結

模式應用處效益
DataTable 離線快取funcDataTable減少 DB 往返、支援 RowFilter 快速查詢
遞迴建樹InitTreeView任意深度階層自動建構
Tag 屬性綁定實體 IDTreeNode.TagUI 與資料模型雙向對應
拖曳事件組合ItemDrag/DragEnter/DragOver/DragDrop完整拖曳體驗
循環檢查IsDragValid防止樹狀結構破壞
交易包裝DeleteFuncItem/SyncParentId資料一致性保證
焦點管理CalculateNextFocusNode操作流暢不中斷

現代化改進建議

  • 採用 **VirtualMode** 或 **Lazy Loading** 處理大量節點(>1000)
  • 引入 **MVVM** 模式分離 View/ViewModel/Model
  • 使用 **Entity Framework** 替代手寫 SQL
  • 支援 **多選節點** 批次操作
  • 加入 **Undo/Redo** 指令模式
  • 節點編輯改用 **Inline Editing** 而非獨立對話框

總結

此實作展示了 WinForms `TreeView` 進階應用的完整模式:從資料載入、樹狀建構、CRUD 操作、拖曳重組到資料庫交易同步。核心在於 **UI 樹狀結構與資料關係模型的強一致性維護**,透過 `Tag` 綁定 ID、`RowFilter` 快速定位、遞迴處理子樹、交易保證原子性。此模式廣泛適用於選單管理、組織架構、分類目錄等階層式資料維護場景。

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