Настройка цвета переднего плана всего окна
Я хотел бы задать цвет переднего плана (текста) для всех моих элементов
Вы бы подумали, что это будет легко, но это не...
<Window Foreground="Red">
<Label Content="Test"/>
<Label Content="Test"/>
<CheckBox Content="Checkbox"/>
</Window>
Это не влияет... Единственный способ, которым я могу заставить это работать, - это установить свойство Foreground для каждого из элементов. И это раздражает, если у вас есть сотни элементов и т.д.
Может быть, вы знаете способ?
Ответы
Ответ 1
Это связано с тем, что такие элементы управления, как Label
и CheckBox
, переопределяют свойство Foreground
в своих стилях.
Ниже приведен пример типичного логического дерева элементов, который показывает, как значение, указанное на уровне Window
, перемещается вниз по дереву:
Window (Red [Local])
-> Grid (Red [Inherited])
-> ListBox (Red [Inherited])
-> ListBoxItem (Red [Inherited])
-> StackPanel (Red [Inherited])
-> Label (Black [Style])
-> TextBlock (Black [Inherited])
-> TextBlock (Red [Inherited])
В квадратных скобках показан источник значения.
Как вы можете видеть разрывы наследования для самого Label
, поскольку он имеет свойство Foreground
в своем стиле по умолчанию:
<Style x:Key="{x:Type Label}"
TargetType="{x:Type Label}">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
...
</Style>
В качестве обходного пути для этого мы можем использовать следующий трюк. Определите стиль по умолчанию для таких элементов управления (как Label
) в приложении (в App.xaml или в Window
inself). И в этом стиле по умолчанию переопределить свойство Foreground
, чтобы установить относительную привязку источника к ближайшему предшественнику элемента управления, который все еще имеет желаемое значение:
<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>
После этого наше дерево будет выглядеть так:
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])
Как вы можете видеть, наша привязка восстанавливает наследование.
Такие стили должны быть определены для каждого элемента, который переопределяет свойство Foreground
в своем стиле. Как предположил @Duane, чтобы не дублировать привязку в каждом стиле, можно использовать функцию BasedOn
:
<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>
Надеюсь, что это поможет.
Ответ 2
К сожалению, как стили работают в WPF, вы не можете создать общий стиль для родительского класса и применить его к подклассифицированному элементу управления.
Одна вещь, которую вы можете сделать, - создать базовый стиль, который нацелен на базовый тип с свойством, которое вы хотите установить (например, ContentControl), а затем создать определенный стиль для каждого элемента управления, который основан на этом стиле. Вот пример:
<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>
Надеюсь, что это поможет.
EDIT:
Вы можете использовать метод Pavlo, приведенный ниже, для восстановления наследования и упрощения его использования следующим образом:
<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>
По крайней мере, вам не нужно копировать один и тот же код setter везде (BTW, я думаю, что TextBlock наследует по умолчанию, без стиля по умолчанию с переопределениями).
Ответ 3
Да, это не так просто в wpf. Но вы можете попробовать как это
<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>
Ответ 4
Конечно, это будет раздражать, если у вас есть сотни отдельных элементов для настройки, но я предполагаю, что у вас не будет сотен различных типов элементов.
В этом случае одна вещь, которую вы можете сделать, это создать стили для ваших типов и установить там цвета переднего плана.
В идеале это может быть в ResourceDictionary
, и каждый стиль будет ссылаться на общий цвет переднего плана, например
<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>
Затем вы применяете этот словарь ресурсов к окнам, которые ему нужны, например:
<Window ...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<TextBlock Text="Foo" />
</Grid>
</Window>