wpftreeviewitem

WPF TreeViewItem style MVVM


I need to change BorderBrush of a TreeViewItem based on a model property. This are my codes:

using System.Windows;

namespace Test_Project
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new Model
            {
                Children = new Model[]
                {
                    new Model
                    {
                        Children = new Model[]
                        {
                            new Model {},
                            new Model
                            {
                                Name = "Special",
                                Children = new Model[]
                                {
                                    new Model {},
                                    new Model {},
                                },
                            },
                        },
                    },
                },
            };
        }
    }

    public class Model
    {
        public string Name { get; set; } = "Regular";
        public Model[] Children { get; set; } = new Model[0];
    }
}
<Window x:Class="Test_Project.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen">
    
    <TreeView ItemsSource="{Binding Children}">
        
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <Border x:Name="part_border" BorderBrush="Transparent" BorderThickness="2">
                    <TextBlock Text="{Binding Name}"/>
                </Border>
                <HierarchicalDataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Name}" Value="Special">
                        <Setter TargetName="part_border" Property="BorderBrush" Value="green"/>
                    </DataTrigger>
                </HierarchicalDataTemplate.Triggers>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
        
    </TreeView>
    
</Window>

I need to change BorderBrush of the model with name "Special" to be colored. This is the result with my try:

enter image description here

But I need to change the BorderBrush of the ListViewItem (Container) not content, So I modified ListViewItem style, this the result (red lines):

enter image description here

<Window x:Class="Test_Project.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen">
    
    <TreeView ItemsSource="{Binding Children}">

        <TreeView.Resources>
            
            <!--TreeViewItem-->

            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
            <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
            <PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
            <Style x:Key="ExpandCollapseToggleStyle" 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,5,5,5" Width="16">
                                <Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
                                    <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="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
                                    <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
                                    <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
                                </Trigger>
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsMouseOver" Value="True"/>
                                        <Condition Property="IsChecked" Value="True"/>
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
                                    <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
                                </MultiTrigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style x:Key="TreeViewItemFocusVisual">
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Rectangle/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                <Setter Property="Padding" Value="1,0,0,0"/>
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TreeViewItem}">
                            <Border x:Name="part_border" BorderThickness="2" BorderBrush="red" Margin="4">
                                <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 Mode=TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
                                    <Border Grid.ColumnSpan="2" x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                                        <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                    </Border>
                                    <ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1"/>
                                </Grid>
                            </Border>
                            <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="IsEnabled" Value="false">
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
                        <Setter Property="ItemsPanel">
                            <Setter.Value>
                                <ItemsPanelTemplate>
                                    <VirtualizingStackPanel/>
                                </ItemsPanelTemplate>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TreeView.Resources>
        
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <Border x:Name="part_border" BorderBrush="Transparent" BorderThickness="2">
                    <TextBlock Text="{Binding Name}"/>
                </Border>
                <HierarchicalDataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Name}" Value="Special">
                        <Setter TargetName="part_border" Property="BorderBrush" Value="green"/>
                    </DataTrigger>
                </HierarchicalDataTemplate.Triggers>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
        
    </TreeView>
    
</Window>

I need this BorderBrush apply to the item if it's name is "Special" and transparent for the rest, But it seems TreeViewItem style dosn't know anything about the "Name" property. Anyone knows how we can achieve this? This what I want:

enter image description here


