Click here to Skip to main content
Click here to Skip to main content

Property Change Notification using a Weak Referencing Strategy

, 29 Aug 2009
Rate this:
Please Sign up or sign in to vote.
FeaturesDesktop and Silverlight CLR compatibilityCapability to perform assignment and raise appropriate events before and after assignment.Weak referencedProvides for both expression tree and loosely typed stringsUses extended EventArgs to supply before and after valuesExtended PropertyChang

Features

  • Desktop and Silverlight CLR compatibility
  • Capability to perform assignment and raise appropriate events before and after assignment.
  • Weak referenced
  • Provides for both expression tree and loosely typed strings
  • Uses extended EventArgs to supply before and after values
  • Extended PropertyChangingEventArgs for cancellable changes
  • Configurable to use caching of EventArgs to decrease heap fragmentation
  • Comes with unit tests for Desktop and Silverlight CLRs

Introduction

INotifyPropertyChanged is a ubiquitous part of Silverlight and WPF programming. It is used extensively in WPF and Silverlight to enable non-DependencyObjects to signal that a bound value is out of date. I’ve seen many approaches that have been used in order to remove the property name string requirement. Some have employed lambda expressions, or walking the stack, while others have used generated proxies or AOP point cuts. This post and the accompanying code are not so much about ridding us from the loosely typed string, although I do provide the means if you don’t mind a performance hit. Today there is another code smell that I would like to address, and it is the use of base classes for property change notification.

In this post I will demonstrate how we are able to encapsulate two property change interface implementations (INotifyPropertyChanged and INotifyPropertyChanging) in a reusable class, and in a weak referencing manner to avoid any possible leakage.

Using a base class implementation for INotifyPropertyChanged has never sat easy with me. It probably goes back to early 2003 after reading Juval Lowy’s landmark book Programming .NET Components. The principal of favouring aggregation over inheritance is a driver for creating shallow class hierarchies and maintainable components. It is a principal that offers many advantages, and one that I strongly recommend.

Using the Library

DanielVaughan.ComponentModel .PropertyChangeNotifier is the main class. You use it by creating a field in your owner class, and instanciating it within the owner’s constructor. We apply the boiler plate code, which consists of a ‘flow-through’ interface implementation for either or both INotifyPropertyChanged and INotifyPropertyChanging.

An example is shown in the following excerpt.

<!-- code formatted by http://manoli.net/csharpformat/ -->
<span class="kwrd">class</span> MockNotifyPropertyChanged : INotifyPropertyChanged, INotifyPropertyChanging
{
    <span class="kwrd">readonly</span> PropertyChangeNotifier notifier;

    <span class="preproc">#region</span> INotifyPropertyChanged Implementation
    <span class="kwrd">public</span> <span class="kwrd">event</span> PropertyChangedEventHandler PropertyChanged
    {
        add
        {
            notifier.PropertyChanged += <span class="kwrd">value</span>;
        }
        remove
        {
            notifier.PropertyChanged -= <span class="kwrd">value</span>;
        }
    }
    <span class="preproc">#endregion</span>

    <span class="preproc">#region</span> INotifyPropertyChanging Implementation
    <span class="kwrd">public</span> <span class="kwrd">event</span> PropertyChangingEventHandler PropertyChanging
    {
        add
        {
            notifier.PropertyChanging += <span class="kwrd">value</span>;
        }
        remove
        {
            notifier.PropertyChanging -= <span class="kwrd">value</span>;
        }
    }
    <span class="preproc">#endregion</span>

    <span class="kwrd">public</span> MockNotifyPropertyChanged()
    {
        notifier = <span class="kwrd">new</span> PropertyChangeNotifier(<span class="kwrd">this</span>);
    }

    <span class="kwrd">int</span> int1;

    <span class="kwrd">public</span> <span class="kwrd">int</span> TestPropertyAssigned
    {
        get
        {
            <span class="kwrd">return</span> int1;
        }
        set
        {
            notifier.Assign(
                <span class="str">"TestPropertyAssigned"</span>, <span class="kwrd">ref</span> int1, <span class="kwrd">value</span>);
        }
    }

    <span class="kwrd">string</span> string1;

    <span class="kwrd">public</span> <span class="kwrd">string</span> TestPropertyAssignedLambda
    {
        get
        {
            <span class="kwrd">return</span> string1;
        }
        set
        {
            notifier.Assign(
                (MockNotifyPropertyChanged mock) => mock.TestPropertyAssignedLambda, 
                <span class="kwrd">ref</span> string1, <span class="kwrd">value</span>);
        }
    }    
}

