问题域:在我的WPF应用程序中,我会根据包含的数据动态更改许多UI控件(如Button或ListItems)的背景。加载控件时或根据接收到的使用动作/数据更改背景。
问题陈述:如果背景太暗(绿色/蓝色),我想将前景设置为白色或黑色。
限制:我有一个大型应用程序,性能是一个主要问题。这就是为什么我对使用转换器犹豫不决,并正在寻找一些基于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上没有设置背景属性(http://snoopwpf.codeplex.com/)在UI中,我发现ContentPresenter的TextBlock的Background始终设置为Default,因此没有将样式触发器应用于TextBlock。此外,此TextBlock的背景值源为Default
另一方面,当我直接在名为Chrome的网格中使用TextBlock时,我可以将其背景明确设置为网格的背景,即设置为按钮的背景。Snooping显示,现在TextBlock的Background ValueSource是ParentTemplate。
按钮在显示其内容时拾取MyTextBlockStyle。
Button或TextBlock的样式触发器从未被触发,除非我将鼠标悬停在按钮上,将按钮的背景更改为黑色,并将此值向下传播到TextBlock背景,将TextBlock的前景色更改为DarkSeaGreen。
此外,当应用程序运行时,在snoop实用程序中更改按钮的背景会触发样式触发器。
问题:
为什么Style触发器都不适用于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>
这个简单的设置解决了我们在动态前景颜色更改方面遇到的大多数问题。