How can I fix the DependencyPropertyDescriptor AddValueChanged Memory Leak on AttachedBehavior? How can I fix the DependencyPropertyDescriptor AddValueChanged Memory Leak on AttachedBehavior? wpf wpf

How can I fix the DependencyPropertyDescriptor AddValueChanged Memory Leak on AttachedBehavior?


AddValueChanged of dependency property descriptor results in memory leak as you already know. So, as described here, you can create custom class PropertyChangeNotifier to listen to any dependency property changes.

Complete implementation can be found here - PropertyDescriptor AddValueChanged Alternative.


Quote from the link:

This class takes advantage of the fact that bindings use weak references to manage associations so the class will not root the object who property changes it is watching. It also uses a WeakReference to maintain a reference to the object whose property it is watching without rooting that object. In this way, you can maintain a collection of these objects so that you can unhook the property change later without worrying about that collection rooting the object whose values you are watching.

Also for sake of completeness of answer I am posting complete code here to avoid any rot issue in future.

public sealed class PropertyChangeNotifier : DependencyObject, IDisposable{    #region Member Variables    private readonly WeakReference _propertySource;    #endregion // Member Variables    #region Constructor    public PropertyChangeNotifier(DependencyObject propertySource, string path)        : this(propertySource, new PropertyPath(path))    {    }    public PropertyChangeNotifier(DependencyObject propertySource, DependencyProperty property)        : this(propertySource, new PropertyPath(property))    {    }    public PropertyChangeNotifier(DependencyObject propertySource, PropertyPath property)    {        if (null == propertySource)            throw new ArgumentNullException("propertySource");        if (null == property)            throw new ArgumentNullException("property");        _propertySource = new WeakReference(propertySource);        Binding binding = new Binding        {            Path = property,             Mode = BindingMode.OneWay,             Source = propertySource        };        BindingOperations.SetBinding(this, ValueProperty, binding);    }    #endregion // Constructor    #region PropertySource    public DependencyObject PropertySource    {        get        {            try            {                // note, it is possible that accessing the target property                // will result in an exception so i’ve wrapped this check                // in a try catch                return _propertySource.IsAlive                ? _propertySource.Target as DependencyObject                : null;            }            catch            {                return null;            }        }    }    #endregion // PropertySource    #region Value    /// <summary>    /// Identifies the <see cref="Value"/> dependency property    /// </summary>    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",    typeof(object), typeof(PropertyChangeNotifier), new FrameworkPropertyMetadata(null, OnPropertyChanged));    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)    {        PropertyChangeNotifier notifier = (PropertyChangeNotifier)d;        if (null != notifier.ValueChanged)            notifier.ValueChanged(notifier, EventArgs.Empty);    }    /// <summary>    /// Returns/sets the value of the property    /// </summary>    /// <seealso cref="ValueProperty"/>    [Description("Returns/sets the value of the property")]    [Category("Behavior")]    [Bindable(true)]    public object Value    {        get        {            return GetValue(ValueProperty);        }        set        {            SetValue(ValueProperty, value);        }    }    #endregion //Value    #region Events    public event EventHandler ValueChanged;    #endregion // Events    #region IDisposable Members    public void Dispose()    {        BindingOperations.ClearBinding(this, ValueProperty);    }    #endregion}


A more lightweight solution for FrameworkElements and FrameworkContentElements is to subscribe to the Unloaded event and remove the handler. This requires a non-anonymous delegate (UpdateAdorner in that case) though:

focusProp.AddValueChanged(m_TextBox, UpdateAdorner);m_TextBox.Unloaded += (sender, args) => focusProp.RemoveValueChanged(sender, UpdateAdorner);