问题
I have a WPF application which ships with a set of default styles for Label, TextBox etc. defined by item types (no explicit keys defined).
Within the application there are two main containers used, one with dark background and one with light background, such that sometimes it's right to use black as the foreground color for a Label and sometimes its dramatically wrong. On the other hand, editors are always styled rather traditionally with light background and dark foreground, so I cannot just set the foreground for all child elements to the inverse.
Is there an elegant way to make my labels (and maybe TextBlocks as well) to decide about their foreground color dependent on 'their' background? I only want to switch between two colors, thus no auto-contrast-maximization needed, only some threshold to avoid white font on white ground.
I also don't want to define two sets of default styles, I strongly search for some way to make my single Label-Default-Style be appropriate for both background variants.
Is it possible (and feasible without too much performance hit) to add a trigger/binding to the style, which evaluates the current background color?
Alternatively, I would be interested in best practices how to cleanly set background-colors for certain FrameworkElements, especially containers/panels, without running into the problems described above.
Here is what I tried (simplified of course):
<UniformGrid>
<UniformGrid.Resources>
<!-- SimpleStyles: Label -->
<Style x:Key="{x:Type Label}" TargetType="{x:Type Label}">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Border>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Background" Value="Black">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UniformGrid.Resources>
<Label x:Name="bgSetExplicitly" Background="Black">abc
</Label>
<Border Background="Black">
<Label x:Name="bgInheritedFromParent" >abc
</Label>
</Border>
<Label>abc
</Label>
<Label>abc
</Label>
You can see that the label's background is chosen nicely, if the Label has an explicit background set (x:Name=bgSetExplicitly), but if the background is 'inherited' from the parent in the VisualTree (x:Name="bgInheritedFromParent"), it's not. I would love to have that working that the style can evaluate the "effective background" (no matter where it comes from) and choose an appropriate foreeground-brush for this background.
回答1:
This question seems to imply a deeper issue. I would guess that you haven't consolidated the management of foreground colors, and so you have an application that has code or styles that set foreground colors all over the place. And now you're dealing with one of the implications of that.
I'd face the deeper issue and fix that. I don't really understand why you're resistant to the idea of creating default styles, but assuming for the moment that you have a good reason not to do this (other than "I should have created default styles at some point, but now that I haven't and my application has gotten big, it's just too hard," in which case, I have a different suggestion), how about creating global resources?
Create objects in the application's resource dictionary for the controls' foreground and background colors, and fix the XAML so that instead of referencing brushes directly, it uses the DynamicResource
markup extension to get them from the resource dictionary. In code, instead of setting the Foreground
property on the controls to a brush directly, use GetResource
to get the brush. And set the background the same way.
Once you've done this, if you want to change foreground/background colors globally in your application, you just change the resources.
This is basically how you start making a WPF application skinnable, which seems to be the road you're going down.
回答2:
The easiest thing to do is bind the foreground color of your TextBoxes, etc. to the background of their containing element, and use a value converter to make the change. This is especially easy if you are only ever using two colors.
As an example....
public class ColorSwapper : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value == null) { return DependencyProperty.UnsetValue; }
Color srcColor = (value as SolidColorBrush).Color;
if (srcColor == Colors.Black)
{
return new SolidColorBrush(Colors.White);
}
else
{
return new SolidColorBrush(Colors.Black);
}
}
...
And some example XAML....
<Grid Grid.Column="1" Name="RightGrid" Background="White">
<TextBlock Text="Hello Nurse!" Foreground="{Binding ElementName=RightGrid, Path=Background, Converter={StaticResource ColorSwapper}}"/>
</Grid>
Of course you will probably want to make some tweaks and cleanup according to your specific needs, but that will do it.
回答3:
Here is another possible approach. If your application only has ALL dark/light backgrounds and their compliments, you can try an approach like this, using the 'ColorSwapper' that I mentioned in my other answer.
<Window>
<Window.Resources>
<local:ColorSwapper x:Key="swapper"/>
<SolidColorBrush x:Key="BackBrush" Color="Black"/>
<!--<SolidColorBrush x:Key="BackBrush" Color="White"/>-->
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="{Binding Source={StaticResource BackBrush}, Converter={StaticResource swapper}}"/>
</Style>
</Window.Resources>
<Grid Background="{StaticResource BackBrush}">
<TextBlock FontSize="24" FontWeight="Bold">ABC</TextBlock>
</Grid>
</Window>
Now whenever you change the color value of 'BackBrush', all of the related foreground colors will automatically update. Based on your discussions, I think that this will also meet your style requirements. At any rate, you will likely have to make small mods to ensure that this approach will fit your exact scenario.
来源:https://stackoverflow.com/questions/6186344/style-to-choose-suitable-foreground-according-to-background-color