Feb 04, 2006

How to display grouped data in a TreeView

15GroupingTreeView

The TreeView control is great at displaying structured data using the HierarchicalDataTemplate (see Karsten’s blog post on this topic). But what do you do if the data you’re given is not structured hierarchically? In this post, I will show you how to create that hierarchy from a flat list of data items, using the grouping feature of data binding.

I am using the same Animal data source I used in my last post. Grouping the Animals by Category is done the same way as in my last sample:

    <local:Animals x:Key="animals"/>

    <CollectionViewSource x:Key="cvs" Source="{Binding Source={StaticResource animals}, Path=AnimalList}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="Category"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>

We now have the data in a hierarchical form. In this particular case it has only one level of groups, and another level with the animals. You can easily imagine that by adding more GroupDescriptions you would end up with a deeper hierarchy.

When binding to a CollectionViewSource, the Binding object knows to grab the CollectionViewSource’s View property. This property returns the custom view (of type ICollectionView) that CollectionViewSource creates on top of the data collection (where the grouping is applied). In our scenario, we want to bind to the hierarchy we created with grouping, or in other words, we want to bind to the groups. We can get to this data by binding to the Groups property in ICollectionView:

    <TreeView ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups}" ItemTemplate="{StaticResource categoryTemplate}" Width="200">
    </TreeView>

When using data binding’s grouping feature, each group of items is wrapped in a CollectionViewGroup object. We can access the name of the group (the property we’re grouping by) by using CollectionViewGroup’s Name property, and we can get to the items that belong to the group through the Items property. This is all the information we need in order to make a HierarchicalDataTemplate that will display the Category of each animal and specify the animals that belong to it:

    <HierarchicalDataTemplate x:Key="categoryTemplate" ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource animalTemplate}">
        <TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
    </HierarchicalDataTemplate>

Finally we need a DataTemplate for the leaf nodes, which specifies how we want the Animal data to be displayed. In this case, we are interested in displaying the Name property of each Animal. Notice that the HierarchicalDataTemplate’s ItemTemplate property points to this template.

    <DataTemplate x:Key="animalTemplate">
        <TextBlock Text="{Binding Path=Name}"/>
    </DataTemplate>

Here is the result of the completed sample:

Here you can find the VS project with this sample code. This works with January CTP WPF bits.

Update September 18, 2007: Here you can find this sample working with Orcas Beta 2 bits.

