Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF
Article

WPF Themes and Skins Engine

Rate me:
Please Sign up or sign in to vote.
4.80/5 (27 votes)
22 Jan 2008CPOL6 min read 295.9K   16.5K   168   23
In this article, I will talk about different techniques to load WPF themes and skins. I will also provide a helper class for loading and unloading themes.

Introduction

One of the best features in WPF is the ability to replace Resources, Styles, Control Templates and Data Templates at runtime. Based on this feature, one can design and implement a theme or a skin mechanism as introduced in many articles including this one.

There are several ways to implement such a mechanism; each has its pros and cons.

In this article, I will talk about different techniques for handling WPF themes and skins. I will also provide a helper class for loading and unloading themes.

Acronyms

Skin

Set of UI visual Resources and Styles that usually can be replaced at runtime by the user. Skins can be installed with the application, or can be downloaded later from the internet. Skins change the face of the user interface, shapes, colors, backgrounds etc. If well designed, a skin can be created and/or edited by users. See applications such as Media Player, WinAmp and many others.

Theme

Windows operating system based theme. The one you replace from the Display/Desktop setting. For example: Classic, Luna, Royale, Aero, etc. WPF has a built-in mechanism for loading styles based on the actual Windows theme (see this). Most developers mislead by saying Theme but intended to say Skin. Themes change the face of Windows controls, such as ComboBox, TextBox, Buttons, etc. Themes are replaced from the Windows settings and not from the Application settings.

This article is not dealing with the creation of Windows themes. It only shows how to load them at runtime as skins, without depending on Windows settings.

Background

Actually there are two well known techniques in WPF for skinning: Loose and Compiled.

Loose

Loose skin mechanism is based on loose XAML files which are actually resource dictionaries. These dictionaries contain Styles, Templates, and Resources and are neither serialized (baml) nor packed into any assembly.

To load a theme or a skin based on a loose XAML files, one may call the XamlReader.Load static method, passing it the URI of the file, or may create a ResourceDictionary and set its Source property to the loose XAML file URI. After load is complete, one should merge the dictionary with the application.

