Aug 172009
 

When working with Silverlight two-way binding (and not only there) you need to implement the INotifyPropertyChanged interface in order to notify any observers that the property values of the object have changed.

The INotifyChangedProperty exposes only the PropertyChanged event which is used by observers to subscribe to the change notifications. The typical pattern is to have a base class which will fire the events like this:

   1: public abstract class PropertyChangedNotifier : INotifyPropertyChanged

   2:    {

   3:        protected virtual void OnPropertyChanged(string propertyName)

   4:        {

   5:            var eventHandler = PropertyChanged;

   6:            if (eventHandler != null)

   7:            {

   8:                eventHandler(this, new PropertyChangedEventArgs(propertyName));

   9:            }

  10:        }

  11: }

This can be used like this:

   1: public class MyClass

   2: {

   3:     private string myProperty;

   4:     public string MyProperty

   5:     {

   6:         get { return myProperty; }

   7:         set

   8:         {

   9:             if(myProperty != value)

  10:             {

  11:                 myProperty = value;

  12:                 OnPropertyChanged("MyProperty");

  13:             }

  14:         }

  15:     }

  16: }

For small projects, this implementation is ok, but on large scale projects, using such an implementation will lead to a maintenance nightmare, especially when performing property rename refactorings. After each refactoring, you need to manually change the name of the property inside the setter.

Type safe implementation using Expression Trees

.NET 3.5 comes with the ability of navigating through the object hierarchy using Expression trees. This is used for example in LINQ providers for dynamically building the SQL queries. The same Expression trees can be used in this case for building a type safe solution for the OnPropertyChanged method:

   1: public abstract class PropertyChangedNotifier : INotifyPropertyChanged

   2: {

   3:    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> action)

   4:    {

   5:        var propertyName = GetPropertyName<T>(action);

   6:        OnPropertyChanged(propertyName);

   7:    }

   8:

   9:    protected static string GetPropertyName<T>(Expression<Func<T>> action)

  10:    {

  11:        var expression = (MemberExpression)action.Body;

  12:        var propertyName = expression.Member.Name;

  13:        return propertyName;

  14:    }

  15:

  16:    public event PropertyChangedEventHandler PropertyChanged;

  17: }

Coming back to the class example above, the new type safe implementation can be used as below:

   1: public class MyClass

   2: {

   3:     private string myProperty;

   4:     public string MyProperty

   5:     {

   6:         get { return myProperty; }

   7:         set

   8:         {

   9:             if(myProperty != value)

  10:             {

  11:                 myProperty = value;

  12:                 OnPropertyChanged(()=>this.MyProperty);

  13:             }

  14:         }

  15:     }

  16: }

Conclusion

In this blog article I have presented a way of implementing a type-safe notifier for the INotifyPropertyChanged interface. The implementation presented here uses expression trees for achieving this.

I haven’t tested the performance overhead of using expression trees, but I guess if this is an issue, you can easily cache the results for further usage.

  • http://Website What is the run-time cost?

    I am worried about the run-time cost. If you have many property changed notifications, you will use a lot of Lambda expressions and instantiate lots of Expression<Func>. How will the garbage collector react to creating and destruction of all these small objecs. Using SilverLight on WindowsPhone the garbage collector will halt everything and collect when needed.

    • Sebastian Negomireanu

      Well I am using this mechanism in several large projects (WPF and full Silverlight framework) and so far I could not see any visible performance penalty (also the profiler did not report these expressions as hotspots). Since the expressions are used in a short scope, they won’t reach Gen 1 or Gen 2 so they will be quickly collected.