Welcome to MSDN Blogs Sign in | Join | Help

Data See, Data Do

Mike Hillberg's Blog on Wpf and Silverlight

Syndication

News

All postings are provided "As Is" with no warranties, and confers no rights. Opinions and views expressed here are not necessarily those of Microsoft Corporation.


A TreeView, a HierarchicalDataTemplate, and a 2D collection walk into a bar ...

Bea has a handy post describing how to group items in a collection using a CollectionViewSource.  I was looking at that, and a post on the WPF forum from markovuksanovic, and for fun created a version of Bea’s example that uses a 2D collection instead of the CollectionViewSource.  (I found a bunch of HierarchicalDataTemplate examples using CollectionViewSource or XmlDataProvider, but couldn’t find any using nested collections.)  Anyway, here’s the result …

 

First, by 2D collection, I mean a collection whose items are themselves collections, like a 2D array.  The “parent” collection here is a collection of AnimalCategory objects, each of which has a category name and a collection of Animal objects for that category.  So AnimalCategory looks like (all of this is in a namespace named “HierarchicalDataTemplateTest”):

 

namespace HierarchicalDataTemplateTest

{

    ...

    public class AnimalCategory

 

        private string _category;

        public string Category

        {

            get { return _category; }

            set { _category = value; }

        }

 

        private ObservableCollection<Animal> _animals;

        public ObservableCollection<Animal> Animals

        {

            get

            {

                if (_animals == null)

                    _animals = new ObservableCollection<Animal>();

 

                return _animals;

            }

        }

 

        public AnimalCategory()

        {

        }

 

        public AnimalCategory(

                    string category,

                    ObservableCollection<Animal> animals)

        {

            _category = category;

            _animals = animals;

        }

 

    }

    ...

}

 

… and Animal looks like:

 

namespace HierarchicalDataTemplateTest

{

    ...

    public class Animal

    {

        private string _name;

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

 

        public Animal()

        {

        }

 

        public Animal(string name)

        {

            _name = name;

        }

 

    }

    ...

}

 

… and these get used in a sample Window application, whose code looks like:

 

public partial class Window1 : System.Windows.Window

{

    static public ObservableCollection<AnimalCategory> AnimalCategories

        = new ObservableCollection<AnimalCategory>();

 

    public Window1()

    {

        InitializeComponent();

 

        ObservableCollection<Animal> animals = new ObservableCollection<Animal>();

        animals.Add(new Animal("California Newt"));

        animals.Add(new Animal("Tomato Frog"));

        animals.Add(new Animal("Green Tree Frog"));

        AnimalCategories.Add( new AnimalCategory("Amphibians", animals) );

 

        animals = new ObservableCollection<Animal>();

        animals.Add(new Animal("Golden Silk Spider"));

        animals.Add(new Animal("Black Widow Spider"));

        AnimalCategories.Add(new AnimalCategory("Spiders", animals));

    }

 

 

That is, our Window1 has an collection named AnimalCategories of AnimalCategory objects, and each of those has a collection of Animal objects.

 

The markup in our Window displays these in a hierarchy – animals in their categories – using a TreeView.  In this case, the TreeView is bound to the static AnimalCategories collection that we created in the above code:

 

<Window x:Class="HierarchicalDataTemplate.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="HierarchicalDataTemplate" Height="300" Width="300"

    xmlns:local="clr-namespace:HierarchicalDataTemplateTest"

    >

 

  <!-- Create a TreeView, and have it source data from

       the AnimalCategories collection -->

  <TreeView ItemsSource="{x:Static local:Window1.AnimalCategories}">

 

    <!-- Specify the template that will display a node

         from AnimalCategories.  I.e., one each for “Amphibians”

         and “Spiders” in this sample.  It will get its nested

         items from the "Animals" property of each item -->

    <TreeView.ItemTemplate>

      <HierarchicalDataTemplate ItemsSource="{Binding Path=Animals}">

 

        <!-- Display the AnimalCategory by showing it's Category string -->

        <TextBlock FontWeight="Bold" Text="{Binding Path=Category}" />

 

        <!-- Specify the nested template for the individual Animal items

             that are within the AnimalCategories.  E.g. “California Newt”, etc. -->

        <HierarchicalDataTemplate.ItemTemplate>

          <DataTemplate>

