A common gripe with WPF and Silverlight development is having to implement INotifyPropertyChanged in your Models and ViewModels. You then have to remember (most of the time) to fire the PropertyChanged event whenever you change a property.

On the project I’m working on we’ve been using PostSharp for a while to automatically weave in the code to do this for us. The other day I set about converting our app to .Net 4 and Silverlight 4. Sadly, the version of PostSharp we were using (1.5) doesn’t play nicely with .Net 4. Fine I thought, I’ll simple update to version 2.0. I then found out that the functionality we need is only available in the licensed version. Although PostSharp is a great product, we only use it for this exact problem and it doesn’t justify forking out for a license for all the devs on my team.

So I decided to set out weaving in the IL myself. I tried various different methods including Microsoft’s CCI and CciSharp. I found these to be very powerful, but fiendishly complicated. Then I tried out Mono.Cecil. This is a fantastic bit of software and is very easy to use.

I stumbled upon a great blog post by Justin Angel. It describes exactly how you can achieve this using Mono.Cecil. The only problem is that if you rewrite a silverlight application assembly in the AfterBuild Target as Justin describes, the XAP file generated contains the DLL before the rewrite and therefore doesn’t work.

I managed to get around this problem by hacking my .csproj file thusly:

  <UsingTask TaskName=”CecilPropertyChanged.MSBuild.WeaveNotifyPropertyChangedTask” AssemblyFile=”$(SolutionDir)Lib\CecilPropertyChanged.MSBuild.dll” />
  <Target Name=”WeaveNotifyPropertyChanged”>
    <CecilPropertyChanged.MSBuild.WeaveNotifyPropertyChangedTask AssemblyPath=”$(IntermediateOutputPath)$(AssemblyName).dll” />
    <Copy SourceFiles=”$(IntermediateOutputPath)$(AssemblyName).dll” DestinationFolder=”$(OutputPath)” />
  </Target>

  <PropertyGroup>
    <XapPackagerDependsOn>
      _CreateSLProperties;
      MarkupCompilePass1;
      ValidateXaml;
      WeaveNotifyPropertyChanged;
      FilesToXap;
      CreateSilverlightAppManifest;
    </XapPackagerDependsOn>
  </PropertyGroup>

Works like a charm!



Further to my previous post, Erik Meijer commented that an overload to the FromEvent method is available to convert from generic event handlers to the specific ones used by much of the BCL. So the code from the previous post would look something like this now:

        #region Ctor
        public MainPage()
        {
            InitializeComponent();

            var mouseEventSource = Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
                eh => new MouseEventHandler(eh),    // Conversion
                eh => this.MouseMove += eh,         // Subscribe
                eh => this.MouseMove -= eh);        // Unsubscribe

            _mouseMoveSubscription = mouseEventSource.Subscribe(OnMouseMove);
        }
        #endregion

        private void OnMouseMove(Event<MouseEventArgs> e)
        {
            var position = e.EventArgs.GetPosition(this);

            Debug.WriteLine("Mouse moved.  Position: {0}.", position);
        }

This solves one of my major gripes. Exciting times!