Reputation: 21
I'm learning WPF and in my application I want to use accent color that is defined by user. All my styles are defined in Style.xaml
which is a ResourceDictionary
. What I want to achieve is this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Namespace"
x:Class="Namespace.Style"
x:ClassModifier="public">
<Color x:Key="AccentColor"
A="{Binding Accent.A}"
R="{Binding Accent.R}"
G="{Binding Accent.G}"
B="{Binding Accent.B}" />
<SolidColorBrush x:Key="AccentBrush" Color="{Binding AccentColor}"/>
</ResourceDictionary>
And in Style.xaml.cs
:
namespace Namespace
{
public partial class Style : ResourceDictionary
{
// this color can be changed later
public Color Accent { get; set; }
public Style()
{
Accent = Color.FromRgb(0x13, 0xaf, 0xf0);
InitializeComponent();
}
}
}
Code above gives error:
'A 'Binding' cannot be set on the 'A' property of type 'Color'.
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.'
What are other ways (preferably not too complex, but also flexible) to implement this feature to let user define his own accent color for application's theme?
Upvotes: 1
Views: 5705
Reputation: 12276
I would advise against "getting a colour from code behind.
Often you want a brush but sometimes a colour. Here's how I usually define these:
<Color x:Key="PaleBlue">#44A7F7</Color>
<Color x:Key="PaleRed">#F75B71</Color>
<SolidColorBrush x:Key="PaleBlueBrush" Color="{StaticResource PaleBlue}"/>
<SolidColorBrush x:Key="PaleRedBrush" Color="{StaticResource PaleRed}"/>
These go in a resourcedictionary which is merged in app.xaml so they have scope for the entire app.
If I wish to change a theme I define another resource dictionary for ( say ) Dark Theme and another for Blue Theme... and so on.
To change themes I merge the appropriate resource dictionary into application.current.resources and they then replace the original. You need to use DynamicResource to reference these if you want them to change immediately but often a theme change is an unusual thing and "just" totally reloading your window is acceptable.
You can replace one of those in code if you really wanted to.
Application.Current.Resources["PaleRedBrush"] = // some new solidcolorbrush.
A lot of things in a resourcedictionary are frozen ( there's a Freeze() method ) and you can't change them. I never tried to implement exactly what you're doing there but I think it might be problematic.
You could alternatively use a bridging static dependency object or class which implements inotifypropertychanged. One candidate would involve a observabledictionary as property in a static. That'd allow you to bind to brush by string as name. http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/ Not tried that neither.
Upvotes: 1
Reputation: 699
I use this. The demo comes with a style switch implementation. You can take a look at the example to see how it works, it's all open source.
Upvotes: 0
Reputation: 879
You don't need anything in the Styles.xaml.cs. Create a Styles.xaml ResourceDictionary:
<ResourceDictionary>
<SolidColorBrush x:Key="AccentColor" Color="#FFFFFF" />
</<ResourceDictionary>
To use this color in the other xaml files, you will have to declare it inside the merged dictionary tag along with it's path:
<Window>
<Window.Resources>
<ResourceDictionary x:Uid="ResourceDictionary_1">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</Window.Resources>
<TextBlock Background="{StaticResource AccentColor}"/>
</Window>
You can use it the way I used to apply the TextBlock background. You will have to provide the path of your Styles.xaml in the Source tag of ResourceDictionary.
Upvotes: 0