            <TextBlock Text="{Binding Path=Name}"/>

          </DataTemplate>

        </HierarchicalDataTemplate.ItemTemplate>

       

      </HierarchicalDataTemplate>

    </TreeView.ItemTemplate>

  </TreeView>

</Window>

 

(Note that the "xmlns:local='clr-namespace:HierarchicalDataTemplateTest'" is a reference to the CLR namespace that holds the Animal and AnimalCategory classes.) 

 

The end result is:

 

 

 

 

 

Just to make it more interesting, let’s play with some different options.  Recall that TreeView is an ItemsControl.  ItemsControl can get its items from the ItemsSource property, as the above example shows.  But ItemsControl also has its own built-in collection, which is the Items property.  So instead of the code creating a special collection for the AnimalCategories objects, it could just add the AnimalCategory objects to the Items property.  That is, make this change in the markup (the yellow part is new):

 

<TreeView ItemsSource="{x:Static local:Window1.AnimalCategories}" Name="TreeView1">

 

… and update Window1.xaml.cs to use the TreeView’s Items collection:

 

public partial class Window1 : System.Windows.Window

{

    static public ObservableCollection<AnimalCategory> AnimalCategories

        = new ObservableCollection<AnimalCategory>();

 

    public Window1()

    {

        InitializeComponent();

 

        ObservableCollection<Animal> animals = new ObservableCollection<Animal>();

        animals.Add(new Animal("California Newt"));

        animals.Add(new Animal("Tomato Frog"));

        animals.Add(new Animal("Green Tree Frog"));

        AnimalCategories.Add( new AnimalCategory("Amphibians", animals) );

        TreeView1.Items.Add(new AnimalCategory("Amphibians", animals));

 

        animals = new ObservableCollection<Animal>();

        animals.Add(new Animal("Golden Silk Spider"));

        animals.Add(new Animal("Black Widow Spider"));

        AnimalCategories.Add(new AnimalCategory("Spiders", animals));

        TreeView1.Items.Add(new AnimalCategory("Spiders", animals));

    }

 

}

 

 

… and you get the same application.

 

 

Finally, one last sample.  The above code is still “new”-ing a lot of objects, and creating objects is what Xaml was invented for.  So reduce the Window1 code to its minimal form:

 

public partial class Window1 : System.Windows.Window

{

    static public ObservableCollection<AnimalCategory> AnimalCategories

        = new ObservableCollection<AnimalCategory>();

 

    public Window1()

    {

        InitializeComponent();

 

        ObservableCollection<Animal> animals = new ObservableCollection<Animal>();

        animals.Add(new Animal("California Newt"));

        animals.Add(new Animal("Tomato Frog"));

        animals.Add(new Animal("Green Tree Frog"));

        TreeView1.Items.Add(new AnimalCategory("Amphibians", animals));

 

        animals = new ObservableCollection<Animal>();

        animals.Add(new Animal("Golden Silk Spider"));

        animals.Add(new Animal("Black Widow Spider"));

        TreeView1.Items.Add(new AnimalCategory("Spiders", animals));

    }

 

}

 

… and create those animals in the Xaml instead (updated lines in yellow):

 

<Window x:Class="HierarchicalDataTemplate.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="HierarchicalDataTemplate" Height="300" Width="300"

    xmlns:local="clr-namespace:HierarchicalDataTemplateTest"

    >

 

  <!-- Create a TreeView, and have it source data from

       the AnimalCategories collection -->

  <TreeView Name="TreeView1">

 

    <!-- Specify the template that will display a node

         from AnimalCategories.  It will get its nested

         items from the "Animals" property of each item -->

    <TreeView.ItemTemplate>

      <HierarchicalDataTemplate ItemsSource="{Binding Path=Animals}">

 

        <!-- Display the AnimalCategory by showing it's Category string -->

        <TextBlock FontWeight="Bold" Text="{Binding Path=Category}" />

 

        <!-- Specify the nested template for the individual Animal items

             that are within the AnimalCategory items. -->

        <HierarchicalDataTemplate.ItemTemplate>

          <DataTemplate>

            <TextBlock Text="{Binding Path=Name}"/>

          </DataTemplate>

        </HierarchicalDataTemplate.ItemTemplate>

       

      </HierarchicalDataTemplate>

