WPF ListView 非活动选择颜色

67

我正在创建一个WPF应用程序,其中需要连续进行多个ListView选择(类似于iTunes浏览器)。问题在于默认的非活动选择颜色太浅了(如下图所示)。 Default inactive selection color (too light)

我该如何更改此颜色,使我的非活动列表视图看起来像这样?(如下图所示) Inactive and active selection colors the same

解决方案

使用Style覆盖默认的SystemColor,代码如下:

<Style TargetType="ListViewItem">
    <Style.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
    </Style.Resources>
</Style>
8个回答

65

更改 SystemColors.ControlBrushKey 对我无效,我必须更改 SystemColors.InactiveSelectionHighlightBrushKey

所以要改为:

<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Red" />

我不得不使用:

<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Red"/>

我也不得不更改这个值,ControlBrushKey没有改变颜色。然而,我输入的颜色仍然被另一种颜色覆盖,所以我的蓝色看起来更紫色。你有遇到过这种情况吗? - Caleb S
2
我希望我能给这个答案点赞超过一次,因为我已经尝试过多次解决这个问题。我也在使用.NET 4.5,并尝试了所有其他的答案,但都没有成功。 - Zev Spitz
这是我在开始处理.NET 4项目之前使用的... 我不得不实现类似于.NET 4解决方案答案的样式,以便更轻松地处理数据(轻松知道选择了什么)。 - B.K.
2
在 .NET 4.5 FrameworkCompatibilityPreferences 中,AreInactiveSelectionHighlightBrushKeysSupported 字段控制用于高亮显示的颜色。 - John Melville
SystemColors.ControlBrushKey 对我有用,谢谢。 - SkyDancer
WPF 在 net6 上,必须使用 SystemColors.InactiveSelectionHighlightBrushKeySystemColors.InactiveSelectionHighlightTextBrushKey - Viktor

60

ListBox模板使用一个叫做ControlBrush的系统颜色来设置非活动状态下的高亮颜色。因此,你可以直接覆盖该颜色:

<ListBox>
    <ListBox.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}">Red</SolidColorBrush>
    </ListBox.Resources>
</ListBox>

4
用这种方式存在问题。它不允许你自定义应用程序的皮肤,如果你更改Windows主题以更改系统颜色,它将恢复为默认设置。 - Micah
1
实际上发生的是您正在覆盖使用它的所有对象的样式键。 - Micah
1
这是一个资源,因此受到资源查找的正常规则的限制。Micah,请自行尝试。在窗口中放置两个ListBox - 一个带有资源覆盖,另一个没有。颜色更改仅影响具有资源覆盖的那个。 - Kent Boogaart
6
对于.NET 4.5版本,看起来他们更改了密钥 -- 参见下面@user672951的答案。 - Ed Bayiates
这是一个糟糕的想法 - 不要这样做 - 对于关闭视觉效果的用户,这将搞乱很多东西 - 滚动条和按钮将更改为当前的高亮颜色(默认情况下为蓝色 - 因此您的内部按钮和ListView滚动条变为蓝色,非常糟糕!)[要了解我的意思,请在“Windows Classic”主题上尝试此黑客,因为它已禁用所有视觉效果。]请参见此处的流行答案以正确执行此操作:https://dev59.com/VnM_5IYBdhLWcg3wPAnU - BrainSlugs83
显示剩余5条评论

20

这个答案在某些情况下可以解决问题,但并不是最理想的,因为当控件被禁用/只读时会出现问题,而且它还会覆盖颜色方案,而不是利用它们。我的建议是在ListBox标签中添加以下内容:

<ListBox....>
    <ListBox.Resources>
            <Style 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 {x:Static SystemColors.HighlightBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
        </Style>
    </ListBox.Resources>
</ListBox>
这将在列表框项被选择时(无论控件状态如何)设置高亮背景颜色。我的答案基于已有答案的帮助,以及以下博客:http://blogs.vbcity.com/xtab/archive/2009/06/29/9344.aspx

这样做可以实现OP所要求的功能,但请注意,这种风格会使所有文本都变成黑色(在我的系统上,突出显示的文本通常是白色,因为黑色在深蓝色背景上太难读了)。 - Roman Starkov
@romkyns 只需创建一个常规触发器,当 IsSelected 为 true 时将前景设置为“HighlightTextBrushKey”。我会提交对答案的编辑,但它可以解决你提到的问题。这是实现 OP 想要的正确方法(字典方法会因用户的主题而导致一些不必要的视觉副作用)... - BrainSlugs83
非常有用...我更喜欢这种方法,因为颜色不会覆盖其他颜色,并且在控件禁用时也可以使用,这正是我的用例。谢谢! - Mark W
这适用于 .Net 4.6 上的 WPF。您无法通过 IsSelected 触发器直接覆盖 ListBoxItemBackground 来创建样式(这适用于许多属性,但不适用于背景)。我猜这是不同触发器执行顺序的问题。您真的必须覆盖控件模板。 - Patrick Stalph

13
你需要覆盖一些SystemColors属性。请查看SystemColors Class (MSDN)。除了InactiveSelectionHighlightBrushKey之外,还有更多的属性,例如影响文本颜色的InactiveSelectionHighlightTextBrushKey。
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White"/>
        <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Yellow"/>
        <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="Blue"/>
        <Style TargetType="ListViewItem">
            <Setter Property="FontSize" Value="20" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Padding" Value="25,5" />
        </Style>
    </Window.Resources>
    <StackPanel Orientation="Horizontal">
        <ListView>
            <ListViewItem Content="Item" />
            <ListViewItem Content="Item" />
        </ListView>
        <ListView>
            <ListViewItem Content="Item" />
            <ListViewItem Content="Item" />
        </ListView>
    </StackPanel>
</Window>

enter image description here


2
最初的回答:根据这个答案,我使用以下代码来使活动状态和非活动状态下颜色相同,而不是硬编码实际值:
基于这个其他答案,我使用了以下代码,可以使活动和非活动颜色相同,而不是硬编码实际值:
<ListBox.Resources>
    <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" 
                     Color="{x:Static SystemColors.HighlightColor}"/>
    <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}"
                     Color="{x:Static SystemColors.HighlightTextColor}"/>
