You might as well as I would suppose that if we just set the foreground of a window to something different from its original color, then the whole controls from the window might change as well?
well this is not true, consider this example.
<Window Foreground="Red"> <Label Content="Test"/> <Label Content="Test"/> <CheckBox Content="Checkbox"/> </Window>
the reason as pointed by Pavlo Glazkov is as such ...
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 theLabel
itself because it has theForeground
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:
<StyleTargetType="{x:Type Label}"><SetterProperty="Foreground"Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/></Style><StyleTargetType="{x:Type CheckBox}"><SetterProperty="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"><SetterProperty="Foreground"Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/></Style><StyleTargetType="{x:Type Label}"BasedOn="{StaticResource ForegroundInheritanceFixStyle}"></Style><StyleTargetType="{x:Type CheckBox}"BasedOn="{StaticResource ForegroundInheritanceFixStyle}"></Style>
And because of this inheritance relationship, I found there is a problem when I tried to change the default settings for some data control, the change is not affected, it was because the container element is no modified for its fore/ground, background color.
so, e.g.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:dx="clr-namespace:DevExpress.Xpf.Core;assembly=DevExpress.Xpf.Core.v12.1" xmlns:vm="clr-namespace:RiskLayoutExample.ViewModels" xmlns:dxg="clr-namespace:DevExpress.Xpf.Grid;assembly=DevExpress.Xpf.Grid.v12.1" xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" > <DataTemplate DataType="{x:Type vm:RiskDataViewModel}"> <dx:BackgroundPanel> <DataGrid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding RiskData}" > <DataGrid.Resources> <Style TargetType="TextBlock" x:Key="{x:Type TextBlock}" > </Style> <Style TargetType="TextBoxBase" x:Key="{x:Type TextBoxBase}" > </Style> <Style TargetType="TextBox" x:Key="{x:Type TextBox}" /> <Style TargetType="DataGridCell" x:Key="{x:Type DataGridCell}"> </Style> <!-- .. --> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Header="Book" Binding="{Binding Book}" IsReadOnly="True" > </DataGridTextColumn> <!-- ... --> </DataGrid.Columns> </DataGrid> </dx:BackgroundPanel> </DataTemplate> </ResourceDictionary>
the text color of the DataGridCell does not change, so I hvae to change the foreground and background of the dxd:BackgroundPanel
So I have to supply the color of the containing element.
<dx:BackgroundPanel Foreground="White" Background="Black"> </dx:BackgroundPanel>