Increase the performance of large WPF applications across the board  

Tuesday, May 11 2010

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.


  • Posted by Charlie Robbins

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

Post a comment


(required, but not displayed)

(optional)