Changing The Background Of SelectedItem In WPF ListBox - wpf

This question has been asked & answered on here about a dozen times, but none of the given answers seem to work. The question: In a WPF ListBox, how do you change the highlight color (of the selected item, and/or the 'mouseover' item)?
This does not work (per here) - the highlight remains as default:
<StackPanel>
<StackPanel.Resources>
<Style x:Key="ListBoxSelectedItemsStyle" TargetType="{x:Type ListBoxItem}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Black" />
</Style.Resources>
</Style>
</StackPanel.Resources>
<ListBox ItemContainerStyle="{StaticResource ListBoxSelectedItemsStyle}">
<ListBoxItem>Item1</ListBoxItem>
<ListBoxItem>Item2</ListBoxItem>
<ListBoxItem>Item3</ListBoxItem>
</ListBox>
</StackPanel>
This also does not work (slight variation, per here):
<StackPanel>
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Yellow"/>
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem>Item1</ListBoxItem>
<ListBoxItem>Item2</ListBoxItem>
<ListBoxItem>Item3</ListBoxItem>
</ListBox>
</StackPanel>
This also does not work (per here):
<StackPanel>
<ListBox>
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Black"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Black"/>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBoxItem>Item1</ListBoxItem>
<ListBoxItem>Item2</ListBoxItem>
<ListBoxItem>Item3</ListBoxItem>
</ListBox>
</StackPanel>
Many many answers include some variation of this (i.e. here, etc) - but no matter which I use, nothing changes. The highlight remains unaffected.

All three of the versions you tried amount to basically the same thing: they provide a new resource, using the same key that is used by the default ListBoxItem control template. In theory, by providing a new resource item with the same key, when it comes time for the framework to look for the appropriate resource, it will traverse the loaded resource dictionaries, find yours first, and use that instead of the original one used by default.
But it seems that there are a couple of problems:
First, the key being used has changed since those references were provided. When I look at the current ListBoxItem style, it uses regular strings as keys, not these properties in the SystemColors class. The current key name is "Item.SelectedActive.Background" for setting the background when the item is selected and has focus.
Secondly though, even when using the new key, I was unable to get the resource item to be found and used. The original default brush is still being used, in spite of the theory being sound.
There is probably some way to still get it to work. But figuring that out would take time, stepping through the code in the debugger and watching to see what WPF is doing when the control is initialized and when the trigger is triggered. Such an investigation would probably shed light on how WPF is looking up the resource, and how one might manage to cause your own resource with the same key to be found instead.
But at the moment, I don't have time to do all that. And I do know how to make it work via brute force. One of the biggest hammers in the XAML toolbox is the "Edit Template" command in the XAML Designer. For this particular purpose, you want to create a copy of the ListBoxItem template so you can use an edited version of that. Right-click on the ListBox in Design mode in the XAML editor and go to this menu:
Choose the "Edit a Copy…" menu item.
That will add new resources to your file that look like this:
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
<Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
<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="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It will also update the ListBox in the XAML to reference that style for the item container template. At this point, you can edit the resources to your heart's content. For example, change the brush for the "Item.SelectedActive.Background" key:
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="Black"/>
Given that the WPF documentation does discuss the resource hierarchy and how resources are found by key, it does seem like it should be possible to get this to work without completely overriding the item container style like this. But I don't see a way how at the moment.
For what it's worth, the above is basically what is being proposed in Changing WPF Listbox SelectedItem text color and highlight/background Color using C#, minus all the extra details found in that question. In a sense, this question could be considered a duplicate of that.
There are a couple of other questions that you might find relevant, if this is something you run into a lot and want a less messy solution. In particular, you can use markup extensions to customize how styles are handled, allowing you to merge or otherwise manipulate the resource dictionaries as they are handled at compile time. This is a lot more complicated approach, but might be worth it if this comes up a lot in your projects. See e.g.:
XAML Combine Styles
How to apply multiple styles in WPF
Your question really is not a duplicate of either of those, but the basic technique could be applied in your situation anyway.

Related

How can I pass a parameter to a DataTrigger?

