Hierarchical Data Templates

RadTreeViewItem inherits from HeaderedItemsControl and they can display hierarchical data, e.g. collections that contain other collections.

The HierarchicalDataTemplate class is designed to be used with HeaderedItemsControl types to display such data. There should be virtually no differences between the usage of HierarchicalDataTemplate in RadTreeView and other controls.

The following example demonstrates how to create a hierarchical data source and bind a RadTreeView to it, using a HierarchicalDataTemplate. The ItemsSource property of the HierarchicalDataTemplate specifies the binding that has to be applied to the ItemsSource property of each item. The DataTemplate property specifies the template that has to be applied on each item, while the ItemTemplate is the template applied on its child items. You can nest several HierarchicalDataTemplate declarations if you need a deeper level of hierarchy.

If you have a databound control, then you must consider the following rules.The ItemsControl prepares its children in a very different way, depending on the type of the child:

  • If it is a ContentControl, the ItemTemplate will be set as a ContentTemplate and the non-visual element as content.
  • If it is a HeaderedControl, the ItemTemplate will be set as a HeaderTemplate and the data item as a Header.
  • If it is a HeaderedContentControl (i.e. the two above combined), the item will go as Content, but the ContentTemplate won't be set. Instead, the item will be set as a Header and the ItemTemplate will be used as a HeaderTemplate.

If the item is an ItemsControl, these properties are set to the child items as well, what is called "inherited": ItemTemplate, ItemTemplateSelector, ItemContainerStyle, ItemContainerStyleSelector, ItemStringFormat, AnimationManager.IsAnimationEnabled.In the case of the RadTreeView, the tree items are HeaderedItemsControl, which means that the above properties will be passed along to all the items, and there is no need to set the ItemTemplate property of the HierarchicalDataTemplate, especially if there is a selector.The ItemContainerStyle will be set as a style for the containers, if there is none and an ItemContainerStyleSelector is present, a style will be selected. Then the ItemContainerStyle (Selector) properties will be passed along ig the container is an ItemsControl.The above rules can "mix" with the properties set in the HierarchicalDataTemplate to create even more combinations of ways to set the templates of items. The HeaderTemplate displayed for an item can be a result of one of the following:

  • Directly (locally) set HeaderTemplate.
  • Directly (locally) set HeaderTemplateSelector.
  • ItemTemplate of the parent ItemsControl.
  • ItemTemplateSelector of the parent ItemsControl.
  • ItemContainerStyle with a HeaderTemplate of the parent ItemsControl.
  • ItemContainerStyleSelector which selects a style with a HeaderTemplate.
  • ItemContainerStyle with a HeaderTemplateSelector of the parent ItemsControl.
  • ItemContainerStyleSelector which selects a style with a HeaderTemplateSelector.
  • An ItemTemplate set in the HierarchicalDataTemplate of the parent.
  • An ItemTemplateSelector set in the HierarchicalDataTemplate of the parent.
  • An ItemContainerStyle set in the HierarchicalDataTemplate of the parent, which sets a HeaderTemplate.
  • An ItemContainerStyleSelector set in the HierarchicalDataTemplate of the parent, which selects a style that sets a HeaderTemplate.
  • In WPF you have the DataType property of the DataTemplate which will be a last fallback if a template is not set at all.

