Compact ViewModels Using Weaving

I love the M-V-VM pattern for Silverlight and WPF applications.  It’s beautiful and works very well, but sometimes it is too verbose, particularly in the ViewModels’ INotifyPropertyChanged implementations.  I find that I spend too much time on the repetitive plumbing tasks that could otherwise be spent programming the actual application logic.

There are many tools and frameworks out there to speed up and simplify MVVM (Caliburn, MVVM Light, WAF, etc.), but I just discovered a very simple tool that goes a long way toward making MVVM more useable. 

It’s called NotifyPropertyWeaver and it solves the problem of repetitive and verbose ViewModels by inserting a build task into your project file (an MSBUILD task) and “weaving” in the usual boilerplate code for you throughout any classes in your assembly that implement INotifyPropertyChanged!  Because it doesn’t use reflection or run-time code generation, there is no performance hit.  In fact, if you look at the IL code it generates, it is indistinguishable from the code you would have written yourself, but without you having to take the time and space to do it.

 

Here’s how to set it up:

1) Download and install the Notify Property Weaver Visual Studio add-in package here http://visualstudiogallery.msdn.microsoft.com/bd351303-db8c-4771-9b22-5e51524fccd3

 

2) In your Silverlight or WPF project, right-click and select IL Weaving > Configure NotifyPropertyWeaver

 

3) Specify where the build task assembly should reside relative to the project or solution root.  The setup wizard will add the necessary files for you automatically.  Switch to the Attribute Assembly tab.

 

4) If you like to be able to control how INotifyPropertyChanged is implemented in your assembly, specify where the attributes assembly should reside relative to the project or solution root. I’m using the same location as the tools directory from the previous step.  The setup wizard will add the necessary files for you automatically. Switch to the Behaviour tab.

 

5) If you are using an MVVM framework, you can have Notify Property Weaver use that framework here.  I’m keeping the default settings to use INotifyPropertyChanged directly.

 

7) Click Ok and build your project.

 

To make sure everything is worked correctly, I used Red Gate’s .NET Reflector to view the MSIL code generated by the compiler.  Here is the original ViewModel class I wrote:

 

public class WeavedViewModel : BaseViewModel
{
    public WeavedViewModel()
    {
        SampleCommand = new DelegateCommand<WeavedViewModel>((vm) =>
            {
                SomeString = "Hello, World!";
                SomeBoolean = true;
            },
            (vm) => true);
    }

    public bool SomeBoolean { get; set; }
    public string SomeString { get; set; }
    public ICommand SampleCommand { get; set; }
}

 

and here is the MSIL code it generated, decompiled for readability:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

public class WeavedViewModel : BaseViewModel
{
    // Methods
    public WeavedViewModel()
    {
        if (CS$<>9__CachedAnonymousMethodDelegate3 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate3 = new Func<WeavedViewModel, bool>(null, (IntPtr) <.ctor>b__1);
        }
        this.SampleCommand = new DelegateCommand<WeavedViewModel>(delegate(WeavedViewModel vm)
        {
            this.SomeString = "Hello, World!";
            this.SomeBoolean = true;
        }, CS$<>9__CachedAnonymousMethodDelegate3);
    }

    // Properties
    public ICommand SampleCommand
    {
        [CompilerGenerated]
        get
        {
            return this.<SampleCommand>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            if (!object.Equals(this.<SampleCommand>k__BackingField, value))
            {
                this.<SampleCommand>k__BackingField = value;
                this.OnPropertyChanged("SampleCommand");
            }
        }
    }

    public bool SomeBoolean
    {
        [CompilerGenerated]
        get
        {
            return this.<SomeBoolean>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            if (!object.Equals(this.<SomeBoolean>k__BackingField, value))
            {
                this.<SomeBoolean>k__BackingField = value;
                this.OnPropertyChanged("SomeBoolean");
            }
        }
    }

    public string SomeString
    {
        [CompilerGenerated]
        get
        {
            return this.<SomeString>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            if (!string.Equals(this.<SomeString>k__BackingField, value))
            {
                this.<SomeString>k__BackingField = value;
                this.OnPropertyChanged("SomeString");
            }
        }
    }
}

 

As you can see, it added backing fields to each of the automatic properties and implemented a value change test and INotifyPropertyChanged in the setter. 

You may have also noticed that, to further compact the ViewModel, I removed the need to implement an ICommand class by using the DelegateCommand<T> included in the Microsoft Patterns & Practices Team’s Prism library.  This class implements ICommand for you and calls a delegate when the command is executed.  In my case, I used an anonymous method to compact my ViewModel implementation even more.

The result (as you can see in the side-by-side comparison below) is a much shorter ViewModel class.  In this case, I was able to reduce the ViewModel to 22 lines of code, down from the original 84 lines.  That roughly a 75% decrease!

 

A sample project with full code is available for you to download.

comments powered by Disqus