I have a list of things that I'm displaying via ItemsControl where each Item is basically a card that can be clicked. Is there a way I can pass a parameter to a DataTrigger to show whether or not a card has been clicked and if it is clicked set the Background to another color?
[...] show whether or not a card has been clicked and if it is clicked [...]
You created a view of cards and want them to be selectable. An ItemsControl is does not support selection, but there is a control called Selector that derives from ItemsControl, which is the abstract base type for items controls that need selection. Its derivatives include ListBox and ListView, which come with selection and highlighting out-of-the-box. In other words, do not re-invent the wheel, there are already more suitable controls that meet your requirements.
Types derived from Selector contain dependency properties for SelectedIndex, SelectedItem or SelectedValue, which makes it easy for you to bind them and create triggers. There is also an attached property IsSelected for item containers, which is exactly what you need to change the Background or any other property depending on the clicked or selected item.
In the following I will show you how to customize the appearance of ListBox items. You can do the same with a ListView. You can extract the default style and template for a ListBoxItem using Blend or Visual Studio.
As you can see below there are a few brushes, a focus visual and the style with control template and triggers. Adapt this style to meet your desired design. Look for the triggers that bind the IsSelected property.
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" StrokeDashArray="1 2" SnapsToDevicePixels="true" StrokeThickness="1" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ListBoxItemContainerStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
<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="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Move these resources to a resource dictionary that is in scope of the controls where you want to use them, or simply copy them to the application resources to make them globally available. In order to apply the style, you have two options.
Use the x:Key and reference them as ItemContainerStyle in each ListBox.
<ListBox ItemContainerStyle="{DynamicResource ListBoxItemContainerStyle}" .../>
Make the style implicit by removing the x:Key. Then it will be applied to all ListBoxItem in scope of the resource dictionary that contains it.
<Style TargetType="{x:Type ListBoxItem}">

In a WPF ListBox, how to "copy" the focused selection style for items over to the non-focused selection style?

First of all, this is not a duplicate of Setting the Inactive Highlight Colour of a WPF ListBox to the Active Highlight Colour. An explanation for that is given below.
Setting:
I have a WPF ListBox in a UserControl that will later be put into an application that uses heavy theming. From the perspective of the UserControl, I don't know in advance what the theming will be like.
Desired behavior:
If the ListBox does not have focus at some point, I still want the selected ListBoxItems to have the same appearance as if the ListBox does have focus.
Additional information:
Note that just setting the colors to some system defaults will not work. Doing so would override the containing application's theming. (That's the reason why this question is not a duplicate of the linked question above.)
Is there a way to realize this, e.g. using XAML?
EDIT: After a bit of research, I think I want to create a copy of the "default" ListBoxItem style ("default" at least in terms of being the default at the level of the UserControl), where all Triggers with Property="Button.IsFocused" Value="False" will not be triggered and all Triggers with Property="Button.IsFocused" Value="True" will always be triggered.
Unfortunately I have no clue where to even start to perform research in how to accomplish this. So any hints towards places where I can start researching would be much appreciated as well.
Summary
It seems like you want to achieve setting the focused style equal to the non-focused style, without editing a theme and doing it in a theme independent way. As far as I know, this isn't possible, primarily because each theme can technically implement ListBoxItem focus behavior in different ways. In fact, I've seen a theme where your desired behavior was the behavior of the ListBoxItem!
How to Modify the Theme
Now if you're open to modifying each theme to suite your needs, read ahead.
If you're modifying the theme globally, you can edit the style for the ListBoxItem directly (after finding out where it exists). If you want the changes applied more specifically, then you'll end up copying the current ListBoxItem style (from whatever theme you're editing) and making changes to it.
A copy of the default ListBoxItem theme is as follows (I used Visual Studio to make the copy). The things you need to change are going to be slightly different for each theme, but the general idea is the same.
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
<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="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The key part is in the middle:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>
This is setting up two different styles for the selected item while focused and while unfocused.
To get your desired behavior, you have one of two options; you can either simply turn it into a simple trigger just on IsSelected, replacing the above chunk with:
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</Trigger>
or you can change the Item.SelectedInactive.Background and Item.SelectedInactive.Border properties to match the active colors (this was above the ListBox style):
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FF26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
Generally the first approach is preferred, as it's more clear what's going on.
Additional Constraints
Now, the above copy of the default theme's ListBoxItem will change it for all ListBoxItems. If you want to only change some, then you need to add a key to your "copied style", like so:
<Style x:Key="InactiveLikeActive" TargetType="{x:Type ListBoxItem}">
And then at some level above where you want the style applied (perhaps even just a single ListBox itself), add the following style definition:
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource InactiveLikeActive}" />
For example:
<ListBox>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource InactiveLikeActive}" />
</ListBox.Resources>
<ListBoxItem>One</ListBoxItem>
<ListBoxItem>Two</ListBoxItem>
</ListBox>
Closing Thoughts
While WPF makes it possible to override almost all default appearances, it doesn't necessarily make it easy, or simple to do.
The shortest variant that has been achieved
<ListBox ItemsSource="{Binding ElectrEquipAll}"
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" >
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1F26A0DA"/>
<Setter Property="BorderBrush" Value="#a826A0Da"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#3D26A0DA"/>
<Setter Property="BorderBrush" Value="#FF26A0DA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

