WPF TreeView HierarchicalDataTemplate - object binding with multiple child collections

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

How can I use the HierarchicalDataTemplate so that the TreeView processes both SubGroups and the collection of records?

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:




 <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; } } 
+53
c # wpf xaml treeview hierarchicaldatatemplate
Dec 16 '09 at 5:21
source share
4 answers

HierarchicalDataTemplate is a way of saying "this is how you render an object of this type, but here is a property that you can check to find the child nodes under this object."

Therefore, you need a single property that returns the descendants of this node. For example, (if you cannot make both the Group and the Input derived from the general type of 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; } } 

Further, you do not need a HierarchicalDataTemplate for the record, since the record has no children. Therefore, XAML needs to be modified to use the new Items and DataTemplate property 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 this is how it looks. Screen shot of output

+99
Dec 16 '09 at 6:19 06:19
source share

I think you are most on the way ... with tiny recycling you should get this job pretty easily ...

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

This way you can open the property inside the Group object

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

^ at this point you can decide if it replaces your lists of subgroups and records or just adds them together and returns them to the getter property ...

Now you need to populate the Children collection with Group or Entry objects, and the HierarchicalDataTemplate will display correctly when the objects are placed in the TreeView.

One final thought, if Entry is always the "bottom level" of the tree (that is, it has no children), you do not need to define a HierarchicalDataTemplate for the Entry object, just a DataTemplate .

Hope this helps :)

+11
Dec 16 '09 at 6:17
source share

Here is an alternative implementation of Guiche's answer that returns IEnumerable , not IList , and uses the yield keyword to simplify the code:

 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; } } } 
+9
Aug 04 2018-11-11T00:
source share

This article helped me find a solution to the same problem: http://blog.pmunin.com/2012/02/xaml-binding-to-compositecollection.html

using MultiBinding and CompositeCollectionConverter ..

/ Hello Anders

+3
Mar 07 '12 at 8:27
source share



All Articles