WPF Merged Dictionary problems and solutions

Some time ago I had to do a Theming infrastructure for the application in my company. Our application was pretty big with hundreds or maybe thousands of resources in XAML. Let me tell you something: Structuring those resources with Merged Dictionaries was a huge pain in my butt. I had to learn all WPF’s problems the hard way. Don’t repeat my mistakes, read this post carefully before restructuring your XAML resource files.

We’ll go through the problems and eventually I promise to provide some great solutions which will give you a head start.

The Challenge

Our application had endless XAML resources in several very long files, which I wanted moved to individual XAML resource file and used Merged Dictionary to load them. Actually I wanted to load different XAML files for each theme, but also to have “Shared” XAML files that are loaded for all themes. These resources have to be in application scope and not for specific Control or Window.
I decided I’m going to do this perfect, so I’ll use StaticResource for everything and none of that DynamicResource magic.

Let’s see how we can go about structuring resource files.

Using MergedDictionaries in App.xaml

In a big Application, we’ll want to organize our WPF resources well. So let’s say we’ll have a Resource Dictionary file for Font sizes

MyFontResources.xaml:

Now I’d like all my TextBlocks to have this font size. In App.xaml:

And a little test – Show the TextBlock. In MainWindow.xaml:

The result is:


Very good.

Now I’d like a new file to hold all my TextBlock styles. Let’s call it… TextBlockStyles.xaml. And it will look like this:

My App.xaml now looks like this:

And the result is still:

So far so good.

We see that in App.xaml when placing one merged dictionary below another, the XAML file in the 2nd dictionary can use the resources defined in the 1st one.
And order does matter here.

Using MergedDictionaries in a file that’s not App.xaml

Let’s say I want another hierarchy level: Themes.
I want a ThemeBlue.xaml and ThemeRed.xaml, both of which are independent from one another. Let’s do that.
I create a new file ThemeBlue.xaml with:

In App.xaml:

And the result is:

Wait… that’s not right.

This brings us to a problem – When using merged dictionaries in a resource dictionary that’s not App.xaml, you cannot use Resources as StaticResources from a merged dictionary defined before your own dictionary.

Two solutions come to mind:

  1. Define any Merged Dictionary directly in App.xaml. Why do I need those additional hierarchy levels anyway?
  2. Use DynamicResource always

Both of these solutions are problematic, but we’ll talk about them later. First, let’s consider another scenario.

Custom controls and Generic.xaml

In my huge applications with hundreds of screens, I might have lots and lots of Custom Controls.

A custom control has a default style defined in a special file called Generic.xaml.  [Check out my article on Default styles in XAML]

Let’s add a new custom button called “CustomButton”

In Generic.xaml:

 

In App.xaml:

Running this will actually crash the program

The reason is the StaticResource we used FontSize=”{StaticResource FontSize.Medium}”.
Changing it to DynamicResource will work.

Placing the FontSize resources inside a merged dictionary in Generic.xaml will solve the problem only if the Style is in Generic.xaml and not in a merged dictionary.

For Generic.xaml, you can use StaticResource for resources defined in Generic.xaml. Otherwise use DynamicReousrce. 

Regroup and count

  1. When using Merged dictionaries in a resource dictionary that’s not App.xaml (2nd+ hierarchy level), the resources defined in that dictionary cannot be used as StaticResource in another dictionary defined below it (in a line after it)
    When merged dictionary is used in App.xaml it works well.
  2. In Generic.xaml, you cannot use resources defined in App.xaml as StaticResource. Only as DynamicResource.
  3. In Generic.xaml, you can use resources as StaticResource if defined in Generic.xaml or in a resource dictionary in Generic.xaml ‘s MergedDictionaries. As long as your default style is in Generic.xaml itself and not in a merged dictionary.
    If you define a default style in a merged dictionary out of Generic.xaml, the resources defined in App.xaml will work with DynamicResource, but you won’t be able to use resources defined in Generic.xaml or a merged dictionary out of Generic.xaml

Solutions

Let’s check out our options.
In all options except #4, for Default styles, you’ll have to have a very long Generic.xaml file or use Implicit styles.

  1. Keep all our resources in a single very long file. You can even use regions inside your XAML file with this extension.
    This long file can be Generic.xaml but we will have to load Generic.xaml as a merged dictionary in App.xaml.
  2. Define all merged dictionaries in a single hierarchy level in App.xaml.
    This is the actual solution I used in my application. In hindsight, I’d go with solution #4.
  3. Use whatever hierarchy you want for your resources and use DynamicResource everywhere
  4. Create whatever XAML hierarchy you want, and place your files in a folder with .txaml extensions.
    A small simple program (provided below in GitHub) will run as a pre-build event and merge your .txaml files into one long .XAML file

    This allows to structure resources folders and files however you want, without WPF’s limitations. StaticResource and the designer will work always.
    This is the only solution where you can have CustomControl styles in multiple files, not just one long Generic.xaml.

You might see by now I think solution #4 is the best.
I think it solves all our problems really. Well, you still have to use DynamicResource for your Generic.xaml default styles, but other than that it’s pretty perfect.

I created the merging tool and an example solution, so you can see this in action. Feel free to use this in your applications.

Solution #4 in GitHub

I’m using 2 folders for my resources and running the merge tool twice.
One folder is merged into AppResources.xaml and another folder is merged into Generic.xaml.
In each folder we can have as many files and as many sub-folders we want. Just remember to order them correctly if the resources rely on each other.
If you look at “Controls” project, you’ll see the pre-build event that does the merging.

