Wpf 基于背景色动态更改前景色

Wpf 基于背景色动态更改前景色,wpf,button,colors,triggers,styles,Wpf,Button,Colors,Triggers,Styles,问题域:在我的WPF应用程序中,我根据按钮或列表项所包含的数据动态更改了许多UI控件的背景。加载控件时或根据收到的使用操作/数据更改背景 问题陈述:如果背景太暗(绿色/蓝色),我想将前景设置为白色或黑色 限制:我有一个大型应用程序,性能是一个主要问题。这就是为什么我对使用转换器犹豫不决,并且正在寻找一些基于xaml/风格触发器的解决方案,因为这只是一个基于条件的问题 尝试的解决方案:为了保持简单,我正在解释我尝试的简单wpf按钮: <UserControl.Resources> &l

问题域:在我的WPF应用程序中,我根据按钮或列表项所包含的数据动态更改了许多UI控件的背景。加载控件时或根据收到的使用操作/数据更改背景

问题陈述:如果背景太暗(绿色/蓝色),我想将前景设置为白色或黑色

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

尝试的解决方案:为了保持简单,我正在解释我尝试的简单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}"/>

此外,我想指出上述风格中的几点:

  • 如果我在名为Chrome的网格中使用ContentPresenter而不是TextBlock,则ContentPresenter上没有设置background属性,当我窥探()UI时,我发现ContentPresenter有TextBlock,其背景始终设置为默认,因此没有对TextBlock应用styletriggers。此外,此文本块的背景值Source是默认值
  • 另一方面,当我直接在名为Chrome的网格内使用TextBlock时,我可以将其背景显式设置为网格的背景,即设置为Button的背景。窥探显示现在TextBlock的背景值源是ParentTemplate

  • 按钮在显示其内容时拾取MyTextBlockStyle

  • 按钮或文本块的样式触发器从未触发,除非我将鼠标移到按钮上,将按钮的背景更改为黑色,并将此值向下传播到文本块背景,将文本块的前景颜色更改为深绿色

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

  • 问题:

  • 为什么没有一个样式触发器适用于Background属性,而它们适用于IsMouseOver属性

  • 我做错了什么

  • 有什么解决办法吗


  • 我找到了解决我问题的办法。 TextBlock不是从控件派生的。任何控件在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>
    
    
    

    这个简单的设置解决了我们在动态前景色变化方面的大多数问题。

    由于性能方面的考虑,我们更喜欢触发器而不是转换器,这对我来说似乎是过早的优化。我敢打赌你甚至还没有对它们之间的差异进行基准测试。