J23
J23

Reputation: 3241

Changing The Background Of SelectedItem In WPF ListBox

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.

Upvotes: 3

Views: 1132

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70701

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:

enter image description here

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.

Upvotes: 1

Related Questions