Introduction
One of the subjects that I didn’t bother to look into is themes in WPF. Although I figured that it would be kind of cool to create custom buttons, comboboxes and menus it wasn’t something I found the most important feature of WPF. This is mainly because I find it more important to have an application that is great in terms of usability than an application that has much bling bling.
Today a customer proved me wrong. People are asking for a bling bling interface, they want it to look good. A look that perfectly blends in with windows is okay, but if you can make a user interface that truely stands out because of rich use of colors and animations, that’s even better. There are however some key things that I have noticed that are important to people.
Before I start talking about that, I want to show how you can skin the user interface of a WPF application in a maintainable way.
How theming works
All controls in WPF are made up out of two parts, behavior and looks. The looks are generally defined in XAML and behavior is defined in code. By splitting these two parts of a control WPF allows you to skin controls using special theme files. A theme file is nothing more than a ResourceDictionary containing styles with a key set to the type of control that is styled by them.
WPF is going to try to apply a certain style to each control. First it will try to apply the style specified by the developer. When there is no explicit style applied to the control it will start looking for a style in the available resource that matches the control type. If no style matching the control type is found in the available resource WPF will start looking for one in the active theme, starting at the named theme file (e.g. Themes/Luna.blue.xaml). If that doesn’t work either it will try to read Themes/classic.xaml and finally Themes/Generic.xaml.
Creating a theme
Before you can actually start creating custom skins for controls you will need a theme resource dictionary. To create a theme for an application you need to place a new resourcedictionary file inside the Themes folder of the application.
After that the only thing required to load the theme is a new ThemeInfo attribute in the application:
[assembly: ThemeInfo(
    ResourceDictionaryLocation.ExternalAssembly,
    ResourceDictionaryLocation.SourceAssembly
)]
This way you tell WPF to look for theme information in external satellite assemblies or in the source assembly (The assembly where you defined the attribute). It will enable the use of generic.xaml as the generic theme for the application as well as named themes in external assemblies.
The default windows themes
One of the uses of theming is applying a special theme to a control when Windows is set to a specific theme. For example, if your control needs a blue background when windows is set to teletubby blue, you add a Themes/Luna.blue.xaml to the application with a style for your control that sets the background to blue. When you follow the specific naming schemes of Windows theme dictionaries you be sure that your control will look right on each of the default Windows color schemes. Adding the ThemeInfo attribute to your application is enough.
Here is the complete list of Microsoft themes:
- Themes/Luna.blue.xaml (Windows XP)
- Themes/Luna.homestead.xaml (Windows XP)
- Themes/Luna.metallic.xaml (Windows XP)
- Themes/Aero.normalcolor.xaml (Windows Vista)
- Themes/Homestead.normalcolor.xaml (Windows XP + Windows XP Mediacenter)
- Themes/classic.xaml (Windows 2000/XP/Vista/2008)
Loading custom themes
While standard Windows themes are okay, I know all developers who are reading this will want to know how you can load a custom theme. The trick is rather simple. Ian Griffiths has posted an article on how to do this on his weblog. At the bottom of his article he explains how you can load a custom theme using the following snippet.
public static ResourceDictionary LoadThemeDictionary(Type t, string themeName, string colorScheme)
{
    Assembly controlAssembly = t.Assembly;
    AssemblyName themeAssemblyName = controlAssembly.GetName();
    object[] attrs = controlAssembly.GetCustomAttributes(typeof(ThemeInfoAttribute), false);
    if (attrs.Length > 0)
    {
        ThemeInfoAttribute ti = (ThemeInfoAttribute) attrs[0];
        if (ti.ThemeDictionaryLocation == ResourceDictionaryLocation.None)
        {
            // There are no theme-specific resources.
            return null;
        }
        if (ti.ThemeDictionaryLocation == ResourceDictionaryLocation.ExternalAssembly)
        {
            themeAssemblyName.Name += "." + themeName;
        }
    }
    string relativePackUriForResources = "/" + themeAssemblyName.FullName + ";component/themes/" +
        themeName + "." + colorScheme + ".xaml";
    Uri resourceLocater = new System.Uri(relativePackUriForResources,  System.UriKind.Relative);
    return Application.LoadComponent(resourceLocater) as ResourceDictionary;
}
After you have loaded the custom theme you can apply the theme to the application by adding the theme resource to the list of merged dictionaries of the application. A sample of this is shown below:
public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        this.Resources.MergedDictionaries.Add(LoadThemeDictionary(typeof(Button),"Luna","Blue"));
    }
}
Skinning default controls
Typically you want a WPF user interface to be build mostly out of standard controls. Don’t start creating custom controls when you don’t need to, I found out that in 90% of the cases it’s enough to combine standard controls and to restyle these controls. It makes creating a custom skin for them a whole lot easier too. As a matter of fact, you get five themes for free: Blue,Silver,Olive, Aero and the classic theme.
Skinning a default control is simple. You can start skinning a control by adding a new <Style> element with a key set to the type of the control. A sample of this is shown below:
<Style x_Key="{x:Type Button}"></Style>
Styling is often not enough to change the look of your application completely. One of the other things you need to do is to create a new ControlTemplate for almost all standard controls used in your application. This done by expanding the style with the following setter.
<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type Button}"></ControlTemplate>
  </Setter.Value>
</Setter>
A good tip when replacing control templates with your own custom templates is to either lookup a sample template on MSDN or to use Blend. When using Blend you can make use of a rather cool feature. Create a new project in Blend and add a control (e.g. a button) to the newly created window. Then right click the button in the Objects and timeline window at the bottomleft of the screen. Select Edit Control Parts >> Edit Copy to create a copy of the default controltemplate. You have now a perfect copy of the default template, which you can either move over to visual studio or continue to modify in Blend.
For those of you who don’t have Expression Blend, fear not, a very complete list of sample templates can be found on MSDN.
Skinning custom controls
There are basically two scenarios for this. Either you created the custom control yourself or you have a control that was created by somebody else. If you created the custom control yourself the task of skinning it is rather easy. Just copy the default control template and modify as required. Otherwise your up for some pretty nasty work 😉 I found that it is kind of hard to skin a control when you can’t see the original controltemplate. There is a plugin for Reflector to decompile baml resources, it will be a great help when skinning a third party control.
Things to keep in mind
One of the key things to keep in mind is that its generally a bad idea to require developers to apply explicit styles to enable a custom theme. This makes the application less maintainable and its just too much work to get done in a midsize application. Instead try to use styles with a {x:Type } key as much as possible.
Splitting up a theme resource dictionary up into several separate resource dictionaries is a good idea. Some styles and templates will get rather large and will make the file less readable and as such less maintainable. A good way to keep track of various styles and templates is to create subfolder for each theme and add files like Button.xaml, Combobox.xaml, etc. in this folder. You can refer to these files from the theme resource dictionary by adding them to the MergedDictionaries list of the theme resource dictionary.
Keeping a single copy of common brushes in a separate file and not hard-coding brushes into styles is also a way of keeping a theme maintainable. Not only does this fit into the category above, it will also allow you to create a new theme quite easily. Copy the original theme and change the brushes, that’s all there is to it.
Conclusion
Aside from the bling bling, theming has a much needed spot in WPF for offering a way to blend custom controls in with default Windows themes. But I think it has an even better place with developers who like to make their application standout. Goodbye battleship gray!