I was just debugging a very long render delay in some WPF code and I came across this little tidbit.
The quote of interest is: “Each time a control references a ResourceDictionary XAML creates a new instance of it. So if you have a custom control library with 30 controls in it and each control references a common dictionary you create 30 identical resource dictionaries!”
Normally that isn’t a huge problem, but when you consider the way that I personally (and have suggested to others) that they organize their resources in Prism projects it gets to be a serious problem. For example, let’s say we have this project structure:
/MyProject.Resources
/Resources
-Buttons.xaml
-DataGrid.xaml
-Global.xaml
-Brushes.xaml
-WindowChrome.xaml
-Icons.xaml
/MyProject.Module1
/Resources
-Module1Resources.xaml
(References all Dictionaries in /MyProject.Resources/Resources/*)
/Views
-View1.xaml
-View2.xaml
/MyProject.Module2
/Resources
-Module2Resources.xaml
(References all Dictionaries in /MyProject.Resources/Resources/*)
/Views
-View1.xaml
-View2.xaml
/MyProject.Shell
/Resources
-ShellResources.xaml
/Views
-MainShell.xaml
If in your views you reference the module-level ResourceDictionary (which helps for maintainability and modularity) then every time you create an instance of View1.xaml for example, you would have to parse all the ResourceDictionaries in /MyProject.Resources/Resources/* every time. And if in View1.xaml you have a reference to say View2.xaml now you’re parsing it all twice every time! This isn’t really a memory concern but it is a huge performance concern. There can potentially be thousands of lines of XAML code to parse and the time really does add up as your views become nested and more complex
I recently switched all of the MergedDictionary references:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source=”/SomeDictionary.xaml” />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
To use the attached SharedResourceDictionary which shadows the Source property and keeps a global cache of all ResourceDictionaries parsed:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<SharedResourceDictionary Source=”/SomeDictionary.xaml” />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
And I saw a performance increase of almost two orders of magnitude: From almost 6000ms to 200ms. As always, this code is available on GitHub.
Comments
Emily CrazyMonkey said on Thursday, May 13, 2010:
Truly nice post you have here. It'd be just great to read more about this theme. Thanx for posting such info.
Unni Ravindranathan - MSFT said on Thursday, May 27, 2010:
Hi Charlie,
You tweeted the fact that such a setup does not work inside Blend. Could you please send me your repro project so we could suggest a workaround?
Unnir at microsoft dot com
Thanks,
Unni
mbt tataga said on Sunday, July 11, 2010:
I twittered this..Ubbertwitter…yeah