Thursday, April 28, 2011

WPF TreeView ItemsSource, HierarchicalDataTemplate and other crazy stuff

Today, I have worked on a WPF utility which includes a TreeView for infomation display. I wanted to display Categories/Products information in the TreeView. Unfortunately, I found it quite difficult at the beginning.

After some try & error routine :) - I became quite accustomed to XAML, HierarchicalDataTemplate and DataTemplateSelector methodology.

So I would like to share my experiences with you, in case you have problems with getting TreeView to display items correctly.



I have faced 2 main problems:
#1 - Choosing optimal data structure for linking ItemsSource to it
Solved using self referencing data structure


#2 - Writing HierarchicalDataTemplate properly.
Found out that if problem #1 is solved as it should be, this becomes much easier.

Here is what I wanted to display in the TreeView.





As you can see, I wanted to display an unlimited number of categories and products, in a multi-level fashion.

Each category can contain 0...n categories, and also 0...n products.


I have intentionally omitted database details in this article and focused on View part of MVC pattern.

If you are interested in ADO.NET entity mechanism which grabs the information from a database, please let me know - and I will post it here.

After some mistakes - I decided to use the following approach for populating TreeView:

1. Created an self-referencing class TreeDataItem. Kinda like a Tree data structure.
2. Created two subclasses of TreeDataItem:

ProductDataItem for products
CategoryDataItem for categories

3. Made an ObservableCollection containing all categories and products.
4. Bounded this collection to my TreeView.ItemsSource

5. Defined HierarchicalDataTemplate / DataTemplateSelector

Here is an UML diagram of TreeDataItem and it's subclasses - ProductDataItem and CategoryDataItem:






Now, I needed to provide HierarchicalDataTemplate for TreeView, to let it know how to display the provided information.

As you can see from the picture #1, I wanted to display Products and Categories differently. And that can be a little bit confusing to do in WPF. If you want to display your TreeView items differently - you need to use a DataTemplateSelector object.

So, here is my custom version of DataTemplateSelector:



class TreeViewItemTemplateSelector:DataTemplateSelector
{
public DataTemplate ProductTemplate { get; set; }

public DataTemplate CategoryTemplate { get; set; }

public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
CategoryDataItem ci = item as CategoryDataItem ;

ProductDataItem pi = item as ProductDataItem;

bool x = item is CategoryDataItem;

if (pi != null)
{

return ProductTemplate;

}

else if (ci != null)
{
return CategoryTemplate;

}

else
{
return null;

}


}

}


Where are ProductTemplate and CategoryTemplate defined? In XAML code!

Here is XAML code for HierarchicalDataTemplates, DataTemplateSelector and TreeView:
<HierarchicalDataTemplate x:Key="ProductTemplate" ItemsSource="{Binding Path=ChildCategories}" >

<TextBlock Text="{Binding Path=Name}" FontWeight="Normal" FontStyle="Italic" />


</HierarchicalDataTemplate>

<HierarchicalDataTemplate x:Key="CategoryTemplate" ItemsSource="{Binding Path=ChildCategories}" >

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


<local:TreeViewItemTemplateSelector x:Key="TVDataTemplateSelector1" CategoryTemplate="{StaticResource CategoryTemplate}" ProductTemplate="{StaticResource ProductTemplate}" />


<DataTemplateSelector x:Key="TreeViewSelector">
</DataTemplateSelector>


And that's all you need to do to get the TreeView display Categories / Products properly (more or less, of course ;) ).

If you have any questions, please comment :))


Resulting TreeView:






Source code, of course, is posted!

1 comment:

Ubuntu 12.04, 14.04, 16.04 - auto start an app or script before login

To run a command or application at startup, even before the user has logged in, you can use this file: /etc/rc.local The commands entered...