If themes we can create a folder for each theme and use the merger to merge each theme into a single XAML file. Once you have one file per theme, and possibly a file for shared resources, you can load them easily per customer configuration.

DynamicResource issues

DynamicResource is a valid solution… with a price 🙂

  1. DynamicResource has a performance problem. It’s heavier than StaticResource. The performance difference however will most likely be unnoticeable (See Benchmark)
  2. You can’t use DynamicResource as a parameter in your converters. The example below will crash the program if used with DynamicResource
3. I had very difficult bugs with DynamicResource regarding unfrozen resources. Long story short, when using DynamicResource, add po:Freeze on all Freezables (like Brushes)
4. When extending a Style with BasedOn, WPF doesn’t allow the base style to be a DynamicResource.

More Sources

I talk here about application scope defined resources. Not specific window or Control resources.
These have other problems I didn’t go into.

Here are some good posts about this issue if you just can’t get enough of Merged Dictionaries 🙂

12 thoughts on “WPF Merged Dictionary problems and solutions

  1. Adam Caviness

    This is good, thanks for sharing what you ran into to. One solution not mentioned is the simplest: merge the needed dictionary into each “terminal” dictionary. That is, if the dictionary contains a Style or Template, it should merge whatever it needs in that dictionary. This was what I believe the WPF elders envisioned. The caveat is obvious however, with each merge you get a new instance so at some point, your #4 solution becomes more desirable and I’m glad you brought that up as an approach. In practice WPF has several very fundamental issues that were never ironed out since the very beginning.

    Reply
    1. michaels9876@gmail.com Post author

      Thanks for your comment Adam.

      Yes, good point!

      It feels less orderly to me to do that I guess. Sort of excess work.
      Also, I read a post that said re-merging a big amount of dictionaries like that caused serious performance problems. Didn’t experience that myself, so should be taken with a grain of salt:)

      But you are right, it’s a basic approach that solves the problem.

      Reply
  2. Pingback: WPF XAML Resource Benchmark - Michael's Coding Spot

  3. Kirk Fertitta

    Hi Michael,

    Thanks for this very nice post. Frustrating that this is even necessary, but so many things seem this way in WPF.

    One question regard solution #3: This seemed preferable to me at first blush — at least as a stopgap measure until I decided what I really wanted. However, we make quite heavy use of BasedOn, as we have multiple control libraries where one style derives from another and extends it. BasedOn does not support DynamicResource, sadly. So, for most projects I would expect that solution #3 isn’t really viable.

    Thoughts?

    Reply
    1. michaels9876@gmail.com Post author

      Hi Kirk,
      I tend to agree with you and I wouldn’t choose to use dynamic resources everywhere myself. Having said that, I did witness a pretty large application using solution #3 successfully.

      The BasedOn issue is a big problem and you would have to do some workarounds.
      One solution is to arrange the App.xaml hierarchy in a way that you would be able to use StaticResource in your extended style.
      But that kind of defeats the purpose of using dynamic resources everywhere…

      I would suggest going with solution #4. Or solution #2 with the occasional DynamicResource.

      And I will add the BasedOn issue to the post. Thanks!

      Reply
  4. Ivan Krivyakov

    Mike, I had to implement something similar, and I must tell you, merging XAML files it is not an easy task for a big project. The devil, as usual, is in the details.

    Firstly, you must handle (or at least detect) inconsistent namespaces in different files. One file may have

    xmlns:keys=”clr-namespace:Foo.Keys”,

    while other may have

    xmlns:keys=”clr-namespace:Bar.Keys”,

    Your program simply bails out with “duplicate attribute” exception. But I guess it would be relatively easy to add more detailed diagnostics. It took me considerable effort to go over the entire project and make the namespaces consistent.

    A even nastier problem happens with cross-assembly references. In some cases you may have

    xmlns:something=”clr-namespace:Bla.Controls”

    and in others

    xmlns:something=”clr-namespace:Bla.Controls;assembly=Bla”

    Furthermore, WPF actually *requires* these to be different. I had to add code to ‘normalize’ such namespaces before importing.

    And most importantly, existing project has lots of “MergedDictionary” elements already, it would be a pain to go over them and modify them all, keeping the order right. I ended up processing MergedDictionary nodes, which requires less modification of the original files, but adds complexity to the merger.

    Reply
    1. michaels9876@gmail.com Post author

      Yeah, my merger definitely needs more work to deal with the namespace problems at the very least. I guess it’s at the proof-of-concept stage now with a lot more polishing to do. When I’ll have free time or someone opens an issue on GitHub, I’ll get it done.
      Anyway, good to hear about your interesting challenges Ivan and that this is implemented in real world applications. I hope it solved all your resource problems.

      Reply
  5. Kirk Fertitta

    Hi Michael,

    One thing I would kindly ask you to edit in your excellent post here is some of the wording used in describing where the MergedDictionaries are defined. I find the use of the phrase “out of App.xaml” and “outside App.xaml” to be a bit confusing. Perhaps you could use phrases such as “defined within App.xaml” and “defined in a file other than App.xaml”, or some such. In the first Regroup and Count, for instance, you use the phrases “outside of App.xaml” and “out of App.xaml” in the same paragraph to refer to different locations (I believe).

    Just a suggestion.

    Reply
    1. michaels9876@gmail.com Post author

      Thanks for the suggestion Kirk! I wasn’t sure how to best phrase it myself when I wrote it. I’ll change it to be more clear

      Reply
  6. Pingback: WPF complete guide to Themes and Skins - Michael's Coding Spot

  7. Pingback: Explicit, Implicit and Default styles in WPF - Michael's Coding Spot

Leave a Reply