WPF TreeView HierarchicalDataTemplate - 绑定到具有多个子集合的对象

Posted

技术标签:

【中文标题】WPF TreeView HierarchicalDataTemplate - 绑定到具有多个子集合的对象【英文标题】:WPF TreeView HierarchicalDataTemplate - binding to object with multiple child collections 【发布时间】:2010-12-27 02:47:41 【问题描述】:

我正在尝试获取TreeView 来绑定我的集合,以便所有组都显示嵌套组并且每个组都将显示条目。

如何使用HierarchicalDataTemplate 以便TreeView 处理子组和条目集合?

组显示子组和条目:

Example:
Group1
--Entry
--Entry
Group2
--Group4
----Group1
------Entry
------Entry
----Entry
----Entry
--Entry
--Entry
Group3
--Entry
--Entry

对象:


namespace TaskManager.Domain

    public class Entry
    
        public int Key  get; set; 
        public string Name  get; set; 
    


namespace TaskManager.Domain

    public class Group
    
        public int Key  get; set; 
        public string Name  get; set; 

        public IList<Group> SubGroups  get; set; 
        public IList<Entry> Entries  get; set; 
    

测试数据:


namespace DrillDownView

    public class TestData
    

        public IList<Group> Groups = new List<Group>();

        public void Load()
        
            Group grp1 = new Group()  Key = 1, Name = "Group 1", SubGroups = new List<Group>(), Entries = new List<Entry>() ;
            Group grp2 = new Group()  Key = 2, Name = "Group 2", SubGroups = new List<Group>(), Entries = new List<Entry>() ;
            Group grp3 = new Group()  Key = 3, Name = "Group 3", SubGroups = new List<Group>(), Entries = new List<Entry>() ;
            Group grp4 = new Group()  Key = 4, Name = "Group 4", SubGroups = new List<Group>(), Entries = new List<Entry>() ;

            //grp1
            grp1.Entries.Add(new Entry()  Key=1, Name="Entry number 1" );
            grp1.Entries.Add(new Entry()  Key=2, Name="Entry number 2" );
            grp1.Entries.Add(new Entry()  Key=3,Name="Entry number 3" );

            //grp2
            grp2.Entries.Add(new Entry() Key=4, Name = "Entry number 4");
            grp2.Entries.Add(new Entry() Key=5, Name = "Entry number 5");
            grp2.Entries.Add(new Entry() Key=6, Name = "Entry number 6");

            //grp3
            grp3.Entries.Add(new Entry() Key=7, Name = "Entry number 7");
            grp3.Entries.Add(new Entry() Key=8, Name = "Entry number 8");
            grp3.Entries.Add(new Entry() Key=9, Name = "Entry number 9");

            //grp4
            grp4.Entries.Add(new Entry() Key=10, Name = "Entry number 10");
            grp4.Entries.Add(new Entry() Key=11, Name = "Entry number 11");
            grp4.Entries.Add(new Entry() Key=12, Name = "Entry number 12");

            grp4.SubGroups.Add(grp1);
            grp2.SubGroups.Add(grp4);

            Groups.Add(grp1);
            Groups.Add(grp2);
            Groups.Add(grp3);
        
    

XAML:


<Window x:Class="DrillDownView.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TaskManager.Domain;assembly=TaskManager.Domain"
        Title="Window2" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <TreeView Name="GroupView" Grid.Row="0" Grid.Column="0" ItemsSource="Binding">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="x:Type local:Group" ItemsSource="Binding SubGroups">
                    <TextBlock Text="Binding Path=Name" />
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate DataType="x:Type local:Entry" ItemsSource="Binding Entries">
                    <TextBlock Text="Binding Path=Name" />
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</Window>

XAML.CS:


public partial class Window2 : Window

    public Window2()
    
        InitializeComponent();
        LoadView();
    

    private void LoadView()
    
        TestData data = new TestData();
        data.Load();
        GroupView.ItemsSource = data.Groups;
    

【问题讨论】:

非常冗长的帖子,缺少您可能想问的问题。 如何使用 HierarchicalDataTemplate 以便树视图同时处理 SubGroups 和 Entries 集合? 重新格式化并移动了一些代码。不喜欢就回吧。处理您的问题.. 这是一个错误吗?在 YAML 中设置树项目源一次,在代码中设置一次。我期望引发异常的代码。 GroupView.ItemsSource = data.Groups 【参考方案1】:

