.NET (290) administrative (42) Ajax (42) AngularJS (1) ASP.NET (144) bicycle (2) books (178) browser (8) C# (128) cars (1) chess (27) CodePlex (10) Coma (8) database (46) deployment (3) Entity Framework (2) essay (109) flash/shockwave (2) flex (1) food (3) friend (2) game (20) idea (5) IIS (8) javascript (81) LInQ (2) Linux (6) management (4) manga (43) misc (663) mobile (1) movies (88) MsAccess (1) murder (2) music (65) mysql (1) news (98) permanent (1) personal (66) PHP (1) physics (2) picture (308) places (12) politics (13) programming (495) rant (118) religion (3) science (40) Sharepoint (3) software (57) T4 (2) technology (11) Test Driven Development (4) translation (2) VB (2) video (100) Visual Studio (44) web design (45) Windows API (8) Windows Forms (3) Windows Server (4) WPF/Silverlight (63) XML (11)

Wednesday, June 17, 2009

Styling WPF Controls, the Windows themes

In order to customize controls the way you want, you can either use a DataTemplate, different specific ControlTemplate properties, use the properties that the controls publish, but in the end, to get it right, you must actually change the ControlTemplate of the control itself. The best practice is to use an external resource dictionary that contains a style that changes various properties and also, if needed, the ControlTemplate.

The problem there is that there is no way to partially change the control template. I mean, yeah, I could think of a few ways, but in the end is the same thing: you need to replace the control template completely. And now you have two ... err.. actually three problems.

First, there are ways of getting the default control template, with all the functionality (we are talking about one, two or even more pages of XAML) and change only what you want. It's not trivial, but it can be done, either by copying it from the published sources (like the Windows SDK or the Expression Blend SystemThemes folder) or by using a utility like ShowMeTheTemplate.

Second, there is the issue of control templates differing depending on the Windows theme. And therefore one must either accept that the application will only look one way for every theme and Windows operating system, or provide templates for all the themes available (there are 6 defaults and a generic fallback theme).

Now, there are many tutorials about how to do that, basically just create a Themes folder, change AssemblyInfo.cs to
[assembly: ThemeInfo(
ResourceDictionaryLocation.SourceAssembly, //where theme specific resource dictionaries are located
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
)]
, copy the xaml files (with certain names) there and change them as you see fit. There is a catch, though! This will only work on your custom controls. Default controls already have a template in the operating system theme, so you need to do one extra thing in order to overwrite them, and that is to write the following in the a parent control Resources block:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{ThemeDictionary [LibraryOrApplicationNamespace]}" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
otherwise it will NOT work.

You can get the same result by adding this to the static constructor of the control, provided you have access to the sources:
DefaultStyleKeyProperty.OverrideMetadata(typeof([MyControlClass]), new FrameworkPropertyMetadata(typeof(MyControlClass)));

One thing you also need to be aware of is that Visual Studio caches the themes. If you change them, especially if you do it outside visual Studio, you need to force a Rebuild of the project.

No comments: