AutoMagically Implementing INotifyPropertyChanged

Justin Angel picture

Justin
Angel

There have been lots of discussions in the Silverlight Developer Community recently about how to best implement the INotifyPropertyChanged interface.

In this blog post I’ll submit before the reader, what I perceive to be the simplest and the superior solution.

As you all know I’m a huge supporter of Silverlight open source projects,  and in this blog post we'll use Mono.Cecil and PostSharp.

 

Download the projects from this blog post @ http://Justinangel.net/Storage/PostBuildMSILWeaving.zip

 

But first, INotifyWhatchaMaCallit?

Right, let’s start explaining this issue from scratch. For that, we need Cows!
Let’s setup a simple Silverlight form used for inputting the names of Cows.

Cows

Yes, Cows.

So first, we’ll start out by creating a simple POCO (Plain Old CLR Object) class to hold our Cow data.

public class Cow

{

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string MiddleName { get; set; }

}

Next, We’ll create super simple form to represent our new Cow class.

Empty Cow Form

Normally, in real-world applications it would be best to use the DataForm control to create this form.
But for demo’s sake we’ll keep it simple with StackPanels and TextBoxs.

<StackPanel HorizontalAlignment="Left">

    <StackPanel Orientation="Horizontal">

        <TextBlock Text="First Name" Width="150" />

        <TextBox Text="{Binding FirstName}" Width="300" />

    </StackPanel>

    <StackPanel Orientation="Horizontal">

        <TextBlock Text="Middle Name" Width="150" />

        <TextBox Text="{Binding MiddleName}" Width="300" />

    </StackPanel>

    <StackPanel Orientation="Horizontal">

        <TextBlock Text="Last Name" Width="150" />

        <TextBox Text="{Binding LastName}" Width="300" />

    </StackPanel>

    <Button x:Name="changeName" Content="Change Person Properties" />

</StackPanel>

Note the bolded and underlined {Binding} expressions that specify the connection between our POCO property and UI Elements.

After we’ve setup our bindings we’ll need to initialize the DataContext with some initial data. Like so:

 this.DataContext = new Cow() { FirstName = "Bassy", MiddleName = "Won't Change", LastName = "LeCow" };

It’s an odd name for a Cow “Bassy ‘Won’t Change’ LeCow”, but it’s good enough for us.

Let’s fire up the form:

DataBound cow form 

But here’s the core issue that starts off this whole INotifyPropertyChanghed discussion. How do we let the form know when the data changes?

Let’s see this problem in action.
We’ll implement the Button.Click event and change the First, Middle and last name of Bassy LeCow.

private void changeName_Click(object sender, RoutedEventArgs e)

{

    Cow p = (Cow)this.DataContext;

    p.FirstName = "Changed by manual implementation";

    p.LastName = "Changed by Post-IL Weaving";

    p.MiddleName = "Changed";

}

But what happens when we click our button?

image

Nothing. Updating the underlying datasource hasn’t done a single thing to update our form.

Silverlight as a UI Framework has no way of knowing when our POCO property has been updated.

 

What’s the solution?

Man shouting into a bullhorn

There are two solutions that could easily fit into this scenario:

1. Inheriting from DependencyObject and replacing all of our POCO properties with DependencyProperties.
Read more about that option at this SilverlightShow article.

2. Implementing the INotifyPropertyChanged interface and invoking PropertyChanged event in our property setters.

 

Up until recently, I was squarely in the DependencyObject camp.
But after a tempestuous round of discussions on the WPF Disciples mailing list and this aptly written blog post by Kent Boogaart I’ve moved to the INotifyPropertyChanged camp.

The core reason I decided on INotifyPropertyChanged was that you can’t use DependencyObjects in non-UI Disptacher threads. Which is a deal breaker.

 

Enough with your jibber jabber, show me the code!

So here’s the basic implementation of INotifyPropertyChanged for the FirstName property:

    public class Cow : INotifyPropertyChanged

    {

        private string _firstName;

        public string FirstName

        {

            get { return _firstName; }

            set

            {

                _firstName = value;

                RaisePropertyChanged("FirstName");

            }

        }

 

        public string LastName { get; set; }

 

        public string MiddleName { get; set; }

 

        #region Implement INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)

        {

            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));

        }

        #endregion

    }

We won’t go over the specifics, but it’s a fairly easy interface to implement, just fire the PropertyChanged event.

When comparing LastName implementation to FirstName implementation we can clearly see that it was much simpler to declare the LastName property.
It’s more readable, more maintainable, and has less chance of duplicating code throughout our system.

 

Solving the problem caused by the solution

