I have this user control xaml which is currently working:
<UserControl ...>
<TreeView Background="Yellow" ... >
<TreeView.Resources >
<HierarchicalDataTemplate ... >
<Grid Name="Header" Background="Green" ... >
</Grid>
</HierarchicalDataTemplate>
<DataTemplate ... >
<Grid Name="MultipleItems">
</Grid>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</UserControl>
I have to change the background color of the TreeView's toggle button only similar to the Grid header's which is Green. The default background color of the toggle button is similar to the TreeView's Yellow.
When I'm trying to add these codes after TreeView tag and before TreeView.Resources tag, I am encountering an error :
<TreeViewItem>
<TreeViewItem.Template>
<ControlTemplate TargetType="TreeViewItem">
<Grid>
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Background" Value="Green"/>
</Style>
</ToggleButton.Style>
</ToggleButton>
</Grid>
</ControlTemplate>
</TreeViewItem.Template>
</TreeViewItem>
The error is "Items collection must be empty before using ItemsSource." . How can I change the background color of the toggle button successfully? Is my fix above is not the correct way? Please let me know if there is another way to do this and how.
Additional Info : Even adding this simple line the error occurs.
<TreeViewItem></TreeViewItem>
In order to change the appearance or visual states of a control, you have to change its style or control template. These are far more complex than you might expect. A control template like the one you provided lacks most of the states and required parts (controls within the template) to work properly. You can have a look at the required parts and states in the documentation.
Instead of creating control templates and styles for controls from scratch, you can extract the default ones using Blend or Visual Studio and adapt them to your needs. In most cases you will copy lots of code and the changes that are required to match your requirements are small, but there is no other way, as you cannot base control templates on other control templates as you can do with styles.
The error is "Items collection must be empty before using ItemsSource."
You get this error because you set a TreeViewItem
in the TreeView
. It will add this item as content, but since you probably already set an ItemsSource
somewhere else it throws an error, because you cannot do both. Instead, you would set a custom style via the ItemContainerStyle
property, e.g. inline like this:
<TreeView ItemsSource="{Binding MyTreeViewNodes}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<!-- ...your control template -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<!-- ...other tree view definitions. -->
</TreeView>
Of course you could also reference a named style using StaticResource
or DynamicResource
. I hope you get the idea, but either way, your control template is incomplete and will not work properly.
I have extracted the default style including the control template for TreeViewItem
and adapted it. You have to copy all of these styles to a resource dictionary in scope or the application resources.
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Background" Color="Green"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Background" Color="Green"/>
<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.Background" Color="Green"/>
<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.Background" Color="Green"/>
<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 x:Name="ExpandBorder" Background="{StaticResource TreeViewItem.TreeArrow.Static.Background}" 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}"/>
<Setter Property="Background" TargetName="ExpandBorder" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Background}"/>
</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}"/>
<Setter Property="Background" TargetName="ExpandBorder" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Background}"/>
</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}"/>
<Setter Property="Background" TargetName="ExpandBorder" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Background}"/>
</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 x:Key="TreeViewItemContainerStyle" 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}">
<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}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" 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.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.Triggers>
<Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
As you can see, this is pretty complex. There is a style for the TreeViewItem
, a focus style, as well as a style for the ToggleButton
in the item itself. I only adapted the Border
named ExpandBorder
and assigned it an initial background and backgrounds depending on the item state in the triggers.
If you want to adapt the colors in the different states, you only have to change these brushes:
TreeViewItem.TreeArrow.Static.Background
TreeViewItem.TreeArrow.MouseOver.Background
TreeViewItem.TreeArrow.Static.Checked.Background
TreeViewItem.TreeArrow.MouseOver.Checked.Background
To apply the new TreeViewItem
style, reference it as ItemContainerStyle
.
<TreeView ItemsSource="{Binding MyTreeViewNodes}" ItemContainerStyle="{StaticResource TreeViewItemContainerStyle}">
You could alternatively apply the style to all TreeViewItem
s in scope if you omit the key for the corresponding style, which makes it an implicit style.
<Style TargetType="{x:Type TreeViewItem}">