In previous post, I simply show how to dynamically generate a class that inherits from a given class, and implement INotifyPropertyChanged for all virtual properties of the object. The example used Reflection.Emit, and I must admit that for people not acquainted with MSIL, the code can be really hard to write. Thanks to Castle project we can also use the DynamicProxy library to obtain the same result, but with really less code and less effort.

Here is the whole class that generate proxy with INotifyPropertyChanged for me

public class CastleDM { public Customer CreateProxy(Type parent) { ProxyGenerator generator = new ProxyGenerator(); return (Customer)generator.CreateClassProxy( parent, new Type[] { typeof(INotifyPropertyChanged) }, new INotifyPropertyChangedInterceptor(), new Object[] { }); } }

As you can see this is a very simple class, I simple use the CreateClassProxy to generate a subclass of the Customer object that implements also the INotifyPropertyChanged, all the work is done by the class INotifyPropertyChangedInterceptor

public class INotifyPropertyChangedInterceptor : StandardInterceptor { private PropertyChangedEventHandler handler; public override object Intercept(IInvocation invocation, params object[] args) { if (invocation.Method.Name == "add_PropertyChanged") { return handler = (PropertyChangedEventHandler)Delegate.Combine(handler, (Delegate)args[0]); } else if (invocation.Method.Name == "remove_PropertyChanged") { return handler = (PropertyChangedEventHandler)Delegate.Remove(handler, (Delegate)args[0]); } else if (invocation.Method.Name.StartsWith("set_")) { if (handler != null) handler(invocation.Proxy, new PropertyChangedEventArgs(invocation.Method.Name.Substring("set_".Length))); } return base.Intercept(invocation, args); } }

This is a very greedy implementation, I simply use an internal variable of type PropertyChangedEventHandler where I store the real handler that external code add to the PropertyChanged event. Then I intercept the invocation of each property that begin with “set_” and raise the PropertyChanged event, passing the Proxy as the first object and a good PropertyChangedEventArgs as second argument.

With this class I can write this code.

CastleDM emitter = new CastleDM(); Object instance = (Customer)emitter.CreateProxy(typeof(Customer)); Customer c = (Customer)instance; c.Property = "TEST"; INotifyPropertyChanged inpc = (INotifyPropertyChanged)instance; inpc.PropertyChanged += delegate(Object sender, PropertyChangedEventArgs args) { Console.WriteLine("PropertyChanged:" + args.PropertyName + " sender is equal to proxy:" + object.ReferenceEquals(sender, c)); }; c.Property = "TEST3"; c.AnotherProp = 22; Console.WriteLine("Property=" + c.Property);

As you can see the object returned from the CreateProxy function is a Customer, but it implements also the INotifyPropertyChanged, and if you run the code you will obtain this output.

This actually shows me that the PropertyChanged is launched correctly, and that the sender is equal to the proxy object. The result is very similar to the one you can obtain with Reflection.Emit, but with really less code.

alk.

Tags:

5 Responses to “Implement InotifyPropertyChanged with Castle.DynamicProxy”

  1. This is a Test Message

  2. What version of DynamicProxy library you were using?
    The code in example does not compile.

  3. I usually work with the trunk, I do not remember exactly what subversion number I compiled for that sample, I’ll look in my HD to find original sample with reference included.

    You can find a compressed version of a test project here.

  4. I use DI container (Windsor) to create instance of class. In above code ProxyGenerator instantiates they class. How can two work in concert?

    Thanks

  5. If you use DI container you should use Interceptors to do AOP. If you still want to use ProxyGenerator with container, you can simply use factory method during registration. This means that for some classes when the container need to resolve them it calls your factory method to create the instance, and then it finish to resolve dependencies.

    alk.