The Telerik HierarchicalDataTemplate offers two properties - ItemContainerStyle and ItemContainerStyleSelector, which allows you to make your hierarchy quite flexible.An interesting point here is the precedence over what should be set. As always, local values are stronger than styles. But what happens when you have both an ItemContainerStyle set in the ItemsControl and the HierarchicalDataTemplate. Also what happens when you have an ItemContainerStyle set in the two above and in the ItemContainerStyle as well. The rule is that the ItemContainerStyle from the HierarchicalDataTemplate is applied to the first level of containers that has a style which is the same as the parent's. The last ItemContainerStyle to be actively set is inherited from them on. The same rule applies for selectors.For more information see the example below.

  • Create a new class and name it MyViewModel, which implements the INotifyPropertyChanged interface.

        public class MyViewModel : INotifyPropertyChanged 
        { 
            private String title; 
            private DateTime dateCreated; 
            private double price; 
            public MyViewModel() 
            { 
                RelatedItems = new ObservableCollection<object>(); 
            } 
            public double Price 
            { 
                get 
                { 
                    return this.price; 
                } 
                set 
                { 
                    if ( this.Price == value ) 
                        return; 
                    this.price = value; 
                    OnPropertyChanged( "Price" ); 
                } 
            } 
            public DateTime DateCreated 
            { 
                get 
                { 
                    return this.dateCreated; 
                } 
                set 
                { 
                    if ( this.DateCreated == value ) 
                        return; 
                    this.dateCreated = value; 
                    OnPropertyChanged( "DateCreated" ); 
                } 
            } 
            public String Title 
            { 
                get 
                { 
                    return this.title; 
                } 
                set 
                { 
                    if ( this.Title == value ) 
                        return; 
                    this.title = value; 
                    OnPropertyChanged( "Title" ); 
                } 
            } 
            public IList<object> RelatedItems 
            { 
                get; 
                set; 
            } 
            public event PropertyChangedEventHandler PropertyChanged; 
            protected virtual void OnPropertyChanged( string info ) 
            { 
                PropertyChangedEventHandler temp = this.PropertyChanged; 
                if ( temp != null ) 
                    temp( this, new PropertyChangedEventArgs( info ) ); 
            } 
        } 
    
        Public Class MyViewModel 
            Implements INotifyPropertyChanged 
            Private m_title As [String] 
            Private m_dateCreated As DateTime 
            Private m_price As Double 
     
            Public Sub New() 
                RelatedItems = New ObservableCollection(Of Object)() 
            End Sub 
     
            Public Property Price() As Double 
                Get 
                    Return Me.m_price 
                End Get 
                Set(ByVal value As Double) 
                    If Me.Price = value Then 
                        Return 
                    End If 
     
                    Me.m_price = value 
                    OnPropertyChanged("Price") 
                End Set 
            End Property 
     
            Public Property DateCreated() As DateTime 
                Get 
                    Return Me.m_dateCreated 
                End Get 
                Set(ByVal value As DateTime) 
                    If Me.DateCreated = value Then 
                        Return 
                    End If 
     
                    Me.m_dateCreated = value 
                    OnPropertyChanged("DateCreated") 
                End Set 
            End Property 
     
            Public Property Title() As [String] 
                Get 
                    Return Me.m_title 
                End Get 
                Set(ByVal value As [String]) 
                    If Me.Title = value Then 
                        Return 
                    End If 
     
                    Me.m_title = value 
                    OnPropertyChanged("Title") 
                End Set 
            End Property 
     
        Private _RelatedItems As IList(Of Object) 
            Public Property RelatedItems() As IList(Of Object) 
                Get 
                    Return _RelatedItems 
                End Get 
                Set(ByVal value As IList(Of Object)) 
                    _RelatedItems = value 
                End Set 
            End Property 
     
            Public Event PropertyChanged As PropertyChangedEventHandler 
     
            Protected Overridable Sub OnPropertyChanged(ByVal info As String) 
                Dim temp As PropertyChangedEventHandler = Me.PropertyChanged 
                RaiseEvent temp(Me, New PropertyChangedEventArgs(info)) 
            End Sub 
        End Class 
    

    The class has four properties:

    • Property Price which is of type double.
    • Property CreatedOn which is of type DateTime.
    • Property Title which is of type string.
    • Property RelatedItems which is a collection of objects. These are the child items.
  • Add a static method to the class which aims to create some mock-up data:

        public static IList<object> GetItems( string name ) 
        { 
            var result = new ObservableCollection<object>(); 
            foreach ( var num in Enumerable.Range( 1, 5 ) ) 
            { 
                var item = new MyViewModel(); 
                item.DateCreated = DateTime.Today.AddDays( -num % 15 ); 
                item.Price = num * 100 + Convert.ToDouble( num ) / 100; 
                item.Title = String.Format( "{0} {1}", name, num ); 
                for ( int i = 0; i < 5; i++ ) 
                { 
                    var child = new MyViewModel(); 
                    child.DateCreated = DateTime.Today.AddDays( -num % 5 - i ); 
                    child.Price = num * 100 + Convert.ToDouble( num + i ) / 100; 
                    child.Title = String.Format( "{0} {1}'s {2}", name, num, i ); 
                    item.RelatedItems.Add( child ); 
                    for ( int j = 0; j < 3; j++ ) 
                    { 
                        var grandChild = new MyViewModel(); 
                        grandChild.DateCreated = DateTime.Today.AddDays( -num % 5 - i + 2 ); 
                        grandChild.Price = num * 100 + Convert.ToDouble( num + i ) / 100; 
                        grandChild.Title = String.Format( "{0} {1} : {2}'s {3}", name, num, i, j ); 
                        child.RelatedItems.Add( grandChild ); 
                    } 
                } 
                result.Add( item ); 
            } 
            return result; 
        } 
    
        Public Shared Function GetItems(ByVal name As String) As IList(Of Object) 
            Dim result = New ObservableCollection(Of Object)() 
            For Each num In Enumerable.Range(1, 5) 
                Dim item = New MyViewModel() 
                item.DateCreated = DateTime.Today.AddDays(-num Mod 15) 
                item.Price = num * 100 + Convert.ToDouble(num) / 100 
                item.Title = [String].Format("{0} {1}", name, num) 
     
                For i As Integer = 0 To 4 
                    Dim child = New MyViewModel() 
                    child.DateCreated = DateTime.Today.AddDays(-num Mod 5 - i) 
                    child.Price = num * 100 + Convert.ToDouble(num + i) / 100 
                    child.Title = [String].Format("{0} {1}'s {2}", name, num, i) 
     
                    item.RelatedItems.Add(child) 
     
                    For j As Integer = 0 To 2 
                        Dim grandChild = New MyViewModel() 
                        grandChild.DateCreated = DateTime.Today.AddDays(-num Mod 5 - i + 2) 
                        grandChild.Price = num * 100 + Convert.ToDouble(num + i) / 100 
                        grandChild.Title = [String].Format("{0} {1} : {2}'s {3}", name, num, i, j) 
     
                        child.RelatedItems.Add(grandChild) 
                    Next 
                Next 
     
                result.Add(item) 
            Next 
            Return result 
        End Function 
    

    Now consider both of the background notes at the beginning of the topic and take a look at the following code snippet. It declares a HierarchicalDataTemplate and uses the ItemContainerStyle property of both the RadTreeView and the HierarchicalDataTemplate.

        <UserControl.Resources> 
     
            <Style TargetType="telerik:RadTreeViewItem" x:Key="redStyle"> 
                <Setter Property="Background" Value="Red" /> 
                <Setter Property="ItemContainerStyle"> 
                    <Setter.Value> 
                        <Style TargetType="telerik:RadTreeViewItem"> 
                            <Setter Property="Background" Value="Orange" /> 
                        </Style> 
                    </Setter.Value> 
                </Setter> 
            </Style> 
     
            <Style TargetType="telerik:RadTreeViewItem" x:Key="greenStyle"> 
                <Setter Property="Background" Value="Green" /> 
            </Style> 
     
        </UserControl.Resources> 
        <Grid x:Name="LayoutRoot" Background="White"> 
     
            <telerik:RadTreeView x:Name="radTreeView" Margin="8" 
                ItemContainerStyle="{StaticResource redStyle}"> 
                <telerik:RadTreeView.ItemTemplate> 
     
                    <telerik:HierarchicalDataTemplate ItemsSource="{Binding RelatedItems}" 
             ItemContainerStyle="{StaticResource greenStyle}"> 
                        <TextBlock Text="{Binding Title}" /> 
                    </telerik:HierarchicalDataTemplate> 
     
                </telerik:RadTreeView.ItemTemplate> 
            </telerik:RadTreeView> 
     
        </Grid> 
    
  • Set the ItemsSource property of the RadTreeView.

            this.radTreeView.ItemsSource = MyViewModel.GetItems( "Item" ); 
    
            Me.radTreeView.ItemsSource = MyViewModel.GetItems("Item") 
    

Here is the final result:

See Also

In this article
Not finding the help you need? Improve this article