There are a lot of options that were recently discussed for solving the syntax issue caused by declaring INPC (INotifyPropertyChanged) properties.

1) Ray Huston and Jonas Follesoe talk about Runtime substitution of INPC properties through Castle.DynamicProxy.

public class MyViewModel : IAutoNotifyPropertyChanged

{

    public virtual string Name { get; set; }

    public virtual int Age { get; set; }

 

    public event PropertyChangedEventHandler PropertyChanged;

 

    public void OnPropertyChanged(string propertyName)

    {

        if (PropertyChanged != null)

            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

    }

}

However, this method requires you stop using our beloved “New” keyword and start using runtime proxies. I’m not a fan of either of those.
A solid solution should have zero-impact on the way we write code today.

 

2) Michael Sync, Einar Ingebrigsten and Oren Eini talk about improving the syntax needed to declare an INPC property.  

public class Employee : INotifyPropertyChanged

{

    public event PropertyChangedEventHandler PropertyChanged;

 

    private string _firstName;

    public string FirstName

    {

        get { return this._firstName; }

        set

        {

            this._firstName = value;

            this.PropertyChanged.Notify(() => this.FirstName);

        }

    }

}

There are various sugar-coated syntaxes that have been suggested for INPC. but all of them require us to change the way we code.

3) Brad Abrams demos Silverlight WCF RIA Services generating the INotifyPropertyChanged and INotifyPropertyChanging interfaces for you.
But that only works if these types are declared on the server and are sent down to Silverlight. Additionally, if the property name changes it’s all still string based.

    [DataMember()]

    [Key()]

    [ReadOnly(true)]

    public int EmployeeID

    {

        get

        {

            return this._employeeID;

        }

        set

        {

            if ((this._employeeID != value))

            {

                ValidationContext context = new ValidationContext(this, null, null);

                context.MemberName = "EmployeeID";

                Validator.ValidateProperty(value, context);

                this._employeeID = value;

                this.OnPropertyChanged("EmployeeID");

            }

        }

    }

 

Solving the problem caused by the Solution to the Solution of the Problem

Confused Man

Confused? Tired? About to start crying? Be a man for gods sakes!

Each of the existing solutions we’ve seen up until now has it’s own set of unique challenges.
All of which either creates weird, unnatural and repeatable C# Syntaxes, or relays heavily on string based solutions.

 

Post Build MSIL Weaving

C# --> MSIL --> Byte Code

C# becomes MSIL, which later becomes Byte Code.
Remember those good old .Net 1.1 days when this chart looked important? 

Well, I believe we’ve exhausted all the possible C# hacks to create sustainable and readable INPC properties in straight C# code.
The Silverlight & WPF developer community has been working on this for 2 years. Let’s think outside the C# box.

Specifically, let’s think in the MSIL box.

Post Build MSIL Weaving is the practice of taking compiled MSIL assemblies and manipulating them after compilation.

So, we can write C# code that manipulates our assemblies after they’ve been compiled.

In this article we’ll look at 2 approaches to doing Post-IL weaving: Mono.Cecil and PostSharp.

 

 

Low Level MSIL Weaving  with Mono.Cecil

This method is super low-level and takes us down to writing code in MSIL with a custom MSBuild Task.
It’s not for everyone, but try and follow.

 

This is the C# Syntax I want us to end up with:

    public class Cow : INotifyPropertyChanged

    {

        [Property]

        public string LastName { get; set; }

 

1) We’ll start off by creating the PropertyAttribute in our Silverlight project:

    [AttributeUsage(AttributeTargets.Property)]

    public class PropertyAttribute : Attribute

    {

    }

 

2) Next, we’d like to create a Custom “AfterBuild” MSBuild task we can integrate into our project to read this attribute.

Let’s create a new Desktop project to hold that MSBuild Task:

New Desktop project

3) Add a reference to the MSBuild V3.5 DLLs:

MSBuild References

4) Now that we’ve got our MSBuild references we’ll create a custom MSBuild task:

public class WeavingInpcTask : Task

{

    public override bool Execute()

    {

        SearchForPropertiesAndAddMSIL();

        return true;

    }

 

    private void SearchForPropertiesAndAddMSIL()

    {

    }

 

    [Required]

    public string SolutionDir { get; set; }

}

5) We’ll need to tell our Silverlight project to execute this task after building our project.
To do that we’ll unload our Silverlight project and add a reference to this task after build.

Unload project