22 Comments
  1. Brett

    Hi Bea,

    We would like to show a set of objects grouped in a TreeView based on the CLR type as opposed to a property value. Are there any plans for a TypeGroupDescription? Is such a thing even possible?

    Regards,

    Brett

    • Bea

      Yes, that is possible with the current implementation of Grouping. You can do that by using the Converter property of PropertyGroupDescription and writing custom code where you test the CLR type of the data item. Items will be grouped by the value you return in the converter.

      How about I make that my next blog post? :)

      • Brett

        Perfect! I am unsure whether I am approaching this problem from the right direction (e.g. grouping vs. other method), but I would be very interested to see how you would use the Converter property for this.

        You made my day! Thanks!

        Brett

        • Bea

          Hi Brett,

          Does my last blog post do what you were looking for? Let me know if you have any further questions on this.

          And thanks for the idea :)

  2. Josh Smith

    Hello Beatriz,

    In the June CTP of WPF, when I run this sample and navigate the tree items via the Up and Down arrow keys, an exception is thrown. I have reported this bug in the WPF newsgroups, but received no reply yet. This seems like an important bug so I am reporting it through as many channels as I can.

    Cheers,
    Josh Smith

    • Bea

      Hi Josh,

      That is a pretty bad bug. I am able to repro it just as you explained and opened the corresponding bug in our database.

      I really appreciate your feedback on this. Thanks for making Avalon a better platform :)

      Bea

  3. Sam

    Hello,

    This is a really old post, but perhaps you get notified by email when a new reply comes..

    I want to modify your GroupingTreeView sample to bind to a property of the window’s DataContext, instead of to a static resource.

    The property is an ObservableCollection and
    I want to replace the first line of the CollectionViewSource declaration with something like this:
    Source=”{Binding Path=AnimalList}”

    Is this possible these days? Thank you very much if you do get this!

    Sam

    • Bea

      Hi Sam,

      Yes, that is possible to do and very easy. All you have to do is add the following to the constructor:

      public Window1()
      {
      this.DataContext = new Animals();
      this.InitializeComponent();
      }

      I uploaded this blog’s code with the DataContext here.

      Thanks,
      Bea

  4. Anonymous

    Great, but, what’ll happen if I want to renderize this tree with a class model, like Windows.Forms.

    • Bea

      Regarding your “class model” question, can you please expand on that? I’m not sure what you mean…
      Thanks,
      Bea

  5. Shanty

    mmm, If you find my comments very fool, sorry, i’m a starter.
    I want to do something like your tree (grouping), but runtime error is raised in this treeview line
    ItemTemplate=”{StaticResource categoryTemplate}”

    why??

    thanks for alls your examples beatriz.

    • Shanty

      emm, ok, i’d downloaded groupingtreeview2 and everything’s ok. Thanks Bea, thank you so much.

      • Bea

        Shanty,

        I’m glad you were able to find the problem in your app.

        Thanks,
        Bea

        • Shanty

          I’m still working with your tree. I inserted INotifyPropertyChanged and ObservableCollection and,a new tree and more classes.
          I can say that your tree is for me a tutorial tree, but, I’m now with a little problem. Well, the tere is grouping by CollectionViewSource and the first level is the group name, but, if you want to viwe gropu name in second level?? and for example in the first level is the jungle?? It’s possible??

          regards, shanty

        • Bea

          Hi Shanty,

          You can add another level of grouping by adding a second PropertyGroupDescription to the GroupDescriptions collection of CollectionViewSource. Does this answer your question?

          Thanks,
          Bea

  6. Aeoth

    Hi Bea,
    I’ve got my Treeview setup – “sorting” based on the property.
    What happens if say…’evolution’ occurs, and a frog evolves into a bear group?

    I’m using INotifyPropertyChanged but it doesnt’ seem to ‘move’ my frog over into the next group.

    • Bea

      Hi Aeoth,

      Currently, if you change the property you’re grouping on, we don’t refresh the groups automatically. You can refresh them yourself with the following code:

      CollectionViewSource cvs = this.Resources["cvs"] as CollectionViewSource;
      cvs.View.Refresh();

      We discussed the feature you’re asking a long time ago, but decided to not add it in the first version of WPF because it’s not trivial to design. For your particular scenario, since you’re using PropertyGroupDescription to generate the groups, it would be easy for grouping code to listen to property changes on the property specified. However, if the user decides to use custom grouping, we would not be able to rely on INotifyPropertyChanged to get change notifications. We want to support this feature at some point, but that will require either only support non-custom grouping, or coming up with a new design that supports all kinds of groupings.

      You can see a project with this blog sample working with property changes here.

      Thanks,
      Bea

      • Aeoth

        Bea, thanks! :D
        That works perfectly

  7. Alex

    Bea,

    Is there a way to use Hierarchical datatemplate on the treeview with anonymous types?
    I want to bind treeview to a result of LINQ query without wrapping it in a concrete type.

    Thanx, Alex

    • Bea

      Hi Alex,

      Templating anonymously typed data works fine, as long as you give the template a key in the resources, and point the ItemTemplate to that key. Implicit templates (the ones that are applied by setting the DataType property) will not work in this scenario.

      I created a simple sample of a ListBox bound to a collection of anonymous types, and used an explicit DataTemplate to template the data items. You can find it here. You can do something similar for TreeView, by setting the ItemTemplate property of the TreeView, plus the ItemTemplate property of the HierarchicalDataTemplate.

      Let me know if this helps.

      Bea

  8. Rashish Tandon

    Hi Bea,

    In relation to an earlier post, if a Property changes and we do cvs.View.Refresh(), it collapses the entire tree and we need to expand it to see the changes. Is there any way to prevent this ?

    • Bea

      Hi Rashish,

      There is no way to prevent that behavior, other than expanding the TreeView again. You can read about expanding TreeView in the following 3 posts: part 1, part 2, part 3.

      Bea

Comments are closed.