开发者

Setting Foreground Color of Entire Window

开发者 https://www.devze.com 2023-02-17 17:59 出处:网络
I\'d 开发者_Go百科like to set the foreground (text) color to all of my elements You\'d think this would be easy, but it\'s not...

I'd 开发者_Go百科like to set the foreground (text) color to all of my elements You'd think this would be easy, but it's not...

<Window Foreground="Red">
   <Label Content="Test"/>
   <Label Content="Test"/>
   <CheckBox Content="Checkbox"/>
</Window>

This has no effect... The only way I can get this to work is if I set the Foreground property on each one of the elements specifically. And this gets annoying if you have hundreds of elements, etc.

Maybe you know of a way?


This is because such controls as Label and CheckBox override the Foreground property in their styles.

Below is an example a typical logical tree of elements that shows how the value specified on the Window level travels down the tree:

Window (Red [Local]) 
  -> Grid (Red [Inherited]) 
     -> ListBox (Red [Inherited]) 
        -> ListBoxItem (Red [Inherited]) 
           -> StackPanel (Red [Inherited]) 
              -> Label (Black [Style])
                 -> TextBlock (Black [Inherited])
              -> TextBlock (Red [Inherited])

In square brackets the source of the value is shown.

As you can see the inheritance breaks on the Label itself because it has the Foreground property set in its default style:

<Style x:Key="{x:Type Label}"
       TargetType="{x:Type Label}">
    <Setter Property="Foreground"
            Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    ...
</Style>

As a workaround for this we can use the following trick. Define the default style for such controls (as Label) in the application (in App.xaml or in the Window inself). And in that default style override the Foreground property to set a relative source binding to the nearest ancestor of the control that still has the desired value:

<Style TargetType="{x:Type Label}">
    <Setter Property="Foreground"
            Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/>
</Style>

<Style TargetType="{x:Type CheckBox}">
    <Setter Property="Foreground"
            Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/>
</Style>

After that our tree will look like this:

Window (Red [Local]) 
  -> Grid (Red [Inherited]) 
     -> ListBox (Red [Inherited]) 
        -> ListBoxItem (Red [Inherited]) 
           -> StackPanel (Red [Inherited]) 
              -> Label (Red [Binding to StackPanel.(TextElement.Foreground)])
                 -> TextBlock (Red [Inherited])
              -> TextBlock (Red [Inherited])

As you can see, our binding restores the inheritance.

Such styles need to be defined for each element that overrides the Foreground property in its style. As @Duane suggested, to not duplicate the binding in each style the BasedOn capability can be used:

<Style x:Key="ForegroundInheritanceFixStyle"
       TargetType="Control">
    <Setter Property="Foreground"
            Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/>
</Style>

<Style TargetType="{x:Type Label}"
       BasedOn="{StaticResource ForegroundInheritanceFixStyle}">
</Style>

<Style TargetType="{x:Type CheckBox}"
       BasedOn="{StaticResource ForegroundInheritanceFixStyle}">
</Style>

Hope this helps.


Sadly the way the styles work in WPF you can't create a generic style on a parent class and have it apply to the sub-classed control.

One thing you can do is create a base style that targets a base type with the property you want to set (like ContentControl) and then create a specific style for each control that is BasedOn that style. Here is an example:

<Window>
    <Window.Resources>

        <Style x:Key="BaseContentControlStyle" TargetType="{x:Type ContentControl}">
            <Setter Property="Foreground" Value="Red" />
        </Style>

        <Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseContentControlStyle}" />

        <Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource BaseContentControlStyle}" />

    </Window.Resources>

    <StackPanel>
        <Label Content="Test"/>
        <Label Content="Test"/>
        <CheckBox Content="Checkbox"/>
    </StackPanel>   
</Window>

Hope this helps.

EDIT:

You could use Pavlo's method below for restoring inheritance and make it a little easier to use like so:

<Window.Resources>

    <Style x:Key="BaseContentControlStyle" TargetType="{x:Type Control}">
        <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/>
    </Style>

    <Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseContentControlStyle}" />

    <Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource BaseContentControlStyle}" />

</Window.Resources>

At least then you don't have to copy the same setter code everywhere (BTW, I think the TextBlock inherits by default; no default style with overrides).


Yes it is not easy in wpf.But you can try like this

<StackPanel>
        <StackPanel.Resources>
            <Style x:Key="CommonStyle">
                <Setter Property="TextElement.Foreground" Value="Red" />
            </Style>
        </StackPanel.Resources>
        <Label Content="Test" Style="{StaticResource CommonStyle}" />
        <Label Content="Test" Style="{StaticResource CommonStyle}"/>
        <CheckBox Content="Checkbox" Style="{StaticResource CommonStyle}"/>

    </StackPanel>


It would certainly be annoying if you have hundreds of individual elements to configure, but I'm assuming you won't have hundreds of different types of elements.

That being the case, one thing you can do is create Styles for your types, and set the foreground colors there.

Ideally, this might be in a ResourceDictionary and each Style would reference a common foreground Color, like

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <SolidColorBrush x:Key="appForegroundColor" Color="Red" />

    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="{StaticResource appForegroundColor}" />
    </Style>

    <!-- Create styles for Element Types here -->

</ResourceDictionary>

Then you apply this resource dictionary to the Window(s) that need it, like:

<Window ...>
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid>
        <TextBlock Text="Foo" />
    </Grid>
</Window>
0

精彩评论

暂无评论...
验证码 换一张
取 消