HierarchicalDataTemplate 是一种表示“这是渲染此类对象的方式,这是一个可以探测以查找该对象下的子节点的属性”

因此,您需要一个返回此节点的“子节点”的属性。 例如(如果不能让 Group 和 Entry 都派生自一个通用的 Node 类型)

public class Group ....
        public IList<object> Items
        
            get
            
                IList<object> childNodes = new List<object>();
                foreach (var group in this.SubGroups)
                    childNodes.Add(group);
                foreach (var entry in this.Entries)
                    childNodes.Add(entry);

                return childNodes;
            
        

接下来,您不需要 HierarchicalDataTemplate 进行条目,因为条目没有子条目。因此需要更改 XAML 以使用新的 Items 属性和 DataTemplate 用于 Entry:

<TreeView Name="GroupView" Grid.Row="0" Grid.Column="0" ItemsSource="Binding">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="x:Type local:Group" ItemsSource="Binding Items">
            <TextBlock Text="Binding Path=Name" />
        </HierarchicalDataTemplate>
        <DataTemplate DataType="x:Type local:Entry" >
            <TextBlock Text="Binding Path=Name" />
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

这就是它的样子。

【讨论】:

+1 来自我 - 因为您在这两点上都验证了我自己的答案;) @Gishu:你觉得我的替代实现怎么样(见我的回答)?我更喜欢使用IEnumerable 的返回类型,除非我真的使用IList。此外,这种方法允许我们利用yield 关键字来简化代码。 @Matthew.. 不错的改进。最好的代码是您不必编写的代码:) +1 这个答案为我节省了大量时间。非常感谢 Gishu 的帮助 @Gishu,如果你有时间,你介意看看我的问题:HierarchicalDataTemplate and Many-to-Many with Payload Binding Issue 吗?我会非常感激——似乎对所涉及的概念有很好的处理。感谢您阅读:)【参考方案2】:

我认为您已经完成了大部分工作...只需进行一点返工,您应该可以很容易地完成此工作...

我建议您创建一个基本抽象类(或接口,随您的喜好)并为 Group 和 Entry 类继承/实现它...

这样,您可以在 Group 对象中公开一个属性

public ObservableCollection<ITreeViewItem> Children  get; set; 

^此时,您可以决定是否替换您的子组和条目列表,或者只是将它们附加在一起并在属性 getter 中返回它们...

现在您只需使用 Group 或 Entry 对象填充 Children 集合,当对象放入 TreeView 时,HierarchicalDataTemplate 将正确呈现..

最后一个想法,如果 Entry 始终是树的“底层”(即没有子级),那么您不需要为 Entry 对象定义 HierarchicalDataTemplateDataTemplate 就足够了。

希望这会有所帮助:)

【讨论】:

一个基本的工作示例将帮助新手掌握这个概念。但不管是好主意。 我采纳了您的建议,并使用 ITreeViewItem 以原始 JSON 作为输入来加载通用 JSON。我的答案可以在这里看到How to display JSON in WPF TreeView【参考方案3】:

这是 Gishu 答案的替代实现,它返回 IEnumerable 而不是 IList,并使用 yield 关键字来简化代码:

public class Group

    ...

    public IEnumerable<object> Items
    
        get
        
            foreach (var group in this.SubGroups)
                yield return group;
            foreach (var entry in this.Entries)
                yield return entry;
        
    

【讨论】:

谢谢!我被卡住了,这很有帮助。我无法同时正确显示条目和组。这为我节省了很多时间。【参考方案4】:

这篇文章在寻找相同问题的解决方案时帮助了我:http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html

使用 MultiBinding 和 CompositeCollectionConverter

【讨论】:

虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。

以上是关于WPF TreeView HierarchicalDataTemplate - 绑定到具有多个子集合的对象的主要内容,如果未能解决你的问题,请参考以下文章

wpf treeview节点前面添加图标

wpf如何根据输入信息动态生成treeview

wpf中怎么为treeview动态添加子菜单

wpf 自定义treeview 如何获得树节点集合

Ionic 2 Multilevel / Hierarchical select

wpf中的treeview如何增加2级节点?在C#中如何添加?