Bookmark and Share Share...    Subscribe to this feed Feed   About Christian Moser  


Merged ResourceDictionaries

Problems loading merged resource dictionaries in .NET 4.0

I experienced a problem, when I am trying to merge resource dictionaries at app-level. As soon as I changed the target framework from 3.5 to 4.0 they don't get loaded anymore. It seems as if they have changed something in the behavior of MergedResourceDictionaries in .NET 4.0.

I found a way how to fix this problem. Just add a dummy default style in the resource dictionary where you merge all resources together. See the following example:

This is the official bug description from Microsoft:

On the creation of every object in XAML, if a default style is present (i.e. style w/ a key of Type) that style should be applied. As you can imagine there are several performance optimizations to make that (implied) lookup a light weight as possible. One of them is that we don’t look inside Resource Dictionaries unless they are flagged as “containing default Styles”. There is a bug: if all your default styles are nested in merged dictionaries three levels deep (or deeper) the top dictionary does not get flagged so the search skips it. The work around is to put a default Style to something, anything, <Style TargetType="x:Dummy" /> in the root Dictionary.

Bug entry on Microsoft Connect
 
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Styles/DefaultStyle.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>
 
 
 
<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="DefaultStyle/Button.xaml"/>
    </ResourceDictionary.MergedDictionaries>
 
    <Style TargetType="{x:Type Rectangle}" />
 
</ResourceDictionary>
 
 

Improve the performance of merged ResourceDictionaries

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!

 
<ResourceDictionary.MergedDictionaries>
   <SharedResourceDictionary Source="/MyControlLibrary;component/Themes/Brushes.xaml"  />
</ResourceDictionary.MergedDictionaries>
 
 

To get rid of this problem, I created the SharedResourceDictionary. You can use it the same way as a conventional ResourceDictionary. The only suptile difference is, that if it is instanced multiple times, the resources are loaded only once.

 
 
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", 
"WPFTutorial.Utils")]
 
/// <summary>
/// The shared resource dictionary is a specialized resource dictionary
/// that loads it content only once. If a second instance with the same source
/// is created, it only merges the resources from the cache.
/// </summary>
public class SharedResourceDictionary : ResourceDictionary
{
    /// <summary>
    /// Internal cache of loaded dictionaries 
    /// </summary>
    public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
        new Dictionary<Uri, ResourceDictionary>();
 
    /// <summary>
    /// Local member of the source uri
    /// </summary>
    private Uri _sourceUri;
 
    /// <summary>
    /// Gets or sets the uniform resource identifier (URI) to load resources from.
    /// </summary>
    public new Uri Source
    {
        get { return _sourceUri; }
        set
        {
            _sourceUri = value;
 
            if (!_sharedDictionaries.ContainsKey(value))
            {
                // If the dictionary is not yet loaded, load it by setting
                // the source of the base class
                base.Source = value;
 
                // add it to the cache
                _sharedDictionaries.Add(value, this);
            }
            else
            {
                // If the dictionary is already loaded, get it from the cache
                MergedDictionaries.Add(_sharedDictionaries[value]);
            }
        }
    }
}
 
 




Last modified: 2011-03-14 08:47:50
Copyright (c) by Christian Moser, 2011.

 Comments on this article

Show all comments
AVEbrahimi
Commented on 14.May 2009
Seems great, I'll check it and post results here.
I've very long startup time in my big WPF project...
Andrew
Commented on 28.May 2009
Looks nice, but is this SharedResourceDictionary Blend "friendly"?
Christian Moser
Commented on 28.May 2009
Hi Andrew,

No, unfortunately the current versions of Blend cannot deal with it :-( I am searching for another solution that is fast and also supported by Blend.
Andrew
Commented on 1.June 2009
Looks like I've found a solution which address the same issue as yours, but it is a Blend friendly - check it out http://www.drwpf.com/blog/Home/tabid/36/EntryID/10/Default.aspx - in section "Manage a Collection of Resource Dictionaries in Code and Merge them at the Element Level" there is class "SharedResources".
DJanjicek
Commented on 31.July 2009
Hi Andrew,

since totday I'm using MergedDictionaries in my Silverlight project and I'm facing huge performance problems. The app is consuming almost 1GB of memory after a few clicks. Your solution could help me out of this trouble but I have no idea how to implement your code and how to use it.

Could you help me with this?
Sergey
Commented on 10.August 2009
I agree with DJanjicek.
Andrew, could you share an example of using your class?
Cameron
Commented on 24.August 2009
Just be aware... we were using the SharedResourceDictionary in our project, until we found out that it was holding onto XAML pages that had been closed, and thus were not being garbage collected.
HDW
Commented on 31.October 2009
_sourceUri = new Uri(value.OriginalString); allows garbage collection.

Also wrapping access to _sharedDictionaries in lock (((ICollection)_sharedDictionaries).SyncRoot) and wrapping access to MergedDictionaries in lock (((ICollection)MergedDictionaries).SyncRoot) makes the class thread safe.
fmunkert
Commented on 9.November 2009
SharedResourceDictionary seems not be compatible with the {ThemeDictionary ...} markup extensions. An InvalidOperationException is thrown which states that ThemeDictionaryExtension can be used with ResourceDictionary only.
There might be other code in WPF which does not work if you derive from ResourceDictionary.
Lee Campbell
Commented on 12.May 2010
Thanks Christian,
FYI: I have added a colleagues expansion on the problem referencing this post on this site
http://leecampbell.blogspot.com/2010/05/mergeddictionaries-performance-problems.html
Mike
Commented on 2.July 2010
I'm having some issues with this solution in the designer. I am trying to use the designer in VS2010. Using this, I get an error when using the designer, and I can't get any code completion. Any ideas, if this works in the designer?
Fran Herrera
Commented on 31.August 2010
Same here. I'm receiving a NotSupportedException when using SharedResourceDictionary on design time. When running the app, everything seems to work. VS2010Express.
jep
Commented on 16.November 2010
The code mentioned above to fix garbage collection throws exceptions:
_sourceUri = new Uri(value.OriginalString); allows garbage collection.

Any solution to this?
shemesh
Commented on 22.November 2010
i m working with Silverlight 4.
1) isn't this behavior (of making a new instance for each control reference) is a memory leak? shouldn't this be addressed by Microsoft?

2)could you post a code that allows GC and thread safe?
EinMitleser
Commented on 17.December 2010
If you want use the MergedDictionary and working Designer, just implement this :

public bool IsInDesignMode
{
get
{
return
(bool)
DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty,
typeof(DependencyObject)).Metadata.DefaultValue;
}
}

EinMitleser
Commented on 17.December 2010
Hello,

if you want to use this class in your project without loosing the designer, just impemelent this static prop into the class:

private static bool IsInDesignMode
{
get
{
return (bool) DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty,
typeof(DependencyObject)).Metadata.DefaultValue;
}
}

--- thats the new getter:
if (IsInDesignMode)
return base.Source;

return _sourceUri

And this put in front of the old setter-code :

if (IsInDesignMode)
{
base.Source = value;
return;
}

The problem is the memory at runtime(yes ms just cookin with water, like all others too/do)...and thanks to mr. moser for his little work...
Ups
Commented on 17.December 2010
I forgott to write the try catch sourrund the base.Source=value
Arun
Commented on 14.January 2011
ya its very useful for us.Its a great one for beginners.
Mary Ellen
Commented on 19.January 2011
Does anyone have this working in Silverlight 4? I am getting an exception when it tries to do "MergedDictionaries.Add" --> InvalidOperationException: Element is already the child of another element.

Any help is appreciated.
Coop
Commented on 1.February 2011
I'm also trying to get this working in SL4. Anyone know of a good example?
bobs
Commented on 28.March 2011
nice article.... Thanks!!
David Compton
Commented on 1.April 2011
In order to overcome the designer issue I used a Powershell script in a Pre and Post Build event to change ResourceDictionary to SharedResourceDictionary at build time.
Kaushal
Commented on 25.May 2011
Hi Christian,
We are experiencing problem is display of context of ViewBox after installing .Net 4.0 framweowrk. Viewbox contains some chart or tabular data. Problem is that some text get invisible on randoam basis. Cant google any suitable solution.
Looking forward for your help.
Kaushal
Commented on 25.May 2011
Hi Christian,
We are experiencing problem is display of context of ViewBox after installing .Net 4.0 framweowrk. Viewbox contains some chart or tabular data. Problem is that some text get invisible on randoam basis. Cant google any suitable solution.
Looking forward for your help.
Vikas Dangwal
Commented on 3.June 2011
Hi Christian, Its really a good article. But when i am planning to use it, it shows me an compilation error state Error 13 The tag 'SharedResourceDictionary' does not exist in XML namespace '[My Name Space]'. Line 6 Position 10. [somefile.xaml] 6 10[ ProjectName]
Could you please tell me if i am missing something. I just created same class suggested by you.

Name
E-Mail (optional)
Comment