I am trying to change the highlighted(selected) color and the highlighted text color of a wpf listbox at runtime. I have tried creating a style and applying it as follows:
To all the neigh-sayers out there... do not lose hope! it can be done!
I started off with VSS right-click on the listbox and used every "Edit Template" and "Edit Additional Templates" for each thing available until I found the how these things work.
You start off quite simply with a list box, bound to MVVM as normal.
<ListBox Width="100"
x:Name="myComboBox" Margin="8"
ItemsSource="{Binding ListBoxListSource}"
SelectedIndex="{Binding ListBox}">
</ListBox>
In UserControl or Window Resources set up a few things....
ListBoxStyle - This styles the main container of the listbox, you can set the borders, margins, padding etc of the main box here. For my example I'm just getting rid of everything to de-style it.
<UserControl.Resources>
<Style x:Key="ListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</UserControl.Resources>
ItemContainerStyle - This is the bit that people say can't be re-styled - it contains the "windows-selector-blue" bar when an item is selected, but fear not this too can be re-styled (merge this UserControl.Resources section in combination with the above one).
This section is> changing the template of the ItemContainer from whatever it is to a Border, setting a top margin of 3 to pad things out and setting up a style. All we're doing with this style is adding 3px transparent border to the left and right of the item. Then in the Triggers>IsSelected (target of myBorder), changing the border Brush to Red.
<UserControl.Resources>
<Style x:Key="ItemContainerStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="myBorder"
Padding="0" Margin="0 3 0 0"
SnapsToDevicePixels="true"
Style="{DynamicResource borderContent}">
<ContentPresenter />
</Border>
<ControlTemplate.Resources>
<Style x:Key="borderContent" TargetType="Border">
<Setter Property="BorderThickness" Value="3 0 3 0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="myBorder" Property="BorderBrush" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
ListBoxItemDataTemplate - The next step is to make the item container that displays your data. In my example the YourTextBlockStyler has a trigger on the Text>binding and changes the foreground and background colours of the text. Take note that the foreground and background of the Listbox style are set to transparent, so you have to over ride them in your TextBlock style if you want to see anything.
<UserControl.Resources>
<DataTemplate x:Key="ListBoxItemDataTemplate">
<TextBlock Text="{Binding}" Style="{StaticResource YourTextBlockStyler}"/>
</DataTemplate>
</UserControl.Resources>
Back to the listbox - Now we've set up all the styles and templates in the Resources section we can update the listbox with Style="" ItemContainerStyle="" and ItemTemplate=""
<ListBox Width="100"
x:Name="myComboBox" Margin="8"
ItemsSource="{Binding ListBoxListSource}"
SelectedIndex="{Binding ListBox}"
Style="{StaticResource ListBoxStyle}"
ItemContainerStyle="{StaticResource ItemContainerStyle}"
ItemTemplate="{StaticResource ListBoxItemDataTemplate}">
</ListBox>
Then your boring list box will transform magically in to a totally restyled list box with red border selector
From into
All without editing a single System.ResourceBrush =]
Thanks to Vinkal and failedprogramming, I got everything working beautifully. I created the following Resources:
<UserControl.Resources>
<SolidColorBrush x:Key="ListTextSelectedColor" x:Shared="False"/>
<SolidColorBrush x:Key="ListSelectedColor" x:Shared="False"/>
<Style x:Key="_ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="{DynamicResource ResourceKey=ListSelectedColor}"/>
<Setter Property="Foreground" Value="{DynamicResource ResourceKey=ListTextSelectedColor}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And then applied the style to my listbox with:
ItemContainerStyle="{DynamicResource ResourceKey=_ListBoxItemStyle}"
And finally, change the solidcolorbrush resources (therefore changing the setter values) in my C# code by doing the following:
this.Resources["ListSelectedColor"] = EmulatorPage.ListSelectedColor;
this.Resources["ListTextSelectedColor"] = EmulatorPage.ListSelectedTextColor;
Thank you to both of you!
Solution:
<Window x:Class="ListBoxStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:ListBoxStyle"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="_ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemContainerStyle="{DynamicResource _ListBoxItemStyle}"
Width="200" Height="250"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListBoxItem>Hello</ListBoxItem>
<ListBoxItem>Hi</ListBoxItem>
</ListBox>
</Grid>
</Window>