Search code examples
wpftreeviewdatatemplatecontroltemplate

WPF TreeView: Using ItemTemplateSelector and ItemContainerStyle Not Working At Same Time


I have a WPF TreeView for which I have specified two HierarchicalDataTemplates, and I use a template selector to choose which one to apply based on a type in a data bound object. The hierarchical data templates get applied and I see the expected names of items in the TreeView.

However, when I try and set the ItemContainerStyle for tree view items then it looks like the hierarchical data templates are no longer applied: the label for each tree view item is set to the class name of the object that the item is bound to. The style I am applying sets Template to the TreeViewItem ControlTemplate (I have plans to modify this template slightly but for now I am using the unmodified one which I exported from Visual Studio).

Data template selector

public class NodeTypeTemplateSelector : DataTemplateSelector
{
    public DataTemplate Type1Template { get; set; }

    public DataTemplate Type2Template { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item == null) return base.SelectTemplate(null, container);

        var layer = item as LayersItemVM;
        if (layer != null)
        {
            switch (layer.NodeType)
            {
                case NodeType.Type1:
                    return Type1Template;
                case NodeType.Type2:
                    return Type2Template;
                default:
                    return base.SelectTemplate(item, container);
            }
        }
        return base.SelectTemplate(item, container);
    }
}

Main Window XAML

<Window x:Class="TreeViewSimple2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TreeViewSimple2"
    mc:Ignorable="d"
    Loaded="MainWindow_OnLoaded"
    Title="MainWindow" Height="350" Width="525">

<Window.Resources>
  <HierarchicalDataTemplate x:Key="Type1Template">
    <StackPanel>
      <Label Content="{Binding Path=Name}"></Label>
    </StackPanel>
  </HierarchicalDataTemplate>

  <HierarchicalDataTemplate x:Key="Type2Template">
    <StackPanel>
      <Label Content="{Binding Path=Name}"></Label>
    </StackPanel>
  </HierarchicalDataTemplate>

  <local:NodeTypeTemplateSelector Type1Template="{StaticResource Type1Template}" Type2Template="{StaticResource Type2Template}" x:Key="NodeTypeTemplateSelector" />
</Window.Resources>

<Grid>
    <TreeView x:Name="TreeView" ItemTemplateSelector="{StaticResource NodeTypeTemplateSelector}">
      <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
          <Setter Property="Template">
            <Setter.Value>
              <ControlTemplate TargetType="{x:Type TreeViewItem}">
                <Grid>
                  <Grid.ColumnDefinitions>
                    <ColumnDefinition MinWidth="19" Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                  </Grid.ColumnDefinitions>
                  <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition/>
                  </Grid.RowDefinitions>
                  <ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}">
                    <ToggleButton.Style>
                      <Style TargetType="{x:Type ToggleButton}">
                        <Setter Property="Focusable" Value="False"/>
                        <Setter Property="Width" Value="16"/>
                        <Setter Property="Height" Value="16"/>
                        <Setter Property="Template">
                          <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ToggleButton}">
                              <Border Background="Transparent" Height="16" Padding="5" Width="16">
                                <Path x:Name="ExpandPath" Data="M0,0 L0,6 L6,0 z" Fill="Transparent" Stroke="#FF989898">
                                  <Path.RenderTransform>
                                    <RotateTransform Angle="135" CenterY="3" CenterX="3"/>
                                  </Path.RenderTransform>
                                </Path>
                              </Border>
                              <ControlTemplate.Triggers>
                                <Trigger Property="IsChecked" Value="True">
                                  <Setter Property="RenderTransform" TargetName="ExpandPath">
                                    <Setter.Value>
                                      <RotateTransform Angle="180" CenterY="3" CenterX="3"/>
                                    </Setter.Value>
                                  </Setter>
                                  <Setter Property="Fill" TargetName="ExpandPath" Value="#FF595959"/>
                                  <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="True">
                                  <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/>
                                  <Setter Property="Fill" TargetName="ExpandPath" Value="Transparent"/>
                                </Trigger>
                                <MultiTrigger>
                                  <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                    <Condition Property="IsChecked" Value="True"/>
                                  </MultiTrigger.Conditions>
                                  <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
                                  <Setter Property="Fill" TargetName="ExpandPath" Value="#FF595959"/>
                                </MultiTrigger>
                              </ControlTemplate.Triggers>
                            </ControlTemplate>
                          </Setter.Value>
                        </Setter>
                      </Style>
                    </ToggleButton.Style>
                  </ToggleButton>
                  <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
                    <ContentPresenter x:Name="PART_Header" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                  </Border>
                  <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
                </Grid>
                <ControlTemplate.Triggers>
                  <Trigger Property="IsExpanded" Value="False">
                    <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
                  </Trigger>
                  <Trigger Property="HasItems" Value="False">
                    <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
                  </Trigger>
                  <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                  </Trigger>
                  <MultiTrigger>
                    <MultiTrigger.Conditions>
                      <Condition Property="IsSelected" Value="True"/>
                      <Condition Property="IsSelectionActive" Value="False"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"/>
                  </MultiTrigger>
                  <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                  </Trigger>
                </ControlTemplate.Triggers>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </TreeView.ItemContainerStyle>
    </TreeView>
