Memory Leaks and Dependency Properties

As we all know (or perhaps not) WPF introduced something called the “Dependency Property”.  This is a way of externalizing state from controls.  So for example, the text value of a TextBox is actually not stored within a private field – instead the TextBox has requested the WPF framework to store the value.

This approach is clearly reducing the encapsulation of the controls, however there are some very notable benefits from doing this:

  1. WPF is aware when values change, and so can update UI nicely (i.e. databinding)
  2. WPF can update values safely (e.g. due to animation) and can choose to pause updates onto an object
  3. WPF can optimise storage of these values (e.g. by only storing values which differ to default)
  4. Values can cascade along the visual control tree, thus allowing styling, inherited values and so on

Overall they are a big win for WPF.  Here is a typical pattern for implementing a dependency property:

   1: public static readonly DependencyProperty FooProperty = 
   2:     DependencyProperty.Register("Foo", typeof(string), typeof(MyUserControl));
   3:  
   4: public string Foo
   5: { 
   6:     get { return (string)GetValue(FooProperty); } 
   7:     set { SetValue(FooProperty, value); } 
   8: } 


So how can we intercept property updates?

Obviously we can put code into the setter of the property … but given point 2 above (i.e. WPF will sometimes update the values directly without calling our object code) this will not cover all eventualities. And so the WPF team came up with a way of notifying our code when a value changes – the DependencyPropertyDescriptor.  You’ll often see samples around the web like this:

   1: DependencyPropertyDescriptor dpd = 
   2:     DependencyPropertyDescriptor.FromProperty(MyUserControl.FooProperty, 
   3:                                               typeof(MyUserControl)));
   4: if (dpd != null)
   5: {
   6:     dpd.AddValueChanged(ControlInstance, delegate
   7:     {
   8:         // Add property change logic.
   9:     });
  10: }


and this will now leak memory.

And where is the memory leak?

Whenever you attach an object onto an event of another object you introduce the potential for a memory leak.  This WPF is fundamentally no different.  The use of the AddValueChanged method has introduced a memory leak, since the object containing our code is now referenced by the dependency property and so will never get garbage collected.

So we need to remove the event handler when …

Exactly … when should we remove the event handler?  Event handlers are typically removed as part of the Dispose pattern, and then everyone tries really hard to make sure views and other objects get disposed properly.  WPF gives us another, much cleaner and more reliable option:

   1: private static readonly DependencyProperty FooPropertyDescriptor = 
   2:     DependencyProperty.RegisterAttached("FooDescriptor", 
   3:                                         typeof (DependencyPropertyDescriptor), 
   4:                                         typeof (MyUserControl));
   5:  
   6: private void AttachEventHandlers() 
   7: { 
   8:     DependencyPropertyDescriptor dpd = 
   9:        DependencyPropertyDescriptor.FromProperty(GridViewColumn.WidthProperty, 
  10:                                                  typeof (GridViewColumn)); 
  11:     if(dpd != null) 
  12:     { 
  13:         dpd.AddValueChanged(source, OnFooChanged); 
  14:         source.SetValue(FooPropertyDescriptor, dpd); 
  15:     } 
  16: }
  17:  
  18: private void OnFooChanged(object sender, EventArgs arg) 
  19: { 
  20:     var source = (DependencyObject)sender; 
  21:     if (!BindingOperations.IsDataBound(source, FooProperty)) 
  22:     { 
  23:         // We are no longer data-bound, so we need to remove the ValueChanged 
  24:         //    event listener to avoid a memory leak 
  25:         var dpd = (DependencyPropertyDescriptor)source.GetValue(FooPropertyDescriptor); 
  26:         if(dpd != null) 
  27:         { 
  28:             dpd.RemoveValueChanged(source, OnFooChanged); 
  29:             source.SetValue(FooPropertyDescriptor, null); 
  30:         } 
  31:         return; 
  32:     }
  33:  
  34:     // Add property change logic 
  35: }
  36:  


Explanation:

  1. We register a private DependencyProperty of type DependencyPropertyDescriptor
  2. When we register the value changed callback, we set the value of this private dependency property
  3. Whenever the value changed callback is called, then we check BindingOperations.IsDataBound.  If this returns false then we use the cached DependencyPropertyDescriptor to deregister the value changed callback.

Result:

  1. No more memory leaks

Credits and references:

A big part of this technique comes from:

http://wpfglue.wordpress.com/2009/12/03/forwarding-the-result-of-wpf-validation-in-mvvm/

For a more detailed primer on dependency properties check out:

http://compilewith.net/2007/08/wpf-dependency-properties.html

March 19 2010
blog comments powered by Disqus