You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForm单窗体创建多个TreeView时仅首个控件正常显示节点问题

问题描述
  • 在单个WinForm窗体中创建三个TreeView对象时,仅第一个TreeView能够显示一级节点,其余TreeView均为空
  • 原本计划添加到其他TreeView的TreeNode节点全部被归集到第一个TreeView中
  • 仅创建单个TreeView时,节点显示逻辑完全正常

问题复现代码如下:

private void temp2()
{
    #region Prepare data
    int libraryCount = 5;
    TreeNode[] libraries = new TreeNode[libraryCount];
    for (int i = 0; i < libraryCount; i++)
    {
        TreeNode library = new TreeNode("library" + i);
        for (int j = 0; j < 6; j++)
        {
            TreeNode itemType = new TreeNode("ItemType" + j);
            library.Nodes.Add(itemType);
        }
        libraries[i] = library;
    }
    #endregion

    #region Create Three TreeViews 
    TreeView tv = new TreeView();
    tv.Width = 100;
    tv.Height = 300;
    tv.Top = 30;
    tv.Left = 30;
    tv.BeginUpdate();
    tv.Nodes.AddRange(libraries);
    tv.EndUpdate();

    TreeView tv2 = new TreeView();
    tv2.Width = 100;
    tv2.Height = 300;
    tv2.Top = 30;
    tv2.Left = 130;
    tv2.BeginUpdate();
    tv2.Nodes.AddRange(libraries);
    tv2.EndUpdate();

    TreeView tv3 = new TreeView();
    tv3.Width = 100;
    tv3.Height = 300;
    tv3.Top = 30;
    tv3.Left = 230;
    tv3.BeginUpdate();
    tv3.Nodes.AddRange(libraries);
    tv3.EndUpdate();
    #endregion

    #region Create form and add treeviews
    Form form = new Form();
    form.Width = (int)(Screen.PrimaryScreen.WorkingArea.Width * 0.4);
    form.Height = (int)(Screen.PrimaryScreen.WorkingArea.Height * 0.5);
    form.Controls.AddRange(new[] { tv, tv2, tv3 });
    form.Show();
    #endregion
}
问题产生原因

WinForm中的TreeNode是引用类型对象,每个TreeNode实例同一时间只能归属于唯一的父节点集合——这个集合要么是某个TreeView的根节点集合,要么是某个上层父TreeNode的子节点集合,不存在一个TreeNode实例同时挂载到多个TreeView的情况。

代码中仅在数据准备阶段创建了一份TreeNode实例存入libraries数组,后续三次调用AddRange给三个TreeView加节点时,传入的都是同一批实例。当框架检测到待添加的TreeNode已经有归属的父集合时,会自动先将节点从原父集合中移除,再挂载到新的集合中。受不同.NET版本的实现逻辑差异影响,最终这批节点只会固定存在于某一个TreeView中,其余两个TreeView自然为空。

修复方案

不要在多个TreeView间复用同一个TreeNode实例,给每个TreeView添加节点时,基于原始节点数据生成全新的TreeNode实例即可,最通用的实现是写递归克隆方法复制节点结构:

/// <summary>
/// 递归克隆TreeNode节点,保留节点结构和文本
/// </summary>
private TreeNode CloneTreeNode(TreeNode source)
{
    TreeNode newNode = new TreeNode(source.Text);
    // 如果节点绑定了Tag、图标、选中状态等自定义属性,在此处同步复制
    foreach (TreeNode childNode in source.Nodes)
    {
        newNode.Nodes.Add(CloneTreeNode(childNode));
    }
    return newNode;
}

添加节点时,将原节点数组替换为克隆后的新节点数组即可,三个TreeView都按该方式赋值即可正常显示全部节点:

// 给第一个TreeView加节点
tv.Nodes.AddRange(libraries.Select(node => CloneTreeNode(node)).ToArray());
// 给第二个TreeView加节点
tv2.Nodes.AddRange(libraries.Select(node => CloneTreeNode(node)).ToArray());
// 给第三个TreeView加节点
tv3.Nodes.AddRange(libraries.Select(node => CloneTreeNode(node)).ToArray());

注意:使用Select方法做集合投影,需要在代码文件开头引入using System.Linq;命名空间。如果不想用Linq,也可以自行循环遍历原节点数组,逐个调用克隆方法生成新数组后再传入AddRange。

内容的提问来源于stack exchange,提问作者shibayan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 17:48:14