Thursday, April 7, 2011

WPF Bindings Not Updating When PropertyChanged Raised When Bound Against a Proxy Object

Last week I came across an issue with binding NHibernate proxy objects to a WPF UI.  My problem involved a read only property that was not updated in the UI even though the property changed event was being raised.  It worked fine when I first created the object, but after loading it at a later time the updates didn’t work anymore.  Upon closer inspection I discovered that the object we were binding to was an Nhibernate proxy object.  To understand what was going on better I have put together a simple example, consider the following class:

This is a very simple shopping cart, we might have a WPF TextBlock bound to the Total property so that we can show the shopping cart’s total value.  Everything would work find, but if we loaded this class lazily so for example through a Customer.CurrentShoppingCart property, then the total TextBlock wouldn’t be updated.  The reason for this is that in order to delay loading of the shopping cart NHibernate uses a proxy object instead of the actual value when it initializes the Customer’s CurrentShoppingCart property.  How the proxy works can be seen in the following diagram.

image

When our TextBlock binds to the Shopping Cart Proxy’s Total property it also subscribes to the PropertyChanged event.  The proxy passes this subscription onto the actual shopping cart which adds the event handler to its PropertyChanged delegate and you would think all is well.  You of course would be wrong, here is what happens when the Shopping Cart raises a property changed event.

image

Essentially for our event to work we need the sender to be the shopping cart proxy and not the actual shopping cart.  To do this we will need to extend one of NHibernate’s proxy implementations.  We were using the Castle proxy implementation so the following code will work with that.  It would be quite possible to implement something similar with the LinFu version, but I’ll leave that as an exercise for the reader.  Before we start I will mention you can get the full gist here: https://gist.github.com/906916

First up we need to create a new version of the LazyInitializer, which will intercept calls to add or remove subscriptions and keep track of them in the proxy.  We will also subscribe to the target objects PropertyChanged event when it is created so that we can forward on property changed calls, to our subscribers.  We will leave everything else as it was.

The next thing we need to do is extend ProxyFactory to create our version of the LazyInitializer.  This is pretty straight forward and looks like this:

When then have to make our own implementation of IProxyFactoryFactory, our implementation is a straight copy of the one in the NHibernate.Castle.ByteCode assembly only we return our FixDataBindingProxyFactory instead of the Castle version.

And our last step is to update our config so that we use our new IProxyFactoryFactory.

We have now solved the problem.  I would like to thank Ioannis for a blog post he wrote related to this problem which included a solution although it didn’t fix my problem did point me in the right direction.

Happy coding!

Caleb