The two property examples shown, delegate the task of assigning the property to the PropertyChangeNotifier. This provides the PropertyChangeNotifier with the opportunity to raise the PropertyChanging event of the INotifyPropertyChanging interface, perform the assignment (or return if a handler called Cancel() on the EventArgs, then raise the PropertyChangedEvent from the INotifyPropertyChanged interface.

Implementation

The following excerpt is taken from the PropertyChangeNotifier class. It contains both Silverlight and Desktop CLR specific sections.

<!-- code formatted by http://manoli.net/csharpformat/ -->
<span class="rem">/// <summary></span>
<span class="rem">/// This class provides an implementation of the <see cref="INotifyPropertyChanged"/></span>
<span class="rem">/// and <see cref="INotifyPropertyChanging"/> interfaces. </span>
<span class="rem">/// Extended <see cref="PropertyChangedEventArgs"/> and <see cref="PropertyChangingEventArgs"/></span>
<span class="rem">/// are used to provides the old value and new value for the property. </span>
<span class="rem">/// <seealso cref="PropertyChangedEventArgs{TProperty}"/></span>
<span class="rem">/// <seealso cref="PropertyChangingEventArgs{TProperty}"/></span>
<span class="rem">/// </summary></span>
<span class="preproc">#if</span> !SILVERLIGHT
[Serializable]
<span class="preproc">#endif</span>
<span class="kwrd">public</span> <span class="kwrd">sealed</span> <span class="kwrd">class</span> PropertyChangeNotifier : INotifyPropertyChanged, INotifyPropertyChanging
{
    <span class="kwrd">readonly</span> WeakReference ownerWeakReference;

    <span class="rem">/// <summary></span>
    <span class="rem">/// Gets the owner for testing purposes.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <value>The owner.</value></span>
    <span class="kwrd">internal</span> <span class="kwrd">object</span> Owner
    {
        get
        {
            <span class="kwrd">if</span> (ownerWeakReference.Target != <span class="kwrd">null</span>)
            {
                <span class="kwrd">return</span> ownerWeakReference.Target;
            }
            <span class="kwrd">return</span> <span class="kwrd">null</span>;
        }
    }

    <span class="rem">/// <summary></span>
    <span class="rem">/// Initializes a new instance </span>
    <span class="rem">/// of the <see cref="PropertyChangeNotifier"/> class.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <param name="owner">The intended sender </span>
    <span class="rem">/// of the <code>PropertyChanged</code> event.</param></span>
    <span class="kwrd">public</span> PropertyChangeNotifier(<span class="kwrd">object</span> owner) : <span class="kwrd">this</span>(owner, <span class="kwrd">true</span>)
    {
    }

    <span class="rem">/// <summary></span>
    <span class="rem">/// Initializes a new instance </span>
    <span class="rem">/// of the <see cref="PropertyChangeNotifier"/> class.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <param name="owner">The intended sender </span>
    <span class="rem">/// <param name="useExtendedEventArgs">If <c>true</c> the</span>
    <span class="rem">/// generic <see cref="PropertyChangedEventArgs{TProperty}"/></span>
    <span class="rem">/// and <see cref="PropertyChangingEventArgs{TProperty}"/> </span>
    <span class="rem">/// are used when raising events. </span>
    <span class="rem">/// Otherwise, the non-generic types are used, and they are cached </span>
    <span class="rem">/// to decrease heap fragmentation.</param></span>
    <span class="rem">/// of the <code>PropertyChanged</code> event.</param></span>
    <span class="kwrd">public</span> PropertyChangeNotifier(<span class="kwrd">object</span> owner, <span class="kwrd">bool</span> useExtendedEventArgs)
    {
        ArgumentValidator.AssertNotNull(owner, <span class="str">"owner"</span>);
        ownerWeakReference = <span class="kwrd">new</span> WeakReference(owner);
        <span class="kwrd">this</span>.useExtendedEventArgs = useExtendedEventArgs;
    }

    <span class="preproc">#region</span> <span class="kwrd">event</span> PropertyChanged

<span class="preproc">#if</span> !SILVERLIGHT
    [field: NonSerialized]
<span class="preproc">#endif</span>
    <span class="kwrd">event</span> PropertyChangedEventHandler propertyChanged;

    <span class="rem">/// <summary></span>
    <span class="rem">/// Occurs when a property value changes.</span>
    <span class="rem">/// </summary></span>
    <span class="kwrd">public</span> <span class="kwrd">event</span> PropertyChangedEventHandler PropertyChanged
    {
        add
        {
            <span class="kwrd">if</span> (OwnerDisposed)
            {
                <span class="kwrd">return</span>;
            }
            propertyChanged += <span class="kwrd">value</span>;
        }
        remove
        {
            <span class="kwrd">if</span> (OwnerDisposed)
            {
                <span class="kwrd">return</span>;
            }
            propertyChanged -= <span class="kwrd">value</span>;
        }
    }

    <span class="rem">/// <summary></span>
    <span class="rem">/// Raises the <see cref="E:PropertyChanged"/> event.</span>
    <span class="rem">/// If the owner has been GC'd then the event will not be raised.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> </span>
    <span class="rem">/// instance containing the event data.</param></span>
    <span class="kwrd">void</span> OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var owner = ownerWeakReference.Target;
        <span class="kwrd">if</span> (owner != <span class="kwrd">null</span> && propertyChanged != <span class="kwrd">null</span>)
        {
            propertyChanged(owner, e);
        }
    }

    <span class="preproc">#endregion</span>

    <span class="rem">/// <summary></span>
    <span class="rem">/// Assigns the specified newValue to the specified property</span>
    <span class="rem">/// and then notifies listeners that the property has changed.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <typeparam name="TProperty">The type of the property.</typeparam></span>
    <span class="rem">/// <param name="propertyName">Name of the property. Can not be null.</param></span>
    <span class="rem">/// <param name="property">A reference to the property that is to be assigned.</param></span>
    <span class="rem">/// <param name="newValue">The value to assign the property.</param></span>
    <span class="rem">/// <exception cref="ArgumentNullException"></span>
    <span class="rem">/// Occurs if the specified propertyName is <code>null</code>.</exception></span>
    <span class="rem">/// <exception cref="ArgumentException"></span>
    <span class="rem">/// Occurs if the specified propertyName is an empty string.</exception></span>
    <span class="kwrd">public</span> <span class="kwrd">void</span> Assign<TProperty>(
        <span class="kwrd">string</span> propertyName, <span class="kwrd">ref</span> TProperty property, TProperty newValue)
    {
        <span class="kwrd">if</span> (OwnerDisposed)
        {
            <span class="kwrd">return</span>;
        }

        ArgumentValidator.AssertNotNullOrEmpty(propertyName, <span class="str">"propertyName"</span>);
        ValidatePropertyName(propertyName);

        AssignWithNotificationAux(propertyName, <span class="kwrd">ref</span> property, newValue);
    }

    <span class="rem">/// <summary></span>
    <span class="rem">/// Slow. Not recommended.</span>
    <span class="rem">/// Assigns the specified newValue to the specified property</span>
    <span class="rem">/// and then notifies listeners that the property has changed.</span>
    <span class="rem">/// Assignment nor notification will occur if the specified</span>
    <span class="rem">/// property and newValue are equal. </span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <typeparam name="T"></typeparam></span>
    <span class="rem">/// <typeparam name="TProperty">The type of the property.</typeparam></span>
    <span class="rem">/// <param name="expression">The expression that is used to derive the property name.</span>
    <span class="rem">/// Should not be <code>null</code>.</param></span>
    <span class="rem">/// <param name="property">A reference to the property that is to be assigned.</param></span>
    <span class="rem">/// <param name="newValue">The value to assign the property.</param></span>
    <span class="rem">/// <exception cref="ArgumentNullException"></span>
    <span class="rem">/// Occurs if the specified propertyName is <code>null</code>.</exception></span>
    <span class="rem">/// <exception cref="ArgumentException"></span>
    <span class="rem">/// Occurs if the specified propertyName is an empty string.</exception></span>
    <span class="kwrd">public</span> <span class="kwrd">void</span> Assign<T, TProperty>(
        Expression<Func<T, TProperty>>

 

I’ve extended the PropertyChangedEventArgs and the PropertyChangingEventArgs to include before and after values. The following excerpt shows the PropertyChangedEventArgs.

<!-- code formatted by http://manoli.net/csharpformat/ -->
<span class="rem">/// <summary></span>
<span class="rem">/// Provides data for the <see cref="INotifyPropertyChanged.PropertyChanged"/> event,</span>
<span class="rem">/// exposed via the <see cref="PropertyChangeNotifier"/>.</span>
<span class="rem">/// </summary></span>
<span class="rem">/// <typeparam name="TProperty">The type of the property.</typeparam></span>
<span class="kwrd">public</span> <span class="kwrd">sealed</span> <span class="kwrd">class</span> PropertyChangedEventArgs<TProperty> : PropertyChangedEventArgs
{
    <span class="rem">/// <summary></span>
    <span class="rem">/// Gets the value of the property before it was changed.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <value>The old value.</value></span>
    <span class="kwrd">public</span> TProperty OldValue { get; <span class="kwrd">private</span> set; }
        <span class="rem">/// <summary></span>
    <span class="rem">/// Gets the new value of the property after it was changed.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <value>The new value.</value></span>
    <span class="kwrd">public</span> TProperty NewValue { get; <span class="kwrd">private</span> set; }
        <span class="rem">/// <summary></span>
    <span class="rem">/// Initializes a new instance </span>
    <span class="rem">/// of the <see cref="PropertyChangedEventArgs{TProperty}"/> class.</span>
    <span class="rem">/// </summary></span>
    <span class="rem">/// <param name="propertyName">Name of the property that changed.</param></span>
    <span class="rem">/// <param name="oldValue">The old value before the change occured.</param></span>
    <span class="rem">/// <param name="newValue">The new value after the change occured.</param></span>
    <span class="kwrd">internal</span> PropertyChangedEventArgs(
        <span class="kwrd">string</span> propertyName, TProperty oldValue, TProperty newValue) 
        : <span class="kwrd">base</span>(propertyName)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }
}

INotifyPropertyChanging doesn’t exist in Silverlight, so I’ve implemented it.

In order to turn of the extended EventArgs, pass an extra argument to the constructor. By turning of the extended EventArgs, we enable to arg caching feature. I implemented this after reading Josh Smith’s nice articles on the subject.

The PropertyChangeNotifier retains a link to the Owner using a WeakReference. Each time a change is handled, the PropertyChangeNotifier checks to see if the Owner is still alive. If it isn’t, the PropertyChangeNotifier will remove all event handlers.

Unit Tests

The download includes various unit tests for both the Desktop and Silverlight environments. I recommend examining them, to further your understanding about how it all works.

Figure: Desktop CLR test results.

 

Figure: Silverlight CLR test results.

 

Future Enhancements

  • Batch support
  • Event Suppression

Download source code for the Desktop and Silverlight CLRs: Core_01_01.zip (1.42 mb)

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)

