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

WPF TreeView从XML加载层级出现递归重复问题排查

TreeView层级递归重复问题排查与解决

问题背景

实现从XML动态填充层级结构的TreeView时,出现嵌套层级递归重复的问题。相关代码如下:

TreeView XAML

<TreeView x:Name="leftNestingTreeView" ItemsSource="{Binding Fragments, UpdateSourceTrigger=PropertyChanged}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding ChildFragments}" DataType="{x:Type a:FragmentModel}">
            <TextBlock  Text = "{Binding fragmentId}"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

数据源绑定代码

Fragments = NestingTree.Values.ToList();
OnPropertyChanged(nameof(Fragments));

XML文件内容

<NestingTree>
  <fragment id="root_fragment" name=" n1">
    <parent id="" name="" />
    <childs count="1">
      <child id="f1" name="frag1" />
    </childs>
  </fragment>
    <fragment id="f1" name="frag1">
    <parent id="root_fragment" name="n1" />
    <childs count="1">
      <child id="f2" name="frag2" />
    </childs>
  </fragment>
  <fragment id="f2" name="frag2">
    <parent id="f1" name="frag1" />
    <childs count="0" />
  </fragment>
</NestingTree>

FragmentModel定义

public class FragmentModel : INotifyPropertyChanged
{
    public string fragmentId { get; set; }    
    private string fragmentName { get; set; }    
    public string FragmentName
    {
        get { return fragmentName; }
        set 
        { 
           fragmentName = value; 
           OnPropertyChanged(nameof(FragmentName)); 
        }
     }
     public FragmentModel ParentFragment { get; set; }
     public List<FragmentModel> ChildFragments { get; set; }                   
     public event PropertyChangedEventHandler PropertyChanged;
     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
     {
        if (PropertyChanged != null)
        {
            PropertyChanged?.Invoke(this, new 
            PropertyChangedEventArgs(propertyName));
        }
     }
}

XML数据填充代码

XDocument xmlNestingTree = XDocument.Load("nesting_tree.xml");
if (xmlNestingTree.Element("NestingTree") is not null)
{
   int count = 0;
   foreach (XElement fragment in 
    xmlNestingTree.Element("NestingTree").Elements("fragment"))
   {
       string id = fragment.Attribute("id").Value;
       FragmentModel f = new FragmentModel
       {
           fragmentId = id,
           FragmentName = fragment.Attribute("name").Value,
       };
       nestingTree.Add(id, f);
   }
   foreach (XElement fragment in 
     xmlNestingTree.Element("NestingTree").Elements("fragment"))
   {
        FragmentModel currentFragment = nestingTree.Values.Where(f => f.fragmentId == fragment.Attribute("id").Value).FirstOrDefault();
        FragmentModel parentFragment = nestingTree.Values.Where(f => f.ChildFragments.Contains(currentFragment)).FirstOrDefault();
        currentFragment.ParentFragment = parentFragment; 
        if (Int32.TryParse(fragment.Element("childs").Attribute("count").Value, out count) && count > 0)
        {
            foreach (XElement child in fragment.Element("childs").Elements("child"))
            {
                FragmentModel currentChildFragment = nestingTree.Values.Where(f => f.fragmentId == child.Attribute("id").Value).FirstOrDefault();  
                currentFragment
                    .ChildFragments
                    .Add(currentChildFragment);
            }
        }
    }
    NestingTree = nestingTree;
}

问题原因分析

  1. 根节点数据源错误:当前Fragments绑定的是NestingTree的所有值,每个节点都被当作TreeView的根节点显示,而每个节点又会展开自身的子节点,导致整个层级重复出现。
  2. ParentFragment赋值逻辑错误:代码尝试通过查找包含当前节点的子节点集合获取父节点,但此时子节点还未设置,导致parentFragment始终为null,完全没有建立正确的父子关系。
  3. ChildFragments未初始化:FragmentModel中的ChildFragments是List类型,但未在构造函数中初始化,添加子节点时会触发空引用异常(潜在问题)。

解决方案

1. 修正FragmentModel,初始化ChildFragments

在构造函数中初始化ChildFragments,避免空引用:

public class FragmentModel : INotifyPropertyChanged
{
    public string fragmentId { get; set; }    
    private string fragmentName { get; set; }    
    public string FragmentName
    {
        get { return fragmentName; }
        set 
        { 
           fragmentName = value; 
           OnPropertyChanged(nameof(FragmentName)); 
        }
     }
     public FragmentModel ParentFragment { get; set; }
     public List<FragmentModel> ChildFragments { get; set; }                   
     public event PropertyChangedEventHandler PropertyChanged;

     // 初始化ChildFragments
     public FragmentModel()
     {
         ChildFragments = new List<FragmentModel>();
     }

     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
     {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
     }
}

2. 修正XML数据填充逻辑,正确建立父子关系

修改父节点查找逻辑,从XML的<parent>节点获取父ID,直接通过Dictionary键索引查询节点,提升性能:

XDocument xmlNestingTree = XDocument.Load("nesting_tree.xml");
if (xmlNestingTree.Element("NestingTree") is not null)
{
   int count = 0;
   foreach (XElement fragment in xmlNestingTree.Element("NestingTree").Elements("fragment"))
   {
       string id = fragment.Attribute("id").Value;
       FragmentModel f = new FragmentModel
       {
           fragmentId = id,
           FragmentName = fragment.Attribute("name").Value,
       };
       nestingTree.Add(id, f);
   }
   foreach (XElement fragment in xmlNestingTree.Element("NestingTree").Elements("fragment"))
   {
        string currentId = fragment.Attribute("id").Value;
        if (!nestingTree.TryGetValue(currentId, out FragmentModel currentFragment))
            continue;

        // 从XML的parent节点获取父ID并查找父节点
        string parentId = fragment.Element("parent").Attribute("id").Value;
        if (!string.IsNullOrEmpty(parentId) && nestingTree.TryGetValue(parentId, out FragmentModel parentFragment))
        {
            currentFragment.ParentFragment = parentFragment;
        }

        if (Int32.TryParse(fragment.Element("childs").Attribute("count").Value, out count) && count > 0)
        {
            foreach (XElement child in fragment.Element("childs").Elements("child"))
            {
                string childId = child.Attribute("id").Value;
                if (nestingTree.TryGetValue(childId, out FragmentModel currentChildFragment))
                {
                    currentFragment.ChildFragments.Add(currentChildFragment);
                    currentChildFragment.ParentFragment = currentFragment;
                }
            }
        }
    }
    NestingTree = nestingTree;
}

3. 修正数据源,只绑定真正的根节点

根节点是XML中parent id为空的节点,修改Fragments赋值逻辑:

// 过滤出父节点为空的根节点
Fragments = NestingTree.Values.Where(f => string.IsNullOrEmpty(f.ParentFragment?.fragmentId)).ToList();
OnPropertyChanged(nameof(Fragments));

验证效果

修改后,TreeView只会以root_fragment作为根节点展开,子节点f1、f2依次嵌套,不会出现重复的层级结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:08:08