For several days now, I have been trying to do something like a global style configuration. How it should work?
I want to have one class to control component visual styles. For example.:
In settings page: I'm selecting my own color, and all components, that have binding to a specific variable that will store that color, change the background color or the border to it. I want to have global styles and be able to control even the color of the buttons when hovering over the mouse.
I was trying with global singleton class, and static class, but it didn't work. I also don't want to make copy of the components, because that's not the point.
Here's how I tried to do it (look at the background in style).
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:config="clr-namespace:Manager.Data.Configuration">
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
<ControlTemplate x:Key="ButtonControlTemplate" TargetType="{x:Type Button}">
<Border
x:Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{Binding StylesConfig.ButtonBackgroundColor}"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template" Value="{StaticResource ButtonControlTemplate}"/>
</Style>
</ResourceDictionary>
The StylesConfig class is just simple static class with static Brush value. I also tried to make singleton (then I typed <Setter Property="Background" Value="{Binding StylesConfig.Instance.ButtonBackgroundColor}"/>
), and the class looks like:
public class StylesConfig : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private static StylesConfig instance;
private Brush buttonBackgroundBrush;
public static StylesConfig Instance
{
get
{
if (instance == null)
instance = new StylesConfig();
return instance;
}
}
public Brush ButtonBackgroundColor
{
get
{
return buttonBackgroundBrush;
}
set
{
buttonBackgroundBrush = value;
OnPropertyChanged(nameof(ButtonBackgroundColor));
}
}
private StylesConfig()
{
ButtonBackgroundColor = new SolidColorBrush(Colors.Red);
}
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Can it be done very easily and not generate a lot of code?
CodePudding user response:
You could try keeping StylesConfig as a singleton, and at the root of your xaml declaring it with a key:
xmlns:myNamespace="clr-namespace:namespace.of.stylesconfig"
<x:Static x:Key="StylesConfig" Member="myNamespace:StylesConfig.Instance" />
Then on any button elsewhere in your xaml:
<Button Background="{Binding ButtonBackgroundColor, Source={StaticResource StylesConfig}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
If this does not work, it is worth looking into using DynamicResource, and switching the value under a certain resource from a resource manager class using Application.Current.FindResource("Dynamic_Resource_Key"). A simple example of this is:
<SolidColorBrush Key="My_Resource" Color="Yellow" />
<Button Background="{DynamicResource My_Resource}" />
Then, from elsewhere in your code at any time:
if(Application.Current.TryFindResource("My_Resource") is SolidColorBrush brush)
{
Application.Current.Resources["My_Resource"] = new SolidColorBrush() { Color = Colors.Red };
}