|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionOne 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. AcronymsSkinSet 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. ThemeWindows 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 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. BackgroundActually there are two well known techniques in WPF for skinning: Loose and Compiled. LooseLoose 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 ResourceDictionary skin = new ResourceDictionary();
skin.Source = new Uri(@"Skins\Skin.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Remove(skin);
Pros
Cons
CompiledCompiled 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. <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
Cons
Code - Skin LoaderTo 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:
The screen shot below demonstrates how the Tomers.WPF.Themes.SimpleSkin.dll assembly loads into the process, using Process Explorer.
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
ComparisonTo 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:
These are the results from the comparison:
Conclusion
|
||||||||||||||||||||||||||||||||||||||||||