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. Structuring big XAML resource hierarchies with Merged Dictionaries turned to be a big pain. I had to learn all of WPF’s problems the hard way. Don’t repeat my mistakes, read this post carefully before restructuring your XAML resource files.
This article will show how to:
- Use Merged Dictionaries in WPF to have application-wide resources that you can later use as a StaticResource or DynamicResource.
- Correctly use nested Merged Dictionaries for better file separation
- How to create Theme-specific resource files
- Go over the special Generic.xaml file, how to use it for custom controls and how to combine it with other application-wide resources.
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:
<ResourceDictionary xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:local="clr-namespace:MergedDictionaryProblems"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:Double x:Key="FontSize.Medium">26</sys:Double>
</ResourceDictionary>
Now I’d like all my TextBlocks to have this font size. In App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyFontResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBlock" x:Key="MyTextBlockStyle">
<Setter Property="FontSize"
Value="{StaticResource FontSize.Medium}"/>
</Style>
</ResourceDictionary>
</Application.Resources>
And a little test – Show the TextBlock. In MainWindow.xaml:
<TextBlock Text="WPF Merged Dictionary experiment" Style="{StaticResource MyTextBlockStyle}"/>
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:
<Style TargetType="TextBlock" x:Key="MyTextBlockStyle">
<Setter Property="FontSize" Value="{StaticResource FontSize.Medium}"/>
</Style>
My App.xaml now looks like this:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyFontResources.xaml"/>
<ResourceDictionary Source="TextBlockStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
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:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyFontResources.xaml"/>
<ResourceDictionary Source="TextBlockStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
In App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ThemeBlue.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
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:
- Define any Merged Dictionary directly in App.xaml. Why do I need those additional hierarchy levels anyway?
- 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”
public class CustomButton : Button
{
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
}
In Generic.xaml:
<Style TargetType="{x:Type local:CustomButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomButton}">
<Border Background="Beige">
<TextBlock Text="Click here" FontSize="{StaticResource FontSize.Medium}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In App.xaml:
<Application.Resources>
<sys:Double x:Key="FontSize.Medium">26</sys:Double>
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
- 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. - In Generic.xaml, you cannot use resources defined in App.xaml as StaticResource. Only as DynamicResource.
- 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.
-
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. -
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. -
Use whatever hierarchy you want for your resources and use DynamicResource everywhere
-
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 fileThis 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.
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 🙂
- DynamicResource has a performance problem. It’s heavier than StaticResource. The performance difference however will most likely be unnoticeable (See Benchmark )
- You can’t use DynamicResource as a parameter in your converters. The example below will crash the program if used with DynamicResource
Converter={converters:BooleanToObjectConverter TrueValue='{StaticResourceMainWindow.Right.Half}',FalseValue='{StaticResource Thickness.MainWindow.LeftAndRight.Half}'},Mode=OneWay}"
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)
xmlns:po=http://schemas.microsoft.com/winfx/2006/xaml/presentation/options
…
<SolidColorBrush po:Freeze="True" x:Key="ProgressBarBackground" Color="#ffedd5d5" />
4. When extending a Style with BasedOn, WPF doesn’t allow the base style to be a DynamicResource.
<Style BasedOn="{DynamicResource basestyle}"> <!-- This is illegal -->
...
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 🙂
- http://www.wpftutorial.net/MergedDictionaryPerformance.html
- http://stackoverflow.com/questions/6693320/mergeddictionaries-and-resource-lookup.
This post is interesting. I personally have over a hundred resource dictionary files in my application and everything works well, but this guy sounds convincing. - If you’re planning to implement Themes / Skins, check out WPF complete guide to Themes and Skins
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.
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.
Hello!
Thank you for this post, it gave me a good amount of incite into how I should initially structure a new enterprise size application I'm beginning work on.
I pulled your code from GitHub and I haven't any issues with it. I was curious, however, if you have an updates you've made that you haven't pushed up to GitHub? I might end up tackling some modifications and I just want to make sure you haven't already made updates somewhere!
Hi Blake,
I'm glad this post helped you! Nope, didn't have any updates. If you do have issues, feel free to talk to me or add a GitHub Issue. Any contributions are also welcome.
Ok, I might do that.
I haven't changed much, at this point, but I just started working with the code. The most noticeable thing I've changed so far is incorporating regions into the generated file in conjunction with this VS extension: https://marketplace.visuals...
Alright, Thanks. The only thing I can think of is that bugs might exist when there are 2 namespace keys that point to different namespaces.
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?
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!
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.
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.
Seeing all recent activity here: I put my version on Github:
https://github.com/ikriv/Xa...
I am using something very similar in our production code. If I remember correctly, it complains if there are two namespace prefixes pointing to different namespaces. I wrote another utility to harmonize namespace prefixes, but it's not on Github
I checked out your project Ivan and it worked for me. Good to have another option for a solution available!
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.
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
That's great. Thanks for your responsiveness.