How to build a button style based on another in WPF

I'm new to WPF and by following a quick tutorial I succeeded on getting a personalized button style as the following one:
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="2" StrokeThickness="1" Stroke="#60000000" StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="NormalBrush" Color="#FF0C0C13"/>
<SolidColorBrush x:Key="LightBrush" Color="#FF2E2E3E"/>
<SolidColorBrush x:Key="PressedBrush" Color="#FF209FD4"/>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#FF494968" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#FF2E2E3E" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<SolidColorBrush x:Key="HorizontalNormalBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="HorizontalLightBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="DarkBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="NormalBorderBrush" Color="Aqua"/>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border" BorderThickness="1" Background="{StaticResource NormalBrush}" BorderBrush="#FF2E2E3E">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource LightBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It seems indeed that we need to override the ControlTemplate and that to keep some functionalities (like the behaviour while hovering the mouse) we need to define them. What is already strange to me here, is that under ControlTemplate it is defined a Border tag and then the trigger refer to that Border. So as a first question, why Border and not something else?
But now the main question is: suppose I want exactly the same button, with the same colors and functionalities but without the borders. I tried to use the BasedOn similar to the following:
<Style x:Key="MyButtonStyleNoBorder" TargetType="{x:Type Button}" BasedOn="{StaticResource MyButtonStyle}">
<Setter Property="BorderThickness" Value="0"/>
</Style>
but no way. The only solution I found is to copy the entire code of MyButtonStyle and then to change only one character (!) to have BorderThickness="0". But this to me looks stupid. Can you please help?
So as a first question, why Border and not something else?
The element named Border is the outermost element and its child will inherit most of its state, in this case the background.
But now the main question is: suppose I want exactly the same button, with the same colors and functionalities but without the borders.
If you like a button without borders you can just set the BorderThickness property of the button to 0. directly or as a setter in the style.
The only solution I found is to copy the entire code of MyButtonStyle and then to change only one character (!) to have BorderThickness="0". But this to me looks stupid. Can you please help?
Styles are kind of a list for what property to change on a target. The basedOn functionality will use the basedOn-style and "add" the new setter from the new style.
A Template is more like a drawing (and some graphical behavior) of the control and when you specify a new one you just throw away the old one. I would be possible to do a basedOn there. How would we determine what to use and what to replace?
Not the answer you wished for but hopefully you got it anyway.
<Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" Background="{StaticResource NormalBrush}" BorderBrush="#FF2E2E3E">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
Will pick up the value the Button object gets on BorderThickness.
I would have done this using MVVM (if you have not considering MVVM yet).
In my ViewModel bound to the Button, I would add a property "IsBorderLess".
Then, on the Triggers:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsBorderLess}" Value="True">
<Setter TargetName="Border" Property="BorderThickness" Value="0"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsBorderLess}" Value="False">
<Setter TargetName="Border" Property="BorderThickness" Value="10"/>
</DataTrigger>
..........

Change disabled listbox background to gray