</ListBox.Resources>

将活动和非活动的颜色设为相同可能不是最理想的选择,但默认的颜色过于淡,很难区分未选中的项目。这样做肯定是一个改进。

需要翻译的内容: Original Answer翻译成"最初的回答"


2

在旧版的.NET Framework中覆盖系统颜色不起作用。在.NET Framework 4.0中可行的解决方案在这里

<ListView>
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListViewItem}">
                <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="Selector.IsSelectionActive"
                                        Value="False" />
                            <Condition Property="IsSelected"
                                        Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Background"
                                TargetName="Bd"
                                Value="DarkOrange" />
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="Selector.IsSelectionActive"
                                        Value="True" />
                            <Condition Property="IsSelected"
                                        Value="True" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Background"
                                TargetName="Bd"
                                Value="OrangeRed" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

适用于ListBox和ListView。


虽然这可能是解决问题的有价值提示,但一个好的答案也应该展示解决方案。请编辑提供示例代码以展示您的意思。或者,考虑将其编写为评论。 - ρяσѕρєя K

1

大多数其他答案建议覆盖SystemColors,但这对我无效。我最终仅编辑了ListBoxItem的默认样式,并在特定的ListBox上设置ItemContainerStyle为编辑后的样式。与编辑其他控件的默认样式相比,这相对容易。您只需要将Item.SelectedInactive.Background和Item.SelectedInactive.Border更改为所需的颜色即可。

   <SolidColorBrush x:Key="Item.Static.Background" Color="#FFFCFCFC" />
    <SolidColorBrush x:Key="Item.Static.Border" Color="#FFFCFCFC" />
    <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="ModifiedColorListBox" 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}" 
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </Border>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsMouseOver" Value="True" />
                            </MultiTrigger.Conditions>
                            <Setter TargetName="Bd" Property="Background" Value="{StaticResource Item.MouseOver.Background}" />
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource Item.MouseOver.Border}" />
                        </MultiTrigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="Selector.IsSelectionActive" Value="False" />
                                <Condition Property="IsSelected" Value="True" />
                            </MultiTrigger.Conditions>
                            <Setter TargetName="Bd" Property="Background" Value="{StaticResource Item.SelectedInactive.Background}" />
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource Item.SelectedInactive.Border}" />
                        </MultiTrigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="Selector.IsSelectionActive" Value="True" />
                                <Condition Property="IsSelected" Value="True" />
                            </MultiTrigger.Conditions>
                            <Setter TargetName="Bd" Property="Background" Value="{StaticResource Item.SelectedActive.Background}" />
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource Item.SelectedActive.Border}" />
                        </MultiTrigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Bd" Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

然后在 XAML 中使用:

<ListBox
    Name="SomeListBox"
    ItemContainerStyle="{StaticResource ModifiedColorListBox}"
    ItemsSource="{Binding SomeCollection}"
    SelectedItem="{Binding SomeObject}">
</ListBox>

0

对我来说,这就是诀窍:

 <ListBox HorizontalContentAlignment="Stretch">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Label  Margin="-5, -2,-5,-2" Content="{Binding Item}">
                            <Label.Style>
                                <Style TargetType="Label">
                                    <Style.Triggers>
                                        <MultiDataTrigger>
                                            <MultiDataTrigger.Conditions>
                                                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBox}},Path=IsFocused}" Value="False"/>
                                                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True"/>
                                            </MultiDataTrigger.Conditions>
                                            <Setter Property="Background" Value="CornflowerBlue"/>
                                        </MultiDataTrigger>
                                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
                                            <Setter Property="Foreground" Value="White"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="False">
                                            <Setter Property="Foreground" Value="Black"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Label.Style>
                        </Label>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接