INotifyPropertyChanged pattern wrapper using LINQ Expressions for Silverlight / MVVM scenarios


Here's a little time-saver when writing  any kind of notifying property, namely MVVM ViewModel classes for Silverlight, or anything implementing the INotifyPropertyChanged interface. The problem is that its implementation pattern is very strict and verbose, hence being a prime candidate for code reuse or copypasta.

INotifyPropertyChange pattern implemented manually

    public class MainPageViewModel : INotifyPropertyChanged
    {
        private bool _isOldAndBusted;
        public bool IsOldAndBusted
        {
            get { return _isOldAndBusted; }
            set
            {
                if (_isOldAndBusted != value)
                {
                    _isOldAndBusted = value;
                    PropertyChanged(this,new PropertyChangedEventArgs("IsOldAndBusted"));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }


Note that this pattern should be used for each property in a type that supports notification. That's a bit verbose for such a common pattern, and this has a very nasty side-effect, as the string identifying the property in the argument must match the property name under pain of runtime exception. This is going to be an issue with :
  1. Typos : We're all lazy / tired at some point and more often than not "Property" will end-up being typed "Proprety".
  2. Refactoring : Refactoring tools can change the name of a property and even sometimes change string bearing the same name, but the process needs to be checked as some tools will rename the string in totally inrelevant places, thus requiring precious developer attention-span
  3. Me : I'm a typo-prone lazy developer with the attention span of a gerbil and I have a sanctified hatred of copypasta code.
So in order to foolproof notifying properties, as well as save some precious keystrokes that will, over the years, greatly favor the lifespan of your keyboard and fingers, let's find how we can use a typesafe way of extracting a property name, and automagically encapsulate the check/assign/raise pattern of INotifyPropertyChange implementations.





A typesafe way of extracting a property name 
Lambdas and LINQ Expressions to the rescue!

Lambda expressions are typesafe and although they are defined at compile time, the C# compiler can also express them as a SAT which the runtime might compile/evaluate. Analyzing this SAT will yield the name of the sought property.

Encapsulating the check/affect/raise pattern

The check part is easy, as we will have to analyze the type at runtime to find if it either implements IEquatable or IComparable before falling back to good ol' Object.Equals.
Affect is not too difficult as we'll use a wrapper around our value.
The real issue is raising the event. Let's have a look at the signature for INotifyPropertyChanged.PropertyChanged:

public interface INotifyPropertyChanged { event PropertyChangedEventHandler PropertyChanged; }

The key part here is the event modifier which implies that PropertyChangedEventhandler delegate can be subscribed by any client, but the delegate can only be invoked by an instance of the enclosing type. That means we'll have a fun time accessing this delegate if we want to raise it automagically.
Once again, Lambda and Expression trees come to the rescue, as we can nicely ask the enclosing type to return the delegate, then raise it, rather than attempt to access the event from outside.

Theorically speaking, we shouldn't be allowed to do this in C# with just a lambda as the following :

Func<INotifyPropertyChanged, PropertyChangedEventHandler> lambda = x => x.PropertyChanged; // error CS0079: The event 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged' can only appear on the left hand side of += or -=

But keep in mind that C# is just a subset of the CIL, so our compiled lambda is constrained by C# at compile time, not at runtime where it's just CIL fed to the runtime, and thanks to CIL being much more flexible and tje CLR not enforcing the Event access check at runtime, we're going to trick the C# compiler into thinking we're accessing the event from within the enclosing type, not through the wrapper by creating the lambda in a static initializer context belonging to the enclosing type, and feeding it to the wrapper as a delegate.

Trust me, it's cool, dude.

Introducing NotifyingField<TVal>

Here's the wrapper class that will do the heavy lifting for us, with a sample use and some unit tests. Note that this has been tested with VS2010 RC2 under Silverlight 3.

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace OneKStrongOxen.Silverlight.Helpers
{
    public class NotifyingField<TVal, TOwner> where TOwner : INotifyPropertyChanged
    {
        // Equality operator that defines whether the notification needs to fire
        private static readonly Func<TVal, TVal, bool> EqualityOperator;

        // Delegate tasked with extracting the PropertyChanged EVENT. Event can only be raised by the enclosing type
        private static Func<TOwner, PropertyChangedEventHandler> _notifierExtractor;

        // Field name
        private readonly string _name;

        private TVal _value;

        // The static constructor will try to find a way to equate old and new values of the field
        // Note that value types will always be boxed, but it's a small price to pay
        // as property change notifications should only happen at a low frequency as they're mostly view driven
        static NotifyingField()
        {
            if (typeof (IEquatable<TVal>).IsAssignableFrom(typeof (TVal)))
            {
                EqualityOperator = (a, b) => ((IEquatable<TVal>) a).Equals(b);
            }
            else if (typeof (IComparable<TVal>).IsAssignableFrom(typeof (TVal)))
            {
                EqualityOperator = (a, b) => ((IComparable<TVal>) a).CompareTo(b) == 0;
            }
            else
            {
                EqualityOperator = (a, b) => Equals(a, b); // ye old fallback to object
            }
        }

        /// <summary>
        /// Create the backing field
        /// </summary>
        /// <param name="property"></param>
        /// <param name="notifier"></param>
        public NotifyingField(
            Expression<Func<TOwner, TVal>> property,
            Func<TOwner, PropertyChangedEventHandler> notifier
            )
        {
            var mbe = property as LambdaExpression;
            if (mbe == null || mbe.Body.NodeType != ExpressionType.MemberAccess)
            {
                throw new ArgumentException("Only property setters are allowed", "property");
            }
            _name = ((MemberExpression) mbe.Body).Member.Name;

            // Steal the event handler so we can invoke it even though we're not the enclosing type
            _notifierExtractor = notifier;
        }

        // Setter shortcut as it's nicer to have _field[this]=value rathern than _field.Set(value,this)
        // Encapsulates the property notifier pattern as well as the equality check guard
        // syntactic sugar will rot your keyboard, so remember to wash your IDE 3 times a day       
        public TVal this[TOwner owner]
        {
            set
            {
                if (!EqualityOperator(_value, value)) // don't raise the property twice
                {
                    _value = value;
                    _notifierExtractor(owner)(owner, new PropertyChangedEventArgs(_name));
                }
            }
        }

        // Redefine the implicit conversion operator
        public static implicit operator TVal(NotifyingField<TVal, TOwner> p)
        {
            return p._value;
        }
    }


    public class HipNotifier : INotifyPropertyChanged
    {
        private readonly NotifyingField<bool, HipNotifier> _isActive =
            new NotifyingField<bool, HipNotifier>(x => x.IsActive, x => x.PropertyChanged);

        public bool IsActive
        {
            get { return _isActive; }
            set { _isActive[this] = value; } // rather than _isActive.Set(value,this)
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    [TestClass]
    public class ActionnerTestCase
    {
        [TestMethod]
        public void TestCreateFireSequence()
        {
            var not = new HipNotifier();
            bool isSet = false;
            string name = null;
            not.PropertyChanged += (sender, args) =>
                                       {
                                           Console.WriteLine("Change: src:{0} on {1}", sender, args.PropertyName);
                                           isSet = true;
                                           name = args.PropertyName;
                                       };

            Assert.IsFalse(not.IsActive, "Initial state");
            not.IsActive = true;
            Assert.IsTrue(not.IsActive, "Altered state");
            Assert.IsTrue(isSet, "Event did not fire");
            Assert.AreEqual("IsActive",name,"Property name incorrect");
        }
    }
}


This is particularly useful for Silverlight ViewModel classes that need to raise ten thousand different events. The Views are already taken care of thanks to the dependecy properties, but most of the time we're going to write ViewModels and usercontrols rather than views, so enjoy and please let me know if there's any issue!

No comments:

Post a Comment

Please leave your comments in English or French and I will be pleased to answer them if you have any questions.

Spammers will be walked down the plank matey. Arrr!