WPF + INotifyPropertyChanged Proxy = Epic Fail!

08 May 2008 in download | dotnet | libs-and-frameworks |

It seemed a straightforward thing to do.

Sending property changed events when a property is changed is repetitive boilerplate code that can be factored out into an aspect of your system’s behaviour. This can be done with a proxy generator library like the Castle’s **DynamicProxy2 **(See my first attempts playing with such a framework here).

The idea goes as follows: Program an IInterceptor that every time a setter of the class encapsulated by the proxy is accessed raises the PropertyChanged event as defined by the interface mentioned in the title. The basic problem is that accessing a property from the outside is OK: But what if you change the property from the inside? A basic proxy that simply surrounds the class cannot register this action, hence no event is automatically raised…

The idea grew: Implement a class that is abstract. All properties defined on it are marked abstract. Then use DynamicProxy2 in the provided way that generates a proxy that inherits from your abstract class and in fact appears to “implement” the properties defined in the class. I am using quotes here, because there seems to be no implementation of the abstract properties available even once the proxy has been generated - don’t ask me, I’ll have to dig for details in the appropriate forum.

However, the Interceptor registers when the setter of such a property is accessed. Provided you do not “Proceed” to an underlying implementation (that may not exist), you can provide an implementation of the property within the Interceptor itself. This is quite easy and in my case I just based it on a Dictionary:

Dictionary<string, object> values = new Dictionary<string, object>(); … public void Intercept(IInvocation invocation) { … bool talkingToPropertySetter = invocation.Method.IsSpecialName && invocation.Method.Name.StartsWith(“set_”); bool talkingToPropertyGetter = … … if (talkingToPropertyGetter) { object output; values.TryGetValue(GetPropertyName(invocation.Method), out output); invocation.ReturnValue = output; } if (talkingToPropertySetter) { values[GetPropertyName(invocation.Method)] = invocation.Arguments[0]; RaisePropertyChangedEvent(invocation.InvocationTarget, GetPropertyName(invocation.Method)); } …

You can also already see how the interceptor raises the event. Indeed, the abstract class does not event need to implement the INotifyPropertyChanged interface: We can say that our proxy should implement it and simply ensure that our interceptor implements the mechanics:

class PropertyInterceptor : IInterceptor { PropertyChangedEventHandler handler; … public void Intercept(IInvocation invocation) { bool talkingToAddChangedEventHandler = invocation.Method.Name == “add_PropertyChanged”; … if (talkingToAddChangedEventHandler) { handler += (PropertyChangedEventHandler)invocation.Arguments[0]; } … private void RaisePropertyChangedEvent(object o, string notifiedProperty) { if (handler != null) handler(o, new PropertyChangedEventArgs(notifiedProperty)); } }

The attached solution shows the full implementation of the prototype. Basically, it works. This play-class behaves as a fully fledged class implementing INotifyPropertyChanged and raising events like the big kids do:

public abstract class Person { public abstract string FirstName { get; set; } public abstract DateTime Birthdate { get; set; }

public virtual void Promote() { this.FirstName = FirstName + “ The Chief”; } }

And then to get the proxy:

Person p2 = new NotifyingGenerator().Create(); ((INotifyPropertyChanged)p2).PropertyChanged += new PropertyChangedEventHandler(p2_PropertyChanged);

Great! On to WPF. After all, one of the main uses of such a such a class would be to bind it to some WPF UI. Here is were things go very awry.

Basically, WPF needs to do reflection as well to perform the binding magic. This, however, seems to bite badly with how the proxy is generated. With a proxy like the above we very quickly get a XamlParseException due to an AmbiguousMatchException.

Unfortunate!

My second attempt was to make the abstract properties on Person protected, provide a public interface with equally named properties, let the proxy implement the interface on the fly, and bind all this to the WPF. Now WPF would not throw an error, but the whole implementation fails: No properties can be read, the interceptor is not invoked. WPF’s reflection goes to some other place where no proxy has gone before.

Epic fails are quite normal for a first shot, but looks like we need to stick to boilerplate code for now…

The VS2008 solution contains both attempts, you can check for yourself. Maybe there is a solution to the issue after all. If time permits I will do some manual reflection on the proxy to try and get an AmbiguousMatchException myself, which should guide to a possible solution if there is any.

Chronology

  |  
comments powered by Disqus