</Grid>


Solution

  • Try this template:

    <TreeView x:Name="TreeView" ItemTemplateSelector="{StaticResource NodeTypeTemplateSelector}">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TreeViewItem}">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition MinWidth="19" Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <ToggleButton x:Name="Expander" ClickMode="Press"
                                                      IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}">
                                    <ToggleButton.Style>
                                        <Style TargetType="{x:Type ToggleButton}">
                                            <Setter Property="Focusable" Value="False"/>
                                            <Setter Property="Width" Value="16"/>
                                            <Setter Property="Height" Value="16"/>
                                            <Setter Property="Template">
                                                <Setter.Value>
                                                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                                                        <Border Background="Transparent" Height="16" Padding="5" Width="16">
                                                            <Path x:Name="ExpandPath" Data="M0,0 L0,6 L6,0 z" Fill="Transparent" Stroke="#FF989898">
                                                                <Path.RenderTransform>
                                                                    <RotateTransform Angle="135" CenterY="3" CenterX="3"/>
                                                                </Path.RenderTransform>
                                                            </Path>
                                                        </Border>
                                                        <ControlTemplate.Triggers>
                                                            <Trigger Property="IsChecked" Value="True">
                                                                <Setter Property="RenderTransform" TargetName="ExpandPath">
                                                                    <Setter.Value>
                                                                        <RotateTransform Angle="180" CenterY="3" CenterX="3"/>
                                                                    </Setter.Value>
                                                                </Setter>
                                                                <Setter Property="Fill" TargetName="ExpandPath" Value="#FF595959"/>
                                                                <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
                                                            </Trigger>
                                                            <Trigger Property="IsMouseOver" Value="True">
                                                                <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/>
                                                                <Setter Property="Fill" TargetName="ExpandPath" Value="Transparent"/>
                                                            </Trigger>
                                                            <MultiTrigger>
                                                                <MultiTrigger.Conditions>
                                                                    <Condition Property="IsMouseOver" Value="True"/>
                                                                    <Condition Property="IsChecked" Value="True"/>
                                                                </MultiTrigger.Conditions>
                                                                <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
                                                                <Setter Property="Fill" TargetName="ExpandPath" Value="#FF595959"/>
                                                            </MultiTrigger>
                                                        </ControlTemplate.Triggers>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </ToggleButton.Style>
                                </ToggleButton>
                                <Border Name="Bd" Grid.Column="1" Background="{TemplateBinding Control.Background}"
                                            BorderBrush="{TemplateBinding Control.BorderBrush}"
                                            BorderThickness="{TemplateBinding Control.BorderThickness}"
                                            Padding="{TemplateBinding Control.Padding}" SnapsToDevicePixels="true">
                                    <ContentPresenter x:Name="PART_Header" ContentSource="Header"
                                                HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
                                                SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/>
                                </Border>
                                <ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/>
                            </Grid>
                            <ControlTemplate.Triggers>
                                <Trigger Property="TreeViewItem.IsExpanded" Value="false">
                                    <Setter TargetName="ItemsHost" Property="UIElement.Visibility" Value="Collapsed"/>
                                </Trigger>
                                <Trigger Property="ItemsControl.HasItems" Value="false">
                                    <Setter TargetName="Expander" Property="UIElement.Visibility" Value="Hidden"/>
                                </Trigger>
                                <Trigger Property="TreeViewItem.IsSelected" Value="true">
                                    <Setter TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"
                                                    Property="Border.Background"/>
                                    <Setter Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"
                                                    Property="Control.Foreground"/>
                                </Trigger>
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="TreeViewItem.IsSelected" Value="true"/>
                                        <Condition Property="TreeViewItem.IsSelectionActive" Value="false"/>
                                    </MultiTrigger.Conditions>
                                    <Setter TargetName="Bd"
                                                    Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"
                                                    Property="Border.Background"/>
                                    <Setter Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"
                                                    Property="Control.Foreground"/>
                                </MultiTrigger>
                                <Trigger Property="UIElement.IsEnabled" Value="false">
                                    <Setter Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" Property="Control.Foreground"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
                        <Setter Property="ItemsControl.ItemsPanel">
                            <Setter.Value>
                                <ItemsPanelTemplate>
                                    <VirtualizingStackPanel/>
                                </ItemsPanelTemplate>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>