HierarchicalDataTemplate Distinct leaves
-
26-09-2019 - |
Question
I have a set of data that is structured like this:
ItemA.GroupA
ItemB.GroupA
ItemC.GroupB
ItemD.GroupC
I need to present the data in a WPF Tree View like this:
GroupA
--- ItemA
--- ItemB
GroupB
--- ItemC
GroupC
--- ItemD
What XAML can I use to group the leaves by distinct value? For instance, there could be multple items in the collection which are GroupA.ItemA, however, I only want to present the node and leaf once.
Solution
I disagree with itowlson here. This isn't a hairy problem and HierarchicalDataTemplate
was made for this kind of thing. Before you go haphazardly diving into patterns and view-models and other unnecessary obfuscation, consider that your problem can be solved with two classes and a single Linq GroupBy
statement.
Here are your classes:
public class GroupItem
{
public string Name
{
get;
private set;
}
public string Group
{
get;
private set;
}
public GroupItem(string name, string group)
{
Name = name;
Group = group;
}
}
public class Group
{
public IEnumerable<GroupItem> Children
{
get;
set;
}
public string Name
{
get;
private set;
}
public Group(string name)
{
Name = name;
}
}
So far, so good. You've got two straightforward classes to hold all the necessary data. Names and Groups are stored as strings. A Group
has a collection of GroupItem
. Now for the code in your Window
:
public partial class DistinctLeaves : Window
{
public ObservableCollection<GroupItem> Items
{
get;
set;
}
public IEnumerable<Group> Groups
{
get;
set;
}
public DistinctLeaves()
{
Items = new ObservableCollection<GroupItem>();
Items.Add(new GroupItem("Item A", "Group A"));
Items.Add(new GroupItem("Item B", "Group A"));
Items.Add(new GroupItem("Item C", "Group B"));
Items.Add(new GroupItem("Item D", "Group C"));
Groups = Items.
GroupBy(i => i.Group).
Select(g => new Group(g.Key) { Children = g });
InitializeComponent();
}
}
Once again, this is all boilerplate except the group-by line. That statement merits further investigation. This will group your items collection according to their Group
property. After the items are in groups, you then create a new instance of the Group
class. Pass in the group's name property (which is the key), and set the children to the group itself, and ta-da!
Finally, here is the XAML for the Window
, which uses the HierarchicalDataTemplate
:
<Window x:Class="TestWpfApplication.DistinctLeaves"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DistinctLeaves" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TreeView ItemsSource="{Binding Groups}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
And here is the result:
alt text http://img339.imageshack.us/img339/8555/distinctleaves.jpg
OTHER TIPS
You're not going to be able to do that in XAML, at least not in any natural way. A better approach is to introduce a view model -- a class that represents your data in a view-friendly manner so that you can keep your XAML simple and natural.
Thus, in your case, you would have a class that wraps your ItemX collection, and exposes the groups and their distinct members:
public class MyItemsViewModel
{
public IList<MyGroupViewModel> Groups { get; }
}
public class MyGroupViewModel
{
public string GroupName { get; }
public IList<MyItem> DistinctItems { get; }
}
Your HierarchicalDataTemplate will then fall out almost automatically. As a further benefit, because your view model classes are just data objects, you can put them under automated test, whereas complex XAML requires manual testing.