Monday, August 13, 2007

Easier managing of INotifyPropertyChanged

INotifyPropertyChanged is the core of WPF Data Binding. One thing that always pissed me off about INotifyPropertyChanged is that it would require me to pass the name of the property to PropertyChanged(). Well, in VS 2005, refactoring is made very simple. So simple, that if you change the name of the property, and then use the context menu to apply the changes to the entire project - it's done flawlessly. However, some complex data binding can cause you to call PropertyChanged for one property, when you set a completely different property! In those instances, you might 'lose track' of a property name change... What happens if the property name passed to PropertyChanged doesn't match that of the property itself? Data binding breaks!

The solution I came up with? Well, we need to write a helper function to get the name of the current property. To do this, we need to examine how properties work. If I have the property named 'MyProperty', it gets converted to accessors when I compile. 'public int MyProperty' becomes two methods, 'public int get_MyProperty()' and 'public void set_MyProperty(int value)'.

The System.Diagnostics.StackTrace class can be used to get the name of the calling method. So, what I did, was devise a function, that when called, obtains the name of the calling method, ensures that it is, in fact, a property (by testing for leading get_ or set_).

Now, I verify that it really IS a property (and not a method pretending to be a property by being named get_ or set_) by using Reflection. First, we get the type of the class by using this.GetType(). Then we get the property with the name we found earlier. If that property is equal to null, then it is NOT a property. Once I've verified that this thing really is a property, then I call PropertyChanged with the appropriate property name.

Then I wrap all of this up into a class, with static methods, for easier reusability. Code is below.

Note that when using reflection to verify that it is actually a property, there are some caveats. For instance, if you have two properties with the same name, one static, one instanced, you will raise an exception. Read the MSDN article for more information. If you cannot deal with those caveats, you could simply take out the call to IsProperty

1public class PropertyChanger
2{
3 public static void PropChanged(object callerobject, PropertyChangedEventHandler PropertyChanged)
4 {
5 string propertyname = new StackTrace().GetFrame(1).GetMethod().Name;
6 string accessor = propertyname.Substring(0, 4);
7 propertyname = propertyname.Substring(4);
8 if (((accessor == "set_") (accessor == "get_")) && (propertyname.Length > 0))
9 {
10 if (IsProperty(callerobject, propertyname))
11 PropertyChanged(callerobject, new PropertyChangedEventArgs(propertyname));
12 else
13 throw new Exception(propertyname + " is not a property.");
14 }
15 else
16 throw new Exception("Calling method is not a get or set accessor.");
17 }
18 private static bool IsProperty(object callerobject, string propertyname)
19 {
20 return callerobject.GetType().GetProperty(propertyname) != null;
21 }
22 }
23}


And to use this class, just call like this:

PropertyChanger.PropChanged(this, PropertyChanged);

where PropertyChanged is the name of your PropertyChanged is the name of your PropertyChanged event handler.

1 comments:

Jodi said...

Thanks for the share

But here between us... it would so easier(and way more performatic) if it was done with preprocessor macros, wouldn't it? :P