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!
Image may be NSFW.
Clik here to view.