I have a ListBox which I need to gray out when it's disabled. Per user request it's not enough to disable it, but it also has to appear differently. shrugs I've looked in several other places and followed the examples, and it seems as if it should work, but it does not. Here are some examples I looked at: Example1, Example2.
Here is my code:
<Style x:Key="ListBoxStyle" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="Foreground" Value="LightGray"></Setter>
<Setter Property="Background" Value="LightGray"></Setter>
<Setter Property="BorderBrush" Value="LightGray"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It seems fairly straightforward. I did the same basic process on ComboBox and TextBox with success. Can anyone help me see where my code is wrong? An example of how to do it correctly would be great. The first example listed above seemed to be exactly what I needed, but the correct answer was "The only way to do this is by overriding the template" which doesn't help me.
I've already tried several simple solutions. It's possible some other style may be affecting this because we're working with a couple different Resource Dictionaries. Does anybody know what may be a good way to track this down?
Edit:
I did a search on the entire solution and the only place ListBox is being used is my portion and the only place it's being styled is the styles I've set. According to MSDN there are no "parts" of a ListBox, so it's not possible I inadvertently styled part of the ListBox in the process of styling for some other control. With no styling, when I disable the ListBox, it is frozen but visible with no text, and has a default background. When I try to apply the Property="Background" Value="LightGray" it seems to be hidden (i.e. nothing is visible). If anybody knows why it may be doing this or how to accomplish my task, I'd appreciate the help.
sa_ddam213's answer didn't work for me so i thought i'd add what i found i had to do. In my case, i had set transparent background, but when i disabled the box it would turn gray. I wanted to be able to control the background of the listbox when the control was disabled and here's what i found to work.
note: For your case, you'd want to change the Transparent color to whatever shade of Gray you want.
note2: This will likely only work if you haven't changed the template for the listbox. (changing the datatemplate is ok).
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.Style>
Both answers didn't really work for me so I found a solution that overwrites the ControlTemplate which works for me:
<Style TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid Width="Auto" Height="Auto">
<Border x:Name="Border"
BorderThickness="1"/>
<ScrollViewer Focusable="false" IsTabStop="False" HorizontalScrollBarVisibility="Disabled">
<StackPanel IsItemsHost="true"/>
</ScrollViewer>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Border.Background" Value="{StaticResource DisabledBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="{StaticResource DefaultBackground}"/>
</Style>
This helped me: https://social.msdn.microsoft.com/Forums/vstudio/en-US/4b033268-864e-488c-b371-80818daf7f71/can-i-override-the-disabled-background-color-for-a-listbox?forum=wpf
I don't think you need to override the ControlTemplate, just adding a Style.Trigger worked fine for me.
Example:
<ListBox>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="LightGray" />
<Setter Property="Background" Value="LightGray" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>

WPF: Trigger for ListBoxItem.IsSelected not working for Background property

I try to change the Background property for my ListBoxItems using triggers in the ItemContainerStyle of my ListBox as follows:
<ListBox Height="100" HorizontalAlignment="Left" Margin="107,59,0,0" Name="listBox1" VerticalAlignment="Top" Width="239">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Lightblue"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" Value="Red"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Items>
<ListBoxItem Content="First Item"/>
<ListBoxItem Content="SecondItem"/>
<ListBoxItem Content="Third Item"/>
</ListBox.Items>
</ListBox>
I would expect unselected items to have a light blue background, hovered items (i.e. when the mouse cursor is over them) to be yellow and selected items to be red.
For the unselected and hovered items this is working as expected, but the selected items still have their standard background color (i.e. blue, if the listbox has focus and light gray otherwise).
Is there anything I'm missing? Is this behaviour documented somewhere?
Thanks for any hint!
EDIT
I'm aware of the solution of overriding the default system colors (as described in Change selected and unfocused Listbox style to not be grayed out, thanks anyway for everyone posting this as an answer). However this is not what I want to do. I'm more interested in why my solution doesn't work.
I'm suspecting the standard ControlTemplate of ListItem to define it's own triggers which seem to take precendence over triggers defined by the style (perhaps someone could confirm this and point me to some resource where this behaviour is defined).
My solution for the meantime is to define a ControlTemplate for my ListItems like:
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="Border" Padding="2" SnapsToDevicePixels="true" Background="LightBlue" Margin="0">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
A little bit of reflecting on the Aero-style offers us an explanation to why this simple trigger-setting doesn't work.
The ListBoxItem has a ControlTemplate with triggers that takes precedence over our trigger. At least this seems to be true for a MultiTrigger.
I´ve managed to override the simple trigger of Selected=true but for the multitrigger I had to make my own ControlTemplate.
This is the template from the Aero style that shows the problematic MultiTrigger:
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Bd" Value="{DynamicResource {x:Static HighlightBrush}}" Property="Background" />
<Setter Value="{DynamicResource {x:Static HighlightTextBrush}}" Property="Foreground" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true" />
<Condition Property="IsSelectionActive" Value="false" />
</MultiTrigger.Conditions>
<Setter TargetName="Bd" Value="{DynamicResource {x:Static ControlBrush}}" Property="Background" />
<Setter Value="{DynamicResource {x:Static ControlTextBrush}}" Property="Foreground" />
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Value="{DynamicResource {x:Static GrayTextBrush}}" Property="Foreground" />
</Trigger>
</ControlTemplate.Triggers>
<Border Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
Hope it clears things up a little bit. I can't fathom why they´ve overcomplicated the style this much.
delete IsSelected trigger
And add to listbox:
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Red" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}"
Color="Red" />
</ListBox.Resources>
First brush for focused second for otherwise
Try adding this to your window resources -
<Window.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Red" />
</Window.Resources>
And remove the IsSelected Trigger from your code, it won't work because every system has its default highlight brush depending on your system theme.
You need to override the highlight brush in your code to make it work.
Try using Selector.IsSelected in your Trigger rather than IsSelected.

Resources