Edit project

  <UsingTask

    TaskName="WeavingINPC.MSBuildTask.WeavingINPCTask"

    AssemblyFile="$(SolutionDir)WeavingINPC.MSBuildTask\bin\$(Configuration)\WeavingINPC.MSBuildTask.dll" />

 

  <Target Name="AfterBuild">

    <WeavingINPCTask SolutionDir="$(SolutionDir)" />

  </Target>

6) Download the latest Mono.Cecil binaries from http://mono.ximian.com/daily/ MonoCharge.

image

7) Add a reference from our MSBuild project to the unzipped Mono.Cecil.DLL.

image

 

Now that we’re done with all the grunt work of setting up an MSBuild Mono.Cecil project we can get down to business.

Here’s how our class looks like:

public class Cow : INotifyPropertyChanged

{

    private string _firstName;

    public string FirstName

    {

        get { return _firstName; }

        set

        {

            _firstName = value;

            RaisePropertyChanged("FirstName");

        }

    }

 

    [Property]

    public string LastName { get; set; }

 

    public string MiddleName { get; set; }

 

    #region Implement INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)

    {

        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));

    }

    #endregion

}

FirstName manually implements INotifyPropertyChanged.
LastName should be auto implemented by Mono.Cecil.
And Middle Name won’t have any INPC support at all.

Let’s open up reflector and see the differences between FirstName and LastName:

FirstName C# Reflector

LastName C# Reflector

 

But wait, we don’t really care about the C# differences, do we? We care about MSIL!

So let’s change reflector to show us the IL code.

Reflector Language Selection

Here’s the set_LastName method MSIL and you can see it doesn’t call RaisePropertyChanged:

LastName MSIL

And here’s the set_firstName method MSIL that does invoke RaisePropertyChanged:

FirstName MSIL

We can see that if we’re going to add MSIL directly to set_lastName we’re going to need to add these 5 lines of MSIL:

5 Lines of MSIL we need to add to LastName

 

Let’s write the code to load up all the assemblies in our solution and find all properties that have the PropertyAttribute:

foreach (string assemblyPath in Directory.GetFiles(SolutionDir, "*.dll", SearchOption.AllDirectories))

{

    AssemblyDefinition sourceAssembly = AssemblyFactory.GetAssembly(assemblyPath);

    foreach (TypeDefinition type in sourceAssembly.MainModule.Types)

        foreach (PropertyDefinition prop in type.Properties)

            foreach (CustomAttribute attribute in prop.CustomAttributes)

               if (attribute.Constructor.DeclaringType.FullName == typeof(PropertyAttribute).FullName)

               {

Here’s the object model we have to go through to find usages of PropertyAttriubte:

Assembly --> Module --> Type --> Property --> Attributes

Next, we’ll add those 5 lines of MSIL:

CilWorker MSILWorker = prop.SetMethod.Body.CilWorker;

 

Instruction ldarg0 = MSILWorker.Create(OpCodes.Ldarg_0);

 

Instruction propertyName = MSILWorker.Create(OpCodes.Ldstr, prop.Name);

 

Instruction callRaisePropertyChanged =

    MSILWorker.Create(OpCodes.Call, raisePropertyChanged);

 

MSILWorker.InsertBefore(prop.SetMethod.Body.Instructions[0], MSILWorker.Create(OpCodes.Nop));

 

MSILWorker.InsertBefore(prop.SetMethod.Body.Instructions[prop.SetMethod.Body.Instructions.Count - 1],

                        ldarg0);

 

MSILWorker.InsertAfter(ldarg0, propertyName);

 

MSILWorker.InsertAfter(propertyName, callRaisePropertyChanged);

 

MSILWorker.InsertAfter(callRaisePropertyChanged, MSILWorker.Create(OpCodes.Nop));

 

This isn’t the simplest code you’d ever seen for sure.
But It’s not that hard to see how these 5 InsertBefore/InsertAfter become our 5 MSIL lines.
Look for the words “nop", “ldarg_0”, “ldstr” and “call”. And then it becomes pretty clear we’ve just implemented the missing MSIL.

FirstName with it's manuall MSIL

 

Next we’ll build our project.

And reflect into set_LastName:

LastName with it's new MSIL 

Isn’t that cool? We’ve added 5 lines of MSIL directly into our property.

 

Let’s run our solution:

form before clicking

form after clicking with INPC changes

So FirstName was changed by our manual INPC implementation, and LastName was changed by our Post-Build MSIL Weaving.

We can now take this solution all the way home and end up with this syntax:

public class Cow : INotifyPropertyChanged

{

    [Property] public string LastName { get; set; }

    [Property] public string MiddleName { get; set; }

    [Property] public string LastName { get; set; }

}

 

 

High Level MSIL Weaving with PostSharp

Some people would find MSIL intimidating, writing your own build tasks frightening and reinventing AOP a daunting task.
Those developers are essentially pansy little girls, but let’s see a more straightforward way of doing Post-Build MSIL Weaving.

 

Step #1: Go to the PostSharp website, register to the website, and install PostSharp. Make sure you close down Visual Studio during installation. Seriously.

Downloading postSharp

 

Step #2: Add a reference to PostSharp.Loas.SL.dll and PostSharp.Public.SL.dll from your Silverlight project.

image

(On my dev box PostSharp installed to: C:\Program Files (x86)\PostSharp 1.5\Reference Assemblies\Silverlight 2.0\)

Step #3: What? We’re done?
That was pretty much all the setup you had to do.

 

Next, we’ll add a the PropertyAttribute so it inherits from OnMethodInvocationAgent and override OnInvocation:

public class PropertyAttribute : OnMethodInvocationAspect

{

    public override void OnInvocation(MethodInvocationEventArgs eventArgs)

    {

        eventArgs.Proceed();

    }

}

We’ll make sure that the attribute was applied on a Setter.

public class PropertyAttribute : OnMethodInvocationAspect

{

    public override void OnInvocation(MethodInvocationEventArgs eventArgs)

    {

        eventArgs.Proceed();

        if (eventArgs.Method.Name.StartsWith("~set_"))

        {

        }

    }

}

And lastly we’ll call the RaisePropertyChange method with the property Name.

public class PropertyAttribute : OnMethodInvocationAspect

{

    public override void OnInvocation(MethodInvocationEventArgs eventArgs)

    {

        eventArgs.Proceed();

        if (eventArgs.Method.Name.StartsWith("~set_"))

        {

            MethodInfo method = eventArgs.Instance.GetType().GetMethod("RaisePropertyChanged");

            method.Invoke(eventArgs.Instance, new object[] {(eventArgs.Method.Name.Replace("~set_", string.Empty))});

        }

    }

}

This is how our class looks like:

    public class Cow: INotifyPropertyChanged

    {

        private string _firstName;

        public string FirstName

        {

            get { return _firstName; }

            set

            {

                _firstName = value;

                RaisePropertyChanged("FirstName");

            }

        }

 

        public string LastName { get; [Property] set; }

 

        public string MiddleName { get; set; }

 

        #region Implement INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)

        {

            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));

        }

        #endregion

   }

