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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1912481/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 21:47:26  来源:igfitidea点击:

WPF TreeView HierarchicalDataTemplate - binding to object with multiple child collections

c#wpfxamltreeviewhierarchicaldatatemplate

提问by longday

I am trying to get a TreeViewto bind my collection so that all groups show nested groups and each group will show entry.

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

How can I use the HierarchicalDataTemplateso that the TreeViewwill process both SubGroups and Entries collection?

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

Groups show subgroups and entries:

组显示子组和条目:

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



Objects:

对象:



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; }
    }
}

Test data:

测试数据:



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:

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:

XAML.CS:



public partial class Window2 : Window
{
    public Window2()
    {
        InitializeComponent();
        LoadView();
    }

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

采纳答案by Gishu

A HierarchicalDataTemplateis a way of saying 'this is how you render this type of object and here is a property that can be probed to find the child nodes under this object'

AHierarchicalDataTemplate是一种说法,“这就是您渲染此类对象的方式,这是一个可以探测以找到此对象下的子节点的属性”

Therefore you need a single property that returns the 'children' of this node. e.g. (If you can't make both Group and Entry derive from a common Node type)

因此,您需要一个返回此节点的“子节点”的属性。例如(如果你不能让 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;
            }
        }

Next you don't need a HierarchicalDataTemplatefor entry since an entry doesn't have children. So the XAML needs to be changed to use the new Items property and a DataTemplatefor Entry:

接下来您不需要HierarchicalDataTemplatefor 条目,因为条目没有子项。因此需要更改 XAML 以使用新的 Items 属性和一个DataTemplatefor 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>

And here's what that looks like. Screenshot of Output

这就是它的样子。 输出截图

回答by kiwipom

I think you are most of the way there... with a tiny bit of rework you should get this working fairly easily...

我认为您已经完成了大部分工作……只需稍加返工,您就应该可以很容易地完成这项工作……

I would suggest you create a base abstract class (or an interface, whichever you prefer) and inherit/implement it for both the Group and Entry class...

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

That way, you can expose a property within your Group object

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

public ObservableCollection<ITreeViewItem> Children { get; set; }

^at this point, you can make a decision if this replaces your lists of SubGroups and Entries, or merely appends them together and returns them in the property getter...

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

Now all you need is to populate the Children collection with either Group or Entry objects, and the HierarchicalDataTemplatewill render correctly when the objects are placed in the TreeView..

现在您所需要的只是使用 Group 或 Entry 对象填充 Children 集合,并且HierarchicalDataTemplate当对象放置在 TreeView 中时它们将正确呈现。

One final thought, if Entry is always the 'bottom level' of the tree (ie has no children) then you do not need to define a HierarchicalDataTemplatefor the Entry object, a DataTemplatewill suffice.

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

Hope this helps :)

希望这可以帮助 :)

回答by Matthew

Here is an alternative implementation of Gishu's answer that returns an IEnumerablerather than an IList, and makes use of the yieldkeyword to simplify the code:

这是 Gishu 答案的另一种实现,它返回 anIEnumerable而不是 an 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;
        }
    }
}

回答by Anders

This post helped me out when looking for a solution for tha same issue: http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html

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

using MultiBinding and CompositeCollectionConverter..

使用 MultiBinding 和 CompositeCollectionConverter..

/Regards Anders

/问候安德斯