【问题标题】:Change foreground color dynamically based on background color根据背景颜色动态更改前景色
【发布时间】:2013-09-03 17:08:48
【问题描述】:

问题域:在我的 WPF 应用程序中,我根据它们包含的数据动态更改了许多 UI 控件(如 Button 或 ListItems)的背景。加载控件时或根据接收到的使用操作/数据更改背景。

问题说明:如果背景太暗(绿色/蓝色),我想将前景设置为白色,否则为黑色。

约束:我有一个大型应用程序,性能是一个主要问题。这就是为什么我不愿使用转换器并正在寻找一些基于 xaml/style 触发器的解决方案,因为这只是一个基于条件的问题。

尝试过的解决方案:为了简单起见,我正在解释我为一个简单的 wpf 按钮尝试了什么:

<UserControl.Resources>
<Style x:Key="NoChromeButton" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="{Binding Background}"/>
        <Setter Property="OverridesDefaultStyle" Value="True"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="BorderBrush" Value="White"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid x:Name="Chrome" 
                          Background="{TemplateBinding Background}" 
                          SnapsToDevicePixels="true"
                          HorizontalAlignment="Stretch">
                    <TextBlock 
                        Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          Margin="{TemplateBinding Padding}" 
                                          Background="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                        Style="{StaticResource MyTextBlockStyle}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="Black"/>
                        </Trigger>
                        <Trigger Property="Background" Value="White">
                            <Setter Property="Foreground" Value="Aqua"/>
                        </Trigger>
                        <Trigger Property="Background" Value="Transparent">
                            <Setter Property="Foreground" Value="BlueViolet"/>
                        </Trigger>
                        <Trigger Property="Background" Value="Green">
                            <Setter Property="Foreground" Value="Blue"/>
                        </Trigger>
                        <Trigger Property="Background" Value="Yellow">
                            <Setter Property="Foreground" Value="Red"/>
                        </Trigger>
                        <Trigger Property="Background" Value="Red">
                            <Setter Property="Foreground" Value="Yellow"/>
                        </Trigger>
                        <Trigger Property="Background" Value="Black">
                            <Setter Property="Foreground" Value="DarkSeaGreen"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

        <Style x:key="MyTextBlockStyle" TargetType="TextBlock">
    <Setter Property="FontSize" Value="12"/>
    <Setter Property="FontStyle" Value="Italic"/>
    <Style.Triggers>
        <Trigger Property="Background" Value="White">
            <Setter Property="Foreground" Value="Aqua"/>
        </Trigger>
        <Trigger Property="Background" Value="Transparent">
            <Setter Property="Foreground" Value="BlueViolet"/>
        </Trigger>
        <Trigger Property="Background" Value="Green">
            <Setter Property="Foreground" Value="Blue"/>
        </Trigger>
        <Trigger Property="Background" Value="Yellow">
            <Setter Property="Foreground" Value="Red"/>
        </Trigger>
        <Trigger Property="Background" Value="Red">
            <Setter Property="Foreground" Value="Yellow"/>
        </Trigger>
        <Trigger Property="Background" Value="Black">
            <Setter Property="Foreground" Value="DarkSeaGreen"/>
        </Trigger>
</Style>
</UserControl.Resources>

在 XAML 中创建按钮时:

  <Button Content="{Binding Name}" Style="{StaticResource NoChromeButton}"/>

另外,我想在上述风格中指出几点:

  1. 如果我在名为 Chrome 的网格中使用 ContentPresenter 而不是 TextBlock,则未在 ContentPresenter 上设置背景属性,当我窥探 (http://snoopwpf.codeplex.com/) UI 时,我发现 ContentPresenter 具有始终设置背景的 TextBlock为默认值,因此没有将样式触发器应用于 TextBlock。另外,这个 TextBlock 的背景值源是 Default。

另一方面,当我直接在名为 Chrome 的网格中使用 TextBlock 时,我可以将其背景显式设置为设置为按钮背景的网格背景。 Snooping 显示现在 TextBlock 的 Background ValueSource 是 ParentTemplate。

  1. 按钮在显示其内容时选择 MyTextBlockStyle。

  2. 永远不会触发 Button 或 TextBlock 的样式触发器,除非我将鼠标悬停在将按钮的背景更改为黑色并将此值向下传播到 TextBlock 背景的按钮上,从而将 TextBlock 的前景色更改为 DarkSeaGreen。

  3. 此外,在应用程序运行时更改 snoop 实用程序中按钮的背景会触发样式触发器。

问题:

  1. 为什么 Style 触发器对 Background 属性不起作用,而对 IsMouseOver 属性起作用?

  2. 我做错了什么?

  3. 有什么解决办法吗?

【问题讨论】:

  • 由于性能问题而对触发器的偏好对我来说听起来像是过早的优化。我敢打赌,您甚至没有对它们之间的差异进行基准测试。

标签: wpf button colors triggers styles


【解决方案1】:

我找到了解决问题的方法。 TextBlock 不是从 Control 派生的。任何控件在 UI 上显示的任何文本在内部都使用 TextBlock 来表示文本内容。如果在 ResourceDictionary 中使用以下设置 TextBlock 样式:

<Style TargetType="TextBlock">
    <Setter Property="FontFamily" Value="Arial" />
    <Setter Property="FontSize" Value="12" />
    <Setter Property="FontStyle" Value="Normal" />
</Style>

任何表示文本的控件都将具有此样式(因为没有为此样式分配键,这意味着默认情况下所有 TextBlock 都会获取它),除非控件的模板覆盖 TextBlock 的默认样式,可以按如下方式完成:

<Button Grid.Column="1" Style="{StaticResource NoChromeButton}">
    <TextBlock Style="{x:Null}" Text="abc" FontFamily="Segoe UI Symbol"/>
</Button>

这个简单的设置解决了我们在动态前景色变化时遇到的大部分问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-05
    • 1970-01-01
    • 2016-06-02
    • 2019-06-20
    • 2013-10-24
    • 1970-01-01
    • 2014-06-06
    相关资源
    最近更新 更多