Let’s run our sample:

Before Button Click

After button click

And Indeed, Post Build MSIL Weaving worked great here as well.

In reflector we can see the Post-IL weaved code in Last Name:

MSIL Weaved into Set_LastNAme

It isn’t really clear when you first look at it.

But basically, this code just invokes our PropertyAttribute method at runtime.
So PostSharp weaved some MSIL to invoke our code, but not the code itself.

We can take this solution all the way home and end up with this syntax:

public class Cow : INotifyPropertyChanged

{

    public string LastName { get; [Property] set; }

    public string MiddleName { get; [Property] set; }

    public string LastName { get; [Property] set; }

}

 

 

What’s next?

In this article I’ve shown 2 Post-Build MSIL weaving techniques:

1) Mono.Cecil – that changes MSIL directly at compile time.
2) PostSharp – that changes MSIL to invoke our code at runtime.

If you’re going to use any of these options, you’ll have to consider where it’s best for you to apply your attribute – on the class? on each property? on the entire assembly?
Are you going to implement INPC with default values, raising notifications only on changes, cross-property change notifications, and a myriad of other features?  Or just stick to the basics?

If you’re going to go with Mono.Cecil you’ll have to work a bit more on the infrastructure of the MSBuild tasks and Visual Studio integration.

If you’re going to go with PostSharp you’ll have to spend some time looking at the various classes and overloads offered in the framework.
Plus, you’ll have to consider the performance and payload ramifications.

 

 

Fin

In my opinion, Post-Build MSIL Weaving provided us with the best and simplest solution.

We can support the property INPC syntax:

public class Cow : INotifyPropertyChanged

{

    public string LastName { get; [Property] set; }

    public string MiddleName { get; [Property] set; }

    public string LastName { get; [Property] set; }

}

or the class INPC syntax:

[INPC]

public class Cow : INotifyPropertyChanged

{

    public string LastName { get; set; }

    public string MiddleName { get; set; }

    public string LastName { get; set; }

}

or even go with the assembly Wide syntax:

[assembly: INPC()]

Each of these syntaxes affords us total control of our INotifyPropertyChanged scenario without having to change the look, feel and flow of our code.

 

Sincerely,

-- Justin Angel



Comments