Solution

  • If you want to put a Border around the entire ItemsPresenter, you should define a custom template that is based on the default one:

    <TreeView ItemsSource="{Binding Children}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <Border x:Name="part_border" BorderBrush="Transparent" BorderThickness="2">
                    <TextBlock Text="{Binding Name}"/>
                </Border>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
        <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Name}" Value="Special">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type TreeViewItem}">
                                    <Border Name="Bd"
                                                 Grid.Column="1"
                                                 Background="{TemplateBinding Background}"
                                                 BorderBrush="Red"
                                                 BorderThickness="2"
                                                 Padding="{TemplateBinding Padding}"
                                                 SnapsToDevicePixels="true">
                                        <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"
                                                           IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource TemplatedParent}}"
                                                           ClickMode="Press">
                                                <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}">
                                                                    <ControlTemplate.Resources>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
                                                                        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
                                                                        <PathGeometry x:Key="TreeArrow">
                                                                            <PathGeometry.Figures>
                                                                                <PathFigureCollection>
                                                                                    <PathFigure IsFilled="True" StartPoint="0 0" IsClosed="True">
                                                                                        <PathFigure.Segments>
                                                                                            <PathSegmentCollection>
                                                                                                <LineSegment Point="0 6"/>
                                                                                                <LineSegment Point="6 0"/>
                                                                                            </PathSegmentCollection>
                                                                                        </PathFigure.Segments>
                                                                                    </PathFigure>
                                                                                </PathFigureCollection>
                                                                            </PathGeometry.Figures>
                                                                        </PathGeometry>
                                                                    </ControlTemplate.Resources>
                                                                    <Border Width="16"
                                                                             Height="16"
                                                                             Background="Transparent"
                                                                             Padding="5,5,5,5">
                                                                        <Path x:Name="ExpandPath"
                                                                               Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}"
                                                                               Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}"
                                                                               Data="{StaticResource TreeArrow}">
                                                                            <Path.RenderTransform>
                                                                                <RotateTransform Angle="135"
                                                                                                  CenterX="3"
                                                                                                  CenterY="3"/>
                                                                            </Path.RenderTransform>
                                                                        </Path>
                                                                    </Border>
                                                                    <ControlTemplate.Triggers>
                                                                        <Trigger Property="IsChecked" Value="True">
                                                                            <Setter TargetName="ExpandPath" Property="RenderTransform">
                                                                                <Setter.Value>
                                                                                    <RotateTransform Angle="180"
                                                                                                      CenterX="3"
                                                                                                      CenterY="3"/>
                                                                                </Setter.Value>
                                                                            </Setter>
                                                                            <Setter TargetName="ExpandPath"
                                                                                     Property="Fill"
                                                                                     Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
                                                                            <Setter TargetName="ExpandPath"
                                                                                     Property="Stroke"
                                                                                     Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
                                                                        </Trigger>
                                                                        <Trigger Property="IsMouseOver" Value="True">
                                                                            <Setter TargetName="ExpandPath"
                                                                                     Property="Stroke"
                                                                                     Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
                                                                            <Setter TargetName="ExpandPath"
                                                                                     Property="Fill"
                                                                                     Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
                                                                        </Trigger>
                                                                        <MultiTrigger>
                                                                            <MultiTrigger.Conditions>
                                                                                <Condition Property="IsMouseOver" Value="True"/>
                                                                                <Condition Property="IsChecked" Value="True"/>
                                                                            </MultiTrigger.Conditions>
                                                                            <Setter TargetName="ExpandPath"
                                                                                     Property="Stroke"
                                                                                     Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
                                                                            <Setter TargetName="ExpandPath"
                                                                                     Property="Fill"
                                                                                     Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
                                                                        </MultiTrigger>
                                                                    </ControlTemplate.Triggers>
                                                                </ControlTemplate>
                                                            </Setter.Value>
                                                        </Setter>
                                                    </Style>
                                                </ToggleButton.Style>
                                            </ToggleButton>
    
                                            <ContentPresenter x:Name="PART_Header"
                                                               Grid.Column="1"
                                                               ContentSource="Header"
                                                               HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                               SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
    
                                            <ItemsPresenter x:Name="ItemsHost"
                                                             Grid.Row="1"
                                                             Grid.Column="1"
                                                             Grid.ColumnSpan="2"/>
                                        </Grid>
                                    </Border>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsExpanded" Value="false">
                                            <Setter TargetName="ItemsHost"
                                                     Property="Visibility"
                                                     Value="Collapsed"/>
                                        </Trigger>
                                        <Trigger Property="HasItems" Value="false">
                                            <Setter TargetName="Expander"
                                                     Property="Visibility"
                                                     Value="Hidden"/>
                                        </Trigger>
                                        <Trigger Property="IsSelected" Value="true">
                                            <Setter TargetName="Bd"
                                                     Property="Background"
                                                     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 TargetName="Bd"
                                                     Property="Background"
                                                     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>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>
    

    enter image description here