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
WPF TreeView HierarchicalDataTemplate - binding to object with multiple child collections
提问by longday
I am trying to get a TreeView
to bind my collection so that all groups show nested groups and each group will show entry.
我正在尝试TreeView
绑定我的集合,以便所有组都显示嵌套组,并且每个组都将显示条目。
How can I use the HierarchicalDataTemplate
so that the TreeView
will 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 HierarchicalDataTemplate
is 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 HierarchicalDataTemplate
for entry since an entry doesn't have children. So the XAML needs to be changed to use the new Items property and a DataTemplate
for Entry:
接下来您不需要HierarchicalDataTemplate
for 条目,因为条目没有子项。因此需要更改 XAML 以使用新的 Items 属性和一个DataTemplate
for 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.
这就是它的样子。
回答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 HierarchicalDataTemplate
will 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 HierarchicalDataTemplate
for the Entry object, a DataTemplate
will suffice.
最后一个想法,如果 Entry 始终是树的“底层”(即没有孩子),那么您不需要HierarchicalDataTemplate
为 Entry 对象定义 a ,aDataTemplate
就足够了。
Hope this helps :)
希望这可以帮助 :)
回答by Matthew
Here is an alternative implementation of Gishu's answer that returns an IEnumerable
rather than an IList
, and makes use of the yield
keyword 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
/问候安德斯