Search code examples
wpftreeviewcontroltemplatehierarchicaldatatemplate

One template for multiple similar HierarchicalDataTemplate-s?


I have a TreeView component, and it can contain several types of nodes. Its all working well except I have multiple hierarchical data templates which differ just in the glyph image they display, DataType and ItemsSource but everything else is exactly the same.

I was wondering, is it possible to create just one ControlTemplate for all hierarchical data template and just modify the icon image by using the tag property?

So for example I have basic TreeNode class and nodes like, PersonNode, PropertyNode, StreetNode which inherit from TreeNode. TreeNode displays a folder icon, PersonNode displays a user icon, PropertyNode displays house icon and StreeNode displays street icon. So, actually they all have same content structure except, the icon, DataType and ItemsSource are changing.

How can I simplify my hierarchical data templates?

Sample code:

 <HierarchicalDataTemplate
    DataType="{x:Type MyTreeFolder:TreeNode}"
    ItemsSource="{Binding Path=Items}">
    
    <StackPanel Orientation="Horizontal">
        <StackPanel.Style>
            <Style TargetType="StackPanel">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=IsSpecialNode}" Value="True">
                        <Setter Property="Background" Value="Blue"></Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </StackPanel.Style>

        <glyphs:GlyphAwesome 
            FontSize="12"
            Glyph="folder"
            Margin="0, 0, 4, 0"
            VerticalAlignment="Center"/>

        <TextBlock Text="{Binding}" VerticalAlignment="Center"/>
                
    </StackPanel>
    
</HierarchicalDataTemplate>

Solution

  • You could create a value converter that checks the type of the tree node and returns the glyph text. The type to glyph text mapping could be stored in a dictionary.

    public class ObjectTypeToGlyphConverter : IValueConverter
    {
       private static readonly IDictionary<Type, string> TypeToGlyphMapping = new Dictionary<Type, string>
       {
          [typeof(TreeNode)] = "folder",
          [typeof(PersonNode)] = "person",
          [typeof(PropertyNode)] = "property"
       };
    
       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
       {
          var type = value?.GetType();
          return type != null && TypeToGlyphMapping.TryGetValue(type, out var glyph) ? glyph : null;
       }
    
       public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
       {
          throw new InvalidOperationException();
       }
    }
    

    You would need to create an instance of this converter in a resource dictionary in scope, e.g.:

    <TreeView ItemsSource="{Binding TreeNodes}">
       <TreeView.Resources>
          <local:ObjectTypeToGlyphConverter x:Key="ObjectTypeToGlyphConverter"/>
       </TreeView.Resources>
       <!-- ...tree view definitions. -->
    </TreeView>
    

    Then you could use it in your hierarchical data template's GlyphAwesome like this.

    <glyphs:GlyphAwesome FontSize="12"
                         Glyph="{Binding Converter={StaticResource ObjectTypeToGlyphConverter}}"
                         Margin="0, 0, 4, 0"
                         VerticalAlignment="Center"/>
    

    In this case you would only need one data template for the type TreeNode, given that as you say all node types are essentially the same and there is no special handling for them except for the icon.