As you may or may not be aware I have recently spending some time trying to get a re-usable MVVM template together. On my voyage of discovery when creating this I wanted to take out the standard boiler plate NotifyPropertyChanged code form the MVVM Light Toolkit. I wanted to use AOP (Aspect Orientated Programming) so that on properties I could just have a single attribute [PropertyChanged] and then it would all be taken care of automagically. Obviously the big advantage of this is that I could then use auto properties to provide a much cleaner code base within my ViewModels.
Example of the end result:
[PropertyChanged]
public string RecordingMenuItemText { get; set; }
So how do we go about this ? Enter the world of Mono Cecil and a custom build task to the rescue. The process I followed was quite simple, build the application and then weave IL against any properties that have the [PropertyChanged] attribute via Mono Cecil. After this was done I just needed to update the XAP file so that i gets deployed to the Emulator / Device.

Putting it all together…
To code for the build task takes a single property so you can specify the assembly to weave the code into. Below is the code I used to inject IL from the MVVM Light Toolkit. I added a reference to the MVVM Light assembly so that I could weave the IL for notify property changed method within the ViewModelBase class adding in the string parameter for the property name from MVVM Light.
namespace SugarTank.Build
{
using System;
using System.Reflection;
using GalaSoft.MvvmLight;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Mono.Cecil;
using Mono.Cecil.Cil;
public class PropertyChangedTask : Task
{
/// <summary>
/// When overridden in a derived class, executes the task.
/// </summary>
/// <returns>
/// true if the task successfully executed; otherwise, false.
/// </returns>
public override bool Execute()
{
this.InjectMsil();
return true;
}
/// <summary>
/// Injects the MSIL.
/// </summary>
private void InjectMsil()
{
ModuleDefinition module = ModuleDefinition.ReadModule(this.AssemblyPath);
foreach (TypeDefinition type in module.Types)
{
foreach (PropertyDefinition prop in type.Properties)
{
foreach (CustomAttribute attribute in prop.CustomAttributes)
{
if (attribute.Constructor.DeclaringType.FullName.Contains("PropertyChanged"))
{
ILProcessor msilWorker = prop.SetMethod.Body.GetILProcessor();
Instruction ldarg0 = msilWorker.Create(OpCodes.Ldarg_0);
var raisePropertyChangedInfo = typeof (ViewModelBase).GetMethod(
"RaisePropertyChanged",
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance, null, new[] {typeof (string)}, null);
var raisePropertyChanged = module.Import(raisePropertyChangedInfo);
Instruction propertyName = msilWorker.Create(OpCodes.Ldstr, prop.Name);
Instruction callRaisePropertyChanged = msilWorker.Create(OpCodes.Callvirt,
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));
}
}
module.Write(AssemblyPath);
}
}
}
/// <summary>
/// Gets or sets the assembly path.
/// </summary>
/// <value>The assembly path.</value>
[Required]
public string AssemblyPath { get; set; }
}
}
Now all we need to do is edit the Windows Phone 7 project file and hook into the after build event to execute our build task. To do this I just edited the project file in notepad and added the following:
<Import Project="$(SolutionDir)References\Community Tasks\MSBuild.Community.Tasks.Targets" />
<UsingTask TaskName="SugarTank.Build.PropertyChangedTask" AssemblyFile="$(SolutionDir)References\Custom Build Tools\SugarTank.Build.dll"/>
<Target Name="AfterBuild">
<CallTarget RunEachTargetSeparately="True" Targets="CopyManifest;CopySplashScreen;WeaveCode;CreateZip" />
</Target>
<Target Name="CopyManifest">
<Copy SourceFiles="$(ProjectDir)\Properties\WMAppManifest.xml" DestinationFolder="$(TargetDir)" />
</Target>
<Target Name="CopySplashScreen">
<Copy SourceFiles="$(ProjectDir)\SplashScreenImage.jpg" DestinationFolder="$(TargetDir)" />
</Target>
<Target Name="WeaveCode">
<PropertyChangedTask AssemblyPath="$(TargetPath)" />
</Target>
<Target Name="CreateZip">
<ItemGroup>
<ZipFiles Include="$(TargetDir)\*.*" Exclude="$(TargetDir)\*.xap;$(TargetDir)\*.pdb;$(TargetDir)\*.zip" />
</ItemGroup>
<Zip Files="@(ZipFiles)" WorkingDirectory="$(TargetDir)" ZipFileName="$(TargetDir)SugarTank.Mobile.xap" />
</Target>
You will notice I have a few other targets I run, these manually create the XAP file as I couldn’t find a way via MSBuild to open up the XAP file and replace the single assembly. I was hoping after the IL was weaved in the XAP file would be created and the updated assembly would automatically make its way into the XAP.
But from what I can see it creates the XAP before the assembly has the code weaved into it which has forced me to take this approach and manually create a valid XAP.
If you know of a better approach to amend the XAP file it would be greatly appreciated.
@jsharratt