C#
ResourceDictionary skin = new ResourceDictionary();
skin.Source = new Uri(@"Skins\Skin.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Remove(skin); 

Pros

  • Loose XAML files are dynamic, thus can be edited online and offline (whether the application is running or not), without compiling or parsing

Cons

  • Slow load time since each loose XAML file should be parsed and tokenized at runtime
  • Not safe since it can be edited without any build/compile action

Compiled

Compiled skin mechanism is based on resource dictionaries inside XAML files, which are parsed, serialized (baml) and packed into the governed assembly. Types such as custom controls (borders, decorators, etc.) and Value Converters are optionally compiled into the same assembly.

To load a theme or a skin based on a compiled resource, one may add a reference to the theme or skin assembly (unless the theme is in the GAC), and merge it with the application resources.

XML
<Application.Resources>
  <ResourceDictionary>
   <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;
                component\themes/aero.normalcolor.xaml" />
   </ResourceDictionary.MergedDictionaries>
   <!-- Other Resources here -->
  </ResourceDictionary>
</Application.Resources>

Pros

  • Fast load time
  • Safe since being parsed, tokenized and serialized
  • Custom types are usually compiled into the same assembly
  • Can be shared by placing the assembly in the GAC

Cons

  • Must be loaded into the running application (AppDomain) to take effect. Once loaded can't be unloaded until restarting the application. This consumes a lot of memory depending on the assembly size and the amount of skins
  • Static, and should be parsed, serialized (baml) and packed into the governed assembly
  • Can't be loaded unless referenced (add reference to assembly) at design time

Code - Skin Loader

To overcome some of the disadvantages in the compiled version theme, to be able to load compiled version theme from any directory and to simplify the load process of a skin, I have implemented a simple skin loader helper as follows:

Skin – An abstract base class for all skin loader types. It provides an interface for loading and unloading a skin, and maintains the skin resources. The Load and Unload methods in this type merge or remove the skin resources from the application single instance resources respectively. It is important to remove the resources of a skin before loading others. Failing to do so may cause a memory leak.

ReferencedAssemblySkin – Loads a referenced theme assembly as previously described. Before using this type, one should add a reference to the theme assembly, and compile the application with it. The assembly loads and stays in the main AppDomain for the application life time.

The screen shot below demonstrates how the Tomers.WPF.Themes.SimpleSkin.dll assembly loads into the process, using Process Explorer.

ss1.jpg

AppDomainAssemblySkin – Loads a skin assembly into a different application domain. This overcomes the problem that is caused by loading a theme assembly directly, as described in this article. The Load override implementation of this type loads the skin into a different application domain, and passes the skin resource stream to the calling application domain. Then a BamlHelper class is used to deserialize the resource stream back into a ResourceDictionary.

The screen shot below looks similar to the previous one, but as you can see, the Tomers.WPF.Themes.SimpleSkin.dll assembly (previously below gdi32.dll) didn't load into the AppDomain/Process.

ss2.jpg
  • This solution prevents the theme assembly from loading into the calling AppDomain, unless the theme uses any CLR type that is defined in the theme assembly. For example, if the theme uses a value converter for binding, and the value converter is defined in the theme assembly, the theme assembly will be loaded into the calling AppDomain. To prevent this from happening, only use custom types defined in a common assembly.

DirectAssemblySkin – Similar to ReferencedAssemblySkin type, this type loads any theme assembly from any location. There is no need to add a reference to the theme assembly.

LooseXamlSkin – Loads a loose XAML file skin as described earlier.

Comparison

To test the load time of each of the strategies above, I wrote a simple test application. The test application draws six WPF controls in a stack, and provides an option to replace Skins at runtime.

The image below is a screen shot from the test application:

ss3.jpg

These are the results from the comparison:

Skin Strategy First Load Time (ms.) Second Load Time (ms.)
Simple Direct 16 15
Simple AppDomain 125 93
Classic Loose 1045 950
Classic AppDomain 375 312

Conclusion

  • If the application does not change themes/skins at runtime, consider using one of direct or reference strategies since they provide the fastest load time.
  • In cases where the application skins have to be edited offline or online without any compilation, consider using the loose strategy.
  • In other cases where the application replaces more than one
    theme or skin at runtime, consider using the AppDomain strategy. This will prevent unused skin assemblies from staying resident in memory.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect CodeValue
Israel Israel
Tomer Shamam is a Software Architect and a UI Expert at CodeValue, the home of software experts, based in Israel (http://codevalue.net). Tomer is a speaker in Microsoft conferences and user groups, and in-house courses. Tomer has years of experience in software development, he holds a B.A degree in computer science, and his writings appear regularly in the Israeli MSDN Pulse, and other popular developer web sites such as CodePlex and The Code Project. About his thoughts and ideas you can read in his blog (http://blogs.microsoft.co.il/blogs/tomershamam).

Comments and Discussions

 
BugNice work, minor bug Pin
Leif Simon Goodwin31-May-20 23:13
Leif Simon Goodwin31-May-20 23:13 
Suggestion.Net 4 changes Pin
MatrixUK4-Feb-13 11:38
MatrixUK4-Feb-13 11:38 
SuggestionRe: .Net 4 changes Pin
GravityPhazer12-Jun-14 9:26
professionalGravityPhazer12-Jun-14 9:26 
GeneralChanging Skins doesn't show skin Pin
Your Display Name Here8-Mar-10 0:09
Your Display Name Here8-Mar-10 0:09 
GeneralXamlParseException Pin
Jasmine Pomelo16-Jul-09 0:13
Jasmine Pomelo16-Jul-09 0:13 
QuestionShared Resources Pin
JohnFenton16-Mar-08 8:55
JohnFenton16-Mar-08 8:55 
GeneralRe: Shared Resources Pin
Tomer Shamam18-Apr-08 1:12
Tomer Shamam18-Apr-08 1:12 
GeneralRe: Shared Resources Pin
Jammer21-May-08 1:00
Jammer21-May-08 1:00 
GeneralVery Nice Pin
Paul Conrad27-Jan-08 10:51
professionalPaul Conrad27-Jan-08 10:51 
GeneralRe: Very Nice Pin
Tomer Shamam27-Jan-08 10:56
Tomer Shamam27-Jan-08 10:56 
QuestionNice Article - One Question Pin
User 27100922-Jan-08 16:03
User 27100922-Jan-08 16:03 
AnswerRe: Nice Article - One Question Pin
Tomer Shamam22-Jan-08 21:58
Tomer Shamam22-Jan-08 21:58 
GeneralYou are the Master Pin
Lior Israel22-Jan-08 12:40
Lior Israel22-Jan-08 12:40 
GeneralRe: You are the Master Pin
Tomer Shamam22-Jan-08 22:05
Tomer Shamam22-Jan-08 22:05 
GeneralGreat article! Pin
Andrew Wood13-Jan-08 13:17
Andrew Wood13-Jan-08 13:17 
AnswerRe: Great article! Pin
Tomer Shamam13-Jan-08 21:46
Tomer Shamam13-Jan-08 21:46 
GeneralRe: Great article! Pin
Andrew Wood22-Jan-08 10:06
Andrew Wood22-Jan-08 10:06 
GeneralRe: Great article! Pin
Tomer Shamam22-Jan-08 10:52
Tomer Shamam22-Jan-08 10:52 
GeneralPerfect timing Pin
Josh Smith7-Jan-08 3:53
Josh Smith7-Jan-08 3:53 
GeneralRe: Perfect timing Pin
Tomer Shamam13-Jan-08 0:32
Tomer Shamam13-Jan-08 0:32 
GeneralA very nice article on Skins/Themes Pin
Sacha Barber4-Jan-08 23:41
Sacha Barber4-Jan-08 23:41 
AnswerRe: A very nice article on Skins/Themes Pin
Tomer Shamam6-Jan-08 0:39
Tomer Shamam6-Jan-08 0:39 
GeneralRe: A very nice article on Skins/Themes Pin
Sacha Barber6-Jan-08 1:14
Sacha Barber6-Jan-08 1:14 
most welcome...its cool

Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same view, and never argue

My Blog : sachabarber.net

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.