Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
14 lines (11 sloc) 12.4 KB
---
layout: post
title: Implementing INotifyPropertyChanged with DynamicProxy2
date: '2008-05-07T23:13:00.001+01:00'
tags: [castle-dynamicproxy,wpf]
modified_time: '2008-05-07T23:13:19.521+01:00'
blogger_id: tag:blogger.com,1999:blog-4015568221071268916.post-7177444328366560362
comments: true
blogger_orig_url: http://serialseb.blogspot.com/2008/05/implementing-inotifypropertychanged.html
---
<p>22:30 and still working on various bits and bobs, so I thought I'd take a well deserved break to tell you about one nice way to implement INotifyPropertyChanged, the one and only interface any class that will be bound in WPF should be implemented.</p> <p>I used to (circa .net 2) not see a bit issue in this kind of code.</p> <div style="font-size: 10pt; background: white; color: black; font-family: consolas, courier new"> <p style="margin: 0px"><span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">MyClass</span> : <span style="color: #2b91af">INotifyPropertyChanged</span></p> <p style="margin: 0px">{</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">private</span> <span style="color: blue">string</span> _myValue;</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">public</span> <span style="color: blue">event</span> <span style="color: #2b91af">PropertyChangedEventHandler</span> PropertyChanged;</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">public</span> <span style="color: blue">string</span> MyValue</p> <p style="margin: 0px">&#160;&#160;&#160; {</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">get</span></p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; {</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">return</span> _myValue;</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">set</span></p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; {</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _myValue = <span style="color: blue">value</span>;</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; RaisePropertyChanged(<span style="color: #a31515">&quot;MyValue&quot;</span>);</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p> <p style="margin: 0px">&#160;&#160;&#160; }</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">protected</span> <span style="color: blue">void</span> RaisePropertyChanged(<span style="color: blue">string</span> propertyName)</p> <p style="margin: 0px">&#160;&#160;&#160; {</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">if</span> (PropertyChanged != <span style="color: blue">null</span>)</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PropertyChanged(<span style="color: blue">this</span>, <span style="color: blue">new</span> <span style="color: #2b91af">PropertyChangedEventArgs</span>(propertyName));</p> <p style="margin: 0px">&#160;&#160;&#160; }</p> <p style="margin: 0px">}</p> </div> <p>But I've been badly spoiled by automatic properties in C# 3.0, and this all sounds like a lot of noise. How great would it be if we could instead simply have the following.</p> <div style="font-size: 10pt; background: white; color: black; font-family: consolas, courier new"> <p style="margin: 0px"><span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">MyDreamClass</span> : <span style="color: #2b91af">INotifyPropertyChanged</span></p> <p style="margin: 0px">{</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">public</span> <span style="color: blue">event</span> <span style="color: #2b91af">PropertyChangedEventHandler</span> PropertyChanged;</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">public</span> <span style="color: blue">string</span> MyValue { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }</p> <p style="margin: 0px">}</p> </div> <p>Definitly much cleaner.&#160; We're going to use DynamicProxy2, which is part of the Castle framework, and is used amongst other things in nhibernate.</p> <p>The process is quite simple. You ask a ProxyGenerator to create a type for you, and pass it an interceptor object that will... Intercept any call!</p> <p>First, let's clear up our class. Because the object generated by DP2 inherits from the original type, it needs to have a virtual modifier on the property. Furthermore, I wanted to add an attribute to specify which properties were to trigger the notification.</p> <div style="font-size: 10pt; background: white; color: black; font-family: consolas, courier new"> <p style="margin: 0px"><span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">MyBetterClass</span> : <span style="color: #2b91af">INotifyPropertyChanged</span></p> <p style="margin: 0px">{</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">public</span> <span style="color: blue">event</span> <span style="color: #2b91af">PropertyChangedEventHandler</span> PropertyChanged;</p> <p style="margin: 0px">&#160;&#160;&#160; [<span style="color: #2b91af">Notify</span>]</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">public</span> <span style="color: blue">virtual</span> <span style="color: blue">string</span> MyValue { <span style="color: blue">get</span>; <span style="color: blue">private</span> <span style="color: blue">set</span>; }</p> <p style="margin: 0px">}</p> </div> <p>Now, let's see how we create the object.</p> <div style="font-size: 10pt; background: white; color: black; font-family: consolas, courier new"> <p style="margin: 0px"><span style="color: blue">static</span> <span style="color: blue">void</span> Main(<span style="color: blue">string</span>[] args)</p> <p style="margin: 0px">{</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">var</span> proxy = <span style="color: blue">new</span> <span style="color: #2b91af">ProxyGenerator</span>();</p> <p style="margin: 0px">&#160;</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: #2b91af">MyBetterClass</span> myClass = proxy.CreateClassProxy&lt;<span style="color: #2b91af">MyBetterClass</span>&gt;(<span style="color: blue">new</span> <span style="color: #2b91af">NotifyPropertyChangedInterceptor()</span>);</p> <p style="margin: 0px">&#160;</p> <p style="margin: 0px">&#160;&#160;&#160; myClass.PropertyChanged += (src,prop) =&gt; <span style="color: #2b91af">Console</span>.WriteLine(prop.PropertyName);</p> <p style="margin: 0px">&#160;</p> <p style="margin: 0px">&#160;&#160;&#160; myClass.MyValue = <span style="color: #a31515">&quot;testValue&quot;</span>;</p> <p style="margin: 0px">}</p> </div> <p>Fairly simple so far. Now let's see the meat of the code, the interceptor.</p> <div style="font-size: 10pt; background: white; color: black; font-family: consolas, courier new"> <p style="margin: 0px"><span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">NotifyAttribute</span> : <span style="color: #2b91af">Attribute</span> { }</p> <p style="margin: 0px"><span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">NotifyPropertyChangedInterceptor</span> : <span style="color: #2b91af">IInterceptor</span></p> <p style="margin: 0px">{</p> <p style="margin: 0px">&#160;&#160;&#160; <span style="color: blue">public</span> <span style="color: blue">void</span> Intercept(<span style="color: #2b91af">IInvocation</span> invocation)</p> <p style="margin: 0px">&#160;&#160;&#160; {</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: green">// let the original call go through first, so we can notify *after*</span></p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; invocation.Proceed();</p> <p style="margin: 0px">&#160;</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">if</span> (invocation.Method.Name.StartsWith(<span style="color: #a31515">&quot;set_&quot;</span>))</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; {</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">string</span> propertyName = invocation.Method.Name.Substring(4);</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">var</span> pi = invocation.TargetType.GetProperty(propertyName);</p> <p style="margin: 0px">&#160;</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: green">// check that we have the attribute defined</span></p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">if</span> (<span style="color: #2b91af">Attribute</span>.GetCustomAttribute(pi, <span style="color: blue">typeof</span>(<span style="color: #2b91af">NotifyAttribute</span>)) == <span style="color: blue">null</span>)</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">return</span>;</p> <p style="margin: 0px">&#160;</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: green">// get the field storing the delegate list that are stored by the event.</span></p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #2b91af">FieldInfo</span> info = invocation.TargetType.GetFields(<span style="color: #2b91af">BindingFlags</span>.Instance | <span style="color: #2b91af">BindingFlags</span>.NonPublic)</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .Where(f =&gt; f.FieldType == <span style="color: blue">typeof</span>(<span style="color: #2b91af">PropertyChangedEventHandler</span>))</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .FirstOrDefault();</p> <p style="margin: 0px">&#160;</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">if</span> (info != <span style="color: blue">null</span>)</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: green">// get the value of the field</span></p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #2b91af">PropertyChangedEventHandler</span> evHandler = info.GetValue(invocation.InvocationTarget) <span style="color: blue">as</span> <span style="color: #2b91af">PropertyChangedEventHandler</span>;</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: green">// invoke the delegate if it's not null (aka empty)</span></p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: blue">if</span> (evHandler != <span style="color: blue">null</span>)</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; evHandler.Invoke(invocation.TargetType, <span style="color: blue">new</span> <span style="color: #2b91af">PropertyChangedEventArgs</span>(propertyName));</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p> <p style="margin: 0px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p> <p style="margin: 0px">&#160;&#160;&#160; }</p> <p style="margin: 0px">}</p> </div> <p>And that's it. Get the field in which the event is stored and invoke it whenever a property has been modified.</p>