Wednesday, April 23, 2008

Generic INotifyPropertyChanged Setter

Those of you who have ever implemented the INotifyPropertyChanged interface to enable databinding in your domain objects will have noticed how the notify code repeats over and over and over again in your property setters. This sure feels like breaking the DRY principle, so it is time to simplify and remove the repeating code using reflection and generics:

protected void SetValueWithChangeNotification<T>(ref T oldValue, T newValue)
{
StackFrame stackFrame = new StackFrame(1);
string stackName = stackFrame.GetMethod().Name;
string propertyName = stackName.Substring(4); //starts with "set_"
//PropertyInfo property = this.GetType().GetProperty(propertyName);

//T oldValue = (T)property.GetValue(this, null);

if (oldValue.Equals(newValue)==false)
{

oldValue = newValue;
RaisePropertyChanged(propertyName);
}

}

Call the method from your property setters like this:

public decimal NominatedValue
{
get { return _nominatedValue; }
set { SetValueWithChangeNotification(ref _nominatedValue, value);}
}

Note that while it is possible to use property.GetValue(this, null) to get the old value, you cannot use property.SetValue(this, newValue, null) to set it as it would cause a infinite recursive call chain.


The private property member is set before raising the property changed event, just to be sure. WinForms UI code runs on a single thread, so no event subscribers should be able to handle the event until the code returns, but this way even multi-threaded code should work fine.

You can always implement the method in a base class if you like, or in a static utility method by passing the object instance as an extra parameter.

4 comments:

Gøran Kvarv said...

Nice!

Bart said...

Nice one, but don't you think the StackFrame-creation can give you a rather big performance hit ?

Kjell-Sverre Jerijærvi said...

I have no before/after performance numbers - but at least developer productivity performance is up :)

And afterall, this is only in the WinForms client in our project - one man, one machine, multi-CPU!

Miguel Madero said...

Interesting approbach, I dont thinkg the performance hit would be huge, but probably AOP could do some nice tricks here to avoind runtime reflection.

What about something like:

[NotifiablePropertyAttribute]
public string MyProperty{get;set;}