C#实现TreeView选中父节点时在DataGridView显示所有子节点
解决TreeView选中父节点显示所有层级子节点到DataGridView的问题
看起来你已经搭好了基础的TreeView和DataGridView框架,现在就差实现选中父节点时拉取所有层级子节点的逻辑了。先给你梳理下问题所在,再给出两种可行的解决方案:
第一步:给TreeView节点绑定技工ID
你现在的TreeNode只显示了技工姓名,没有存储对应的ID_Mechanic,导致选中节点时没法定位到对应的父节点ID。先修改你的FillTree方法,把ID存到节点的Tag属性里:
public void FillTree(TreeNodeCollection tn, int? id) { NpgsqlCommand cmdtreeview; if (id == null) { cmdtreeview = new NpgsqlCommand("select * from mechanic where id_chief is null", ncon); } else { cmdtreeview = new NpgsqlCommand("select * from mechanic where id_chief = @id_mechanic", ncon); cmdtreeview.Parameters.Add(new NpgsqlParameter("id_mechanic", id)); } NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmdtreeview); DataTable dt = new DataTable(); da.Fill(dt); foreach (DataRow dr in dt.Rows) { string nodeText = $"{dr["name"]} {dr["surname"]}"; TreeNode node = new TreeNode(nodeText); // 把技工ID绑定到Tag,后续选中节点时能直接获取 node.Tag = dr["id_mechanic"]; tn.Add(node); FillTree(node.Nodes, (int?)dr["id_mechanic"]); } }
方案一:用PostgreSQL递归CTE查询(推荐,适合大数据量)
利用PostgreSQL的递归CTE语法,直接从数据库查询该父节点下所有层级的子节点,性能比内存筛选更好:
private void treeViewMechanic_AfterSelect(object sender, TreeViewEventArgs e) { // 根节点(无上级)可根据需求决定是否处理 if (e.Node.Tag == null) { dataGridViewMechanic.DataSource = null; return; } int parentId = (int)e.Node.Tag; // 递归CTE查询所有层级子节点 string recursiveQuery = @" WITH RECURSIVE sub_mechanics AS ( -- 初始查询:直接子节点 SELECT * FROM mechanic WHERE id_chief = @parentId UNION ALL -- 递归查询:子节点的子节点 SELECT m.* FROM mechanic m JOIN sub_mechanics sm ON m.id_chief = sm.id_mechanic ) SELECT * FROM sub_mechanics; "; using (NpgsqlCommand cmd = new NpgsqlCommand(recursiveQuery, ncon)) { cmd.Parameters.Add(new NpgsqlParameter("parentId", parentId)); NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd); DataTable childDt = new DataTable(); da.Fill(childDt); // 绑定到DataGridView dataGridViewMechanic.DataSource = childDt; } }
方案二:内存中递归筛选(适合小数据量)
如果你的技工数据量不大,也可以提前把所有数据加载到全局DataTable中,然后在内存里递归筛选所有子节点:
// 假设mechanicdt是全局已加载所有技工数据的DataTable private void treeViewMechanic_AfterSelect(object sender, TreeViewEventArgs e) { if (e.Node.Tag == null) { dataGridViewMechanic.DataSource = null; return; } int parentId = (int)e.Node.Tag; DataTable childTable = mechanicdt.Clone(); // 复制表结构 // 递归添加所有层级子节点 AddAllChildNodes(parentId, childTable); dataGridViewMechanic.DataSource = childTable; } private void AddAllChildNodes(int parentId, DataTable targetTable) { // 筛选当前父节点的直接子节点 DataRow[] directChildren = mechanicdt.Select($"id_chief = {parentId}"); foreach (DataRow row in directChildren) { targetTable.ImportRow(row); // 递归添加子节点的子节点 int childId = (int)row["id_mechanic"]; AddAllChildNodes(childId, targetTable); } }
为什么你之前的尝试没成功?
- 用
DataRelation的方式默认只显示直接子节点(一级),要显示所有层级需要额外处理,不如直接用递归查询更直观。 - 你尝试的LINQ写法筛选条件错误:
i.Field<String>("ID_Mechanic") == null是找没有ID的记录,完全不是筛选子节点的逻辑,自然得不到正确结果。
内容的提问来源于stack exchange,提问作者ma name jeff
相关产品推荐
相关产品推荐

