views:

2441

answers:

4

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

How can I use the HierarchicalDataTemplate so that treeview will process both SubGroups and Entries collection?

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


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

XAML.CS


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

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

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...

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

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...

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..

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.

Hope this helps :)

IanR
+1 beat me to it :).. I had to try it out.
Gishu
+14  A: 

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'

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)

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 HierDataTemplate 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:

<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

Gishu
+1 from me - because you validated my own answer on both points ;)
IanR
And +1 from me because, well, it's the right answer :)
Anvaka
A: 

Hi

Good Answer by Gishu

Keep it up

Sachin

Sachin
A: 

Does anyone know the equivalent for Siverlight? Meaning, the HierarchicalDataTemplate does not have a DataType property in Silverlight. Any help would be appreciated.

Thanks, Greg

gcadmes