    </TreeView.ItemTemplate>

 

    <local:AnimalCategory Category="Amphibians">

      <local:AnimalCategory.Animals>

        <local:Animal Name="California Newt" />

        <local:Animal Name="Tomato Frog" />

        <local:Animal Name="Green Tree Frog" />

      </local:AnimalCategory.Animals>

    </local:AnimalCategory>

 

    <local:AnimalCategory Category="Spiders">

      <local:AnimalCategory.Animals>

        <local:Animal Name="Golden Silk Spider" />

        <local:Animal Name="Black Widow Spider" />

      </local:AnimalCategory.Animals>

    </local:AnimalCategory>

 

  </TreeView>

       

</Window>

 

Note here that the <AnimalCategory> tags are directly under the <TreeView> tag; since content of a <TreeView> goes automatically to the Items property, this markup is equivalent to the previous “TreeView.Items.Add…” code.

 

 

Published Wednesday, October 11, 2006 8:15 AM by MikeHillberg

Filed under: ,

Attachment(s): hdt.jpg

Comments

# re: A TreeView, a HierarchicalDataTemplate, and a 2D collection walk into a bar ... @ Thursday, March 08, 2007 3:58 AM

Is there any way of binding some TreeCollection to WPF TreeView? If we dont know how many levels this collection will have?

zajda82

# re: A TreeView, a HierarchicalDataTemplate, and a 2D collection walk into a bar ... @ Monday, April 30, 2007 6:59 PM

Re: "Is there any way of binding some TreeCollection to WPF TreeView? If we dont know how many levels this collection will have?"

Yes, TreeView expands to any depth.  If you look at the ItemTemplate for the TreeView above, you see it is a HierarchicalDataTemplate with an ItemsSource:

<HierarchicalDataTemplate ItemsSource="{Binding Path=Animals}">

That ItemsSource property explains how, at each level of the tree view, to find the next level.

This post from Bea (http://www.beacosta.com/2006/02/how-do-i-display-grouped-data-in.html) and this from Karsten (http://blogs.msdn.com/karstenj/archive/2005/11/02/488420.aspx) have more info on TreeView and HierarchicalDataTemplate.

MikeHillberg

# re: A TreeView, a HierarchicalDataTemplate, and a 2D collection walk into a bar ... @ Monday, July 02, 2007 8:30 AM

Could not get the example to work, it does not care for the DataTemplate tag nested in the HeirarchicalDataTemplate.ItemTemplate tag.

Was this example tested?

kidsysco

# re: A TreeView, a HierarchicalDataTemplate, and a 2D collection walk into a bar ... @ Thursday, July 05, 2007 7:33 AM

OK I got this example to build. I think that it is important to deliver folks at least a little info on the namespace delcared at the top of the XAML file. I do have a fundamental understanding of WPF and I can manage to get alot done with it but I did not realize that the XAML needs a reference to the namespace that it is operating in. I guess that just sounds weird to me and I do not quite understand why. I will go ahead and read those sections of the WPF spec again to see if I can figure out why.

xmlns:local="clr-namespace:HierarchicalDataTemplateTest"

I had to change that line to ...

xmlns:local="clr-namespace:myNameSpaceForThisXamlFile"

Thanks for your help Mike!

kidsysco

# re: Could not get the example to work @ Thursday, July 05, 2007 1:19 PM

Sorry kidsysco, you're right; I should have called that out and included the namespace in the .cs part of the sample.  I've updated it now.

Thanks for posting the comment!

MikeHillberg

# Load a Treeview on Demand ... @ Monday, September 17, 2007 10:35 AM

I try hard to load data only when user click on leaf.

I have succeeded in creating Treeview data in one shot but data are very heavy to load and I'd like to go down in tree only when needed ...

Ideas guys ?

Thanks

Eric

Remora

# re: A TreeView, a HierarchicalDataTemplate, and a 2D collection walk into a bar ... @ Friday, October 05, 2007 3:39 AM

Hello Mike,

How do we design HierarchicalDataTemplate for nested collections.

For example

<Collection>

   <Collection>

       <Collection>

The above stuff is just a raw example.

But we can have objects of same types nested inside each other.

for example,

Node having list of Nodes.

Could you please help on this.

Regards

AD

anildhan

Anonymous comments are disabled
Page view tracker