Cleaning up your theme files with MergeDictionary

Once you get the resources for more than one control in generic.xaml, things can get a bit messy.

What one really wants: split up your resources into separate dictionaries for each control.

I do this as part of the latest bag-o-tricks.

I should get Lauren or Ashish to fill in the details about the “source” semantics, since they are shared for all resources (images, fonts, etc) in WPF.

Open up the bag-o-tricks in VS (or the file system) and let’s dig in.

First, you’ll notice I’ve already split out the ColorPicker and DateControls into separate xaml files.

Let’s do the Unload and Edit trick I showed yesterday.

You’ll notice that these files, just like the other theme files (and all of the other XAML files in the solution) are defined as <Page/>. What does this mean?

Let’s open our trusty Reflector to take a look.

If we build the project and drag-drop WPFSamplesLib.dll into Reflector, we can drill into the Resources. You’ll notice that we have an entry for each XAML file in our project. Except instead of .xaml, we see .baml as a file extension. You’ll also notice that the name for each entry in the resources maps to the full path for the xaml file we had in the project (the themes directory is included).

Rob goes into this a little bit on Channel9.

Going back to the project, let’s reload the project and take a look at generic.xaml.

You’ll notice this funky syntax for the Source of the ResourceDictionary. If we break it into chucks, it makes sense.

  1. WPFSamplesLib is the name of the assembly (but we strip out the .dll).

  2. “;component/” is magic. No…it basically tells the resource loader that we’re digging into embedded resources in the defined assembly.

  3. The last bit is just a pointer to the entry in the assembly resources. You’ll notice it matches the path we have in the project and the path you see in Reflector. Yes, there is a xaml/baml extension mismatch, but this is intentional. We try to hide devs from having to think about XAML vs. BAML. BAML is just the XAML content in an optimized, binary form.

This trick also works from code, in fact, using our trusty Reflector we can see this syntax for resources is the same syntax used by the compiler in initialize components for our application. (We’re looking at WPFSamplesApp.exe, this time.)

Make sense?

This post is brought to you by our friends at Paint .NET and Reflector. Paint .NET and Reflector are the first two apps I install on a clean machine. Both available freely. Both examples of .NET 2.0 WinForms hotness. (I’m being patient for the WPF versions of each.)

Have a good weekend, friends.

Comments (1)

  1. Ivolved says:

    Works like a charm except one case…If I create a Winforms Usercontrol to wrap my WPF Custom control in an ElementHost (in an external project to my Custom Control)and attempt drop my UserControl onto a Winforms Form, I get a "Failed to Create Component" message.

    ‘System.Windows.Markup.XamlParseException: ‘/MyAssembly;component/themes/MyControl.generic.xaml’ value cannot be assigned to property ‘Source’ of object ‘System.Windows.ResourceDictionary’. Cannot find type ‘MyNamespace.MyControl’. The assembly used when compiling might be different than that used when loading and the type is missing.

    Any Idea?