views:

66

answers:

2

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.

+1  A: 

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.

itowlson
thanks will try it!
miguel
While this is not technically wrong- he should indeed have a view-model class already defined for this kind of thing- I think this misses the point of the question. He's not asking how he should fix up his code to be MVVM-correct, he's asking how he can organize his existing data to represent distinct groups.
Charlie
Charlie: I actually think we're pretty much in agreement -- perhaps I phrased my answer badly, but we are both saying that HierarchicalDataTemplate is the right solution and very simple to use *provided* the data structure is correct. I've used the term "view model" for the view-friendly data structure, which you consider obfuscation -- but the data structures we've come up with are the same, and I agree with your implementation of that structure. The difference is terminology, not practice: I apologise if my use of the term "view model" appeared heavyweight or intimidating.
itowlson
+1  A: 

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

Charlie
thanks chalie, that worked!
miguel