17 February 2009

Silverlight data binding the lazy way

Your typical business class utilizing data binding in Silverlight (and possibly WPF too, but I don't have any experience with that) look like this:
using System.ComponentModel;

namespace Dotnetbyexample
{
  public class Person : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    private string firstName;
    public string FirstName
    {
      get { return this.firstName; }
      set
      {
        this.firstName = value;
        this.NotifyPropertyChanged("FirstName");
      }
    }

    private string lastName;
    public string LastName
    {
      get { return this.lastName; }
      set
      {
        this.lastName = value;
        this.NotifyPropertyChanged("LastName");
      }
    }

    private string address;
    public string Address
    {
      get { return this.address; }
      set
      {
        this.address = value;
        this.NotifyPropertyChanged("Address");
      }
    }

    private string city;
    public string City
    {
      get { return this.city; }
      set
      {
        this.city = value;
        this.NotifyPropertyChanged("City");
      }
    }

    private string state;
    public string State
    {
      get { return this.state; }
      set
      {
        this.state = value;
        this.NotifyPropertyChanged("State");
      }
    }

    private string zip;
    public string Zip
    {
      get { return this.zip; }
      set
      {
        this.zip = value;
        this.NotifyPropertyChanged("Zip");
      }
    }

    public void NotifyPropertyChanged(string propertyName)
    {
      if (PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }
  }
}
Silverlight data binding rocks, for if you change the attribute, the listening GUI elements will update automatically, but what I particulary not like is the fact that the NotifyPropertyChanged method needs to be called with the name of the property as a string. Change the property name and the event does not work anymore. I created the following extension method
using System.ComponentModel;
using System.Diagnostics;

namespace Dotnetbyexample
{
  public static class INotifyPropertyChangedExtension
  {
    public static void NotifyPropertyChanged(
       this INotifyPropertyChanged npc, 
       PropertyChangedEventHandler PropertyChanged)
    {
      if (PropertyChanged != null)
      {
        string propertyName =
           new StackTrace().GetFrame(1).GetMethod().Name.Substring(4);

        PropertyChanged(npc, new PropertyChangedEventArgs(propertyName));
      }
    }
  }
}
which allows me to rewrite the business object as follows
using System.ComponentModel;

namespace Dotnetbyexample
{
  public class Person : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    private string firstName;
    public string FirstName
    {
      get { return this.firstName; }
      set
      {
        this.firstName = value;
        this.NotifyPropertyChanged(PropertyChanged);
      }
    }

    private string lastName;
    public string LastName
    {
      get { return this.lastName; }
      set
      {
        this.lastName = value;
        this.NotifyPropertyChanged(PropertyChanged);
      }
    }

    private string address;
    public string Address
    {
      get { return this.address; }
      set
      {
        this.address = value;
        this.NotifyPropertyChanged(PropertyChanged);
      }
    }

    private string city;
    public string City
    {
      get { return this.city; }
      set
      {
        this.city = value;
        this.NotifyPropertyChanged(PropertyChanged);
       }
    }

    private string state;
    public string State
    {
      get { return this.state; }
      set
      {
        this.state = value;
        this.NotifyPropertyChanged(PropertyChanged);
      }
    }

    private string zip;
    public string Zip
    {
      get { return this.zip; }
      set
      {
        this.zip = value;
        this.NotifyPropertyChanged(PropertyChanged);
      }
    }
  }
}
and presto: I got rid of the custom NotifyPropertyChanged method at the bottom of the class, and now the call to the extension method NotifyPropertyChanged is the same for every setter. No need to remember to change the string in the NotifyPropertyChanged when the property name changes - or when you copy the getter/setter from somewhere else ;-) Key to the whole trick is the quite unorthodox usage of the Stacktrace
new StackTrace().GetFrame(1).GetMethod().Name.Substring(4);
credits for this of course go to Rockford Lhotka with his CSLA framework - in the BusinessBase object of this framework I noticed this trick a year or so ago and suddenly I wondered if it would work in a Silverlight environment as well. Update: in the newest version of the CSLA framework the method using this trick -CanWriteProperty - is marked deprecated, and is replaced by a method CanWriteProperty(string) that takes the property name as an explicit string - so Lhotka is now swinging round to follow the PropertyChangedEventHandler.