Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF ListBox Highlight in Windows 10

I'm fairly new to WPF but experienced in .NET (Winforms). I'm trying to manipulate the highlight style of a listbox to control the focused and unfocused color of the selected item. Every single tutorial on this that I have found uses a custom style to assign a new value to the SystemColors.HighlightBrushKey and SystemColors.ControlBrushKey. But it isn't working. After countless hours trying to get this to work, it occurred to me that maybe it was OS related. I had been trying it on a Windows 10 system. I ran the exact same code on a Windows 7 setup, and lo and behold, it worked!

So apparently the old method doesn't work in Windows 10 (at least that's what it looks like to me). Has anybody found an alternative? At the end of the day, I just want the listbox to maintain the bright highlight even when it doesn't have focus. The default grey highlight is difficult to see, and doesn't seem appropriate in some usages. I have a real world scenario where it feels very unnatural for the highlight to basically disappear when the focus moves away from the ListBox.

Below is the XAML code I used that worked on Windows 7 but not on Windows 10. (By the way, I have also tried replacing SystemColors.ControlBrushKey with SystemColors.InactiveSelectionHighlightBrushKey -- the results were the same).

    <Window x:Class="TestApp.TestWindow"
        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"
        xmlns:local="clr-namespace:TestApp"
        mc:Ignorable="d"
        Title="TestWindow" Height="300" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <Style x:Key="myListboxStyle">
            <Style.Resources>
                <!-- Background of selected item when focused -->
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
                <!-- Background of selected item when not focused -->
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Green" />
            </Style.Resources>
        </Style>
    </Window.Resources>
    <Grid>
        <ListBox x:Name="listBox" Style="{StaticResource myListboxStyle}" HorizontalAlignment="Left" Height="100" Margin="22,18,0,0" VerticalAlignment="Top" Width="237">
            <ListBoxItem>Test 1</ListBoxItem>
            <ListBoxItem>Test 2</ListBoxItem>
            <ListBoxItem>Test 3</ListBoxItem>
        </ListBox>
            <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="50,165,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>

    </Grid>
</Window>
like image 590
Cosmicjive Avatar asked Mar 04 '16 19:03

Cosmicjive


1 Answers

Microsoft broke this for windows 10, but we can fix it!

Here is what the template looks like in windows 10 (just the parts i care about):

   <ControlTemplate TargetType="{x:Type ListBoxItem}">
 ...
        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
            <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" 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="#1F26A0DA"/>
                <Setter Property="BorderBrush" TargetName="Bd" Value="#A826A0DA"/>
            </MultiTrigger>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="Selector.IsSelectionActive" Value="False"/>
                    <Condition Property="IsSelected" Value="True"/>
                </MultiTrigger.Conditions>
                <Setter Property="Background" TargetName="Bd" Value="#3DDADADA"/>
                <Setter Property="BorderBrush" TargetName="Bd" Value="#FFDADADA"/>
            </MultiTrigger>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="Selector.IsSelectionActive" Value="True"/>
                    <Condition Property="IsSelected" Value="True"/>
                </MultiTrigger.Conditions>
                <Setter Property="Background" TargetName="Bd" Value="#3D26A0DA"/>
                <Setter Property="BorderBrush" TargetName="Bd" Value="#FF26A0DA"/>
            </MultiTrigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

Notice they have hard-coded the values for the colors, like "#1F26A0DA".

In windows 7, the built-in template for ListBoxItems was:

         <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>
                        <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="Selector.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>

So basically Microsoft was using resources like "SystemColors.InactiveSelectionHighlightBrushKey" in windows 7. But now they made it so we can't do this without overriding the template; since they hard-coded in all the values.

So to patch this, simply override the Template for ListBoxItem in you App.Xaml file; so that everything gets the patch.

 <Style TargetType="{x:Type ListBoxItem}">
        <Style.Resources> <!-- Use your own colors here if you want, or do it per class -->    
            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#FFFFA500"/> 
            <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey }" Color="#FFFFA500"/>
        </Style.Resources>

      <Setter Property="Template">
<!-- Revert the Template in Windows 10 to match the Windows 7 template that used "SystemColors.HighlightBrushKey" and such-->
                <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>
                        <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="Selector.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>
like image 128
00jt Avatar answered Sep 20 '22 13:09

00jt