Jonathan Birkholz

Aop INotifyPropertyChanged StructureMap

Anyone who has worked with INotifyPropertyChanged knows that this simple interface can be a royal pain in the ass.

To try and eliminate the pain, people have created some great solutions using AOP : IL weaving (PostSharp) and using a proxy (Castle Dynamic Proxy).

PostSharp has a little too much voodoo for me atm. I think I will warm up to it though and re-examine using PostSharp on my next solution. But for now, I wanted to use Castle Project’s Dynamic Proxy.

Naturally since Castle also has a very popular IoC container in Windsor, most examples marry Dynamic Proxy and Windsor to form an AOP INotifyPropertyChanged solution.

Since I am using StructureMap for this project, I endeavored to create my own solution using Dynamic Proxy.

My first attempt I shared at the Virtual Brown Bag look liked it worked but in reality I was constructing my objects twice.

Once with SM and once with the Proxy generator.

I had to go back to the drawing board and posted my problem at the SM google group. http://groups.google.com/group/structuremap-users/browse_thread/thread/1a6b19ce8152db1b?hl=en

I believe the syntax given to me was an older version of SM. For the record, I am using version 2.5.3.0

But it did direct me to the general idea on where I should start looking. I ended up needing to create an IBuildInterceptor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class MyBuildInterceptor : IBuildInterceptor
{
public MyBuildInterceptor(Type concreteType)
{
_ConcreteType = concreteType;
}
readonly Type _ConcreteType;
public object Build(BuildSession buildSession, Type pluginType, Instance instance)
{
var constructorArgs = _ConcreteType
.GetConstructors()
.FirstOrDefault()
.GetParameters()
.Select(p => buildSession.CreateInstance(p.ParameterType))
.ToArray();
var interceptors = new List<IInterceptor>
{
new NotifyInterceptor()
}
.ToArray();
return new ProxyGenerator().CreateClassProxy(_ConcreteType, interceptors, constructorArgs);
}
public IBuildPolicy Clone()
{
return InnerPolicy.Clone();
}
public void EjectAll()
{
InnerPolicy.EjectAll();
}
public IBuildPolicy InnerPolicy { get; set; }
}

To register my ViewModels I created a convention using a TypeScanner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyNotifyConvention : ITypeScanner
{
public void Process(Type type, PluginGraph graph)
{
if (type.GetInterface("IViewModel") == null)
return;
var interfaceType = type.GetInterfaces().FirstOrDefault(i => i.Name.Contains("ViewModel")
&& i.Name != "IViewModel");
if (interfaceType == null)
return;
graph.Configure(r =>
r.ForRequestedType(interfaceType)
.InterceptConstructionWith(new MyBuildInterceptor(type))
.TheDefaultIsConcreteType(type));
}
}

I then used a Dynamic Proxy Interceptor that I basically copy and pasted from Serial Seb’s example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class NotifyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// let the original call go through first, so we can notify *after*
invocation.Proceed();
if (invocation.Method.Name.StartsWith("set_"))
{
string propertyName = invocation.Method.Name.Substring(4);
RaisePropertyChangedEvent(invocation, propertyName, invocation.TargetType);
}
}
void RaisePropertyChangedEvent(IInvocation invocation, string propertyName, Type type)
{
// get the field storing the delegate list that are stored by the event.
var methodInfo = type.GetMethod("RaisePropertyChanged");
if (methodInfo == null)
{
if (type.BaseType != null)
RaisePropertyChangedEvent(invocation, propertyName, type.BaseType);
}
else // info != null
{
methodInfo.Invoke(invocation.InvocationTarget, new object[] {propertyName});
}
}
}

Instead of looking for the PropertyChanged Handler, I create a RaisePropertyChanged method and use that. Although I could raise the event using the field method Seb was using, WPF wasn’t updating the binding. I didn’t really bother to investigate and just rolled with this solution.

So that’s my solution and I’ll start using it in my current solution. Naturally I will clean up the code some more etc. Feel free to use this for any of your own projects. You can also get the solution at my GitHub:

http://github.com/RookieOne/StructureMapAopNotify