About the Author

Daniel Vaughan
President Outcoder
Switzerland Switzerland
Daniel Vaughan is a Microsoft MVP and cofounder of Outcoder, a Swiss software and consulting company dedicated to creating best-of-breed user experiences and leading-edge back-end solutions, using the Microsoft stack of technologies--in particular WPF, WinRT, and Windows Phone.
 
Daniel is the author of Windows Phone 7.5 Unleashed and Windows Phone 8 Unleashed, both published by SAMS.
 
Daniel is also the creator of a number of open-source projects, including Calcium SDK, and Clog.
 
Would you like Daniel to bring value to your organisation? Please contact

Daniel's Blog | MVP profile | Follow on Twitter
 
Windows Phone Experts
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralUsage Example PinmemberEng. Jalal4-Nov-09 0:25 
GeneralRe: Usage Example PinmemberDaniel Vaughan4-Nov-09 4:16 
GeneralRe: Usage Example PinmemberEng. Jalal5-Nov-09 1:00 
GeneralRe: Usage Example PinmemberDaniel Vaughan5-Nov-09 1:09 
GeneralRe: Usage Example PinmemberEng. Jalal5-Nov-09 1:25 
GeneralRe: Usage Example PinmemberDaniel Vaughan5-Nov-09 1:34 
GeneralRe: Usage Example PinmemberDaniel Vaughan5-Nov-09 1:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140709.1 | Last Updated 29 Aug 2009
Article Copyright 2009 by Daniel Vaughan
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid