ResourceDictionary: Use with care


Seems like WPF is plagued with memory leaks and problems, if not handled with care. Resource dictionary is another potential pain point, the current project I’m working on is one such victim.

Consider an UserControl that references a ResourceDictionary in it’s MergedDictionaries. Consider that the referenced ResourceDictionary references other ResourceDictionaries, and so on. Now consider that you render 30 of these user controls on a window, each user control creates a unique instance of the same ResourceDictionary, plus much more. Now you suddenly have heaps of memory being allocated, unnecessary memory for that matter. Apparently this occurs in .NET 3.5 only, not 4.0.

In order to resolve this, the simple resolution I can think of is to reference all the Resource Dictionaries you need in your App.xaml, and remove the user control’s Resource Dictionaries. However it might not always be possible, so I had to look for another approach.

Fortunately I came across an excellent article that has a nice solution for this problem by Christian Mosers.

I added a tweak to that solution, which is to use a WeakReference. In theory, this should allow the controls referencing the cached Resource Dictionaries to be garbage collected since we are using Weak References. I have not had the opportunity to verify this yet, so if you do please let me know.

public class CachedResourceDictionary : ResourceDictionary
{
    private static Dictionary<Uri, WeakReference> _cache;

    static CachedResourceDictionary()
    {
        _cache = new Dictionary<Uri, WeakReference>();
    }

    private Uri _source;

    public new Uri Source
    {
        get { return _source; }
        set
        {
            _source = value;
            if (!_cache.ContainsKey(_source))
            {
                AddToCache();
            }
            else
            {
                WeakReference weakReference = _cache[_source];
                if (weakReference != null && weakReference.IsAlive)
                {
                    MergedDictionaries.Add((ResourceDictionary)weakReference.Target);
                }
                else
                {
                    AddToCache();
                }
            }

        }
    }

    private void AddToCache()
    {
        base.Source = _source;
        if (_cache.ContainsKey(_source))
        {
            _cache.Remove(_source);
        }
        _cache.Add(_source, new WeakReference(this, false));
    }
}

In my opinion, this solution is the means to an end, and one should re-think and re-work how resources are being used. In the short run to reduce memory footprint, this is a good fix. One potential problem I experience with this is that Expression Blend 2 does not like derived Resource Dictionary. Expression Blend 4 can work around this problem with the new Design Time Resource Dictionary feature. If you don’t have Blend 4 (like us), consider other poor man’s approach. My approach was to write ¬†two Nant scripts to find and replace Resource Dictionaries to make it Blend friendly, the other to reverse it back.

Happy coding!

Share this post :
About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 93 other followers

%d bloggers like this: