Friday, August 26, 2011

C#: A better PropertyChanged

If you have to implement and use IPropertyChanged in your code then you might feel annoyed by all the code that must be written to support the PropertyChanged event:
public class Product : INotifyPropertyChanged
{
 public event PropertyChangedEventHandler PropertyChanged;

 private int _productId;
 public int ProductId
 {
  get { return _productId; }
  set
  {
   _productId = value;
   RaisePropertyChanged("ProductId");
  }
 }

 private string _name;
 public string Name
 {
  get { return _name; }
  set
  {
   _name = value;
   RaisePropertyChanged("Name");
  }
 }

 protected void RaisePropertyChanged(string propertyName)
 {
  if (PropertyChanged != null)
   PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 } 
}
Not only is this code trivial and repetitive but since the property name is passed as a string then it can easily lead to typos. Luckily there is a better way to do this:
public class Product : PropertyChangedBase<Product>
{
 public int ProductId
 {
  get { return GetPropertyValue(x => x.ProductId); }
  set { SetPropertyValue(x => x.ProductId, value); }
 }

 public string Name
 {
  get { return GetPropertyValue(x => x.Name); }
  set { SetPropertyValue(x => x.Name, value); }
 }
}
PropertyChangedBase:
public class PropertyChangedBase<T> : INotifyPropertyChanged
{
 private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
 public event PropertyChangedEventHandler PropertyChanged;

 protected TProp GetPropertyValue<TProp>(Expression<Func<T, TProp>> property)
 {
  var name = GetPropertyName(property);
  return (TProp) _properties[name];
 }

 protected void SetPropertyValue<TProp>(Expression<Func<T, TProp>> property, object value)
 {
  var name = GetPropertyName(property);
  _properties[name] = value;
  RaisePropertyChanged(name);
 }

 protected void RaisePropertyChanged(string propertyName)
 {
  if (PropertyChanged != null)
   PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 }

 private string GetPropertyName<TProp>(Expression<Func<T, TProp>> property)
 {
  return (property.Body as MemberExpression).Member.Name;
 }
}