WPF allows a control library to provide different resource dictionaries for different system themes, essentially allowing an application to match the operating system's selected visual theme (Aero, Luna, etc).
I'm wondering if I can include multiple theme resource dictionaries with my application and utilise some existing theme support within the framework. This should work for my own theme names, and ideally allow the user to change the theme and therefore the skinned appearance of the application at runtime. Even if this were only a config setting, it could still be interesting.
Here is a snippet of code that I used in my application that supported theming. In this example, I have two themes (Default and Classic XP). The theme resources are stored in the DefaultTheme.xaml and ClassicTheme.xaml respectively.
This is the default code in my App.xaml
<Application ...>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ArtworkResources.xaml" />
<ResourceDictionary Source="DefaultTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="SwooshButton" TargetType="ButtonBase">
<!-- style setters -->
</Style>
<!-- more global styles -->
</ResourceDictionary>
</Application.Resources>
</Application>
Then in the code behind of the App.xaml I have the following method to allow for changing the theme. Basically, what you do is clear the Resource Dictionaries and then reload the dictionary with the new theme.
private Themes _currentTheme = Themes.Default;
public Themes CurrentTheme
{
get { return _currentTheme; }
set { _currentTheme = value; }
}
public void ChangeTheme(Themes theme)
{
if (theme != _currentTheme)
{
_currentTheme = theme;
switch (theme)
{
default:
case Themes.Default:
this.Resources.MergedDictionaries.Clear();
AddResourceDictionary("ArtworkResources.xaml");
AddResourceDictionary("DefaultTheme.xaml");
break;
case Themes.Classic:
this.Resources.MergedDictionaries.Clear();
AddResourceDictionary("ArtworkResources.xaml");
AddResourceDictionary("ClassicTheme.xaml");
break;
}
}
}
void AddResourceDictionary(string source)
{
ResourceDictionary resourceDictionary = Application.LoadComponent(new Uri(source, UriKind.Relative)) as ResourceDictionary;
this.Resources.MergedDictionaries.Add(resourceDictionary);
}
What you'll also need to keep in mind with this approach is that any styles that utilize a theme will need to have a dynamic resource. For example:
<Window Background="{DynamicResource AppBackgroundColor}" />
I don't know of a way of doing this in the framework, but you can do it if you style every control that can change yourself.
The theory is to make the style a DynamicResource
and then load the ResourcesDictionary
based on the users configuration for the different style.
Here is an article that has an example.