Home > C#, MVVM, Silverlight, WPF > Dynamically adding RaisePropertyChanged to MVVM Light ViewModels using Reflection.Emit

Dynamically adding RaisePropertyChanged to MVVM Light ViewModels using Reflection.Emit

Introduction

I like the clean separation between views and view models made possible by the MVVM pattern. But I don’t like the code pattern required to implement RaisePropertyChanged in simple properties.

private static string someProperty;

public string SomeProperty
{
    get
    {
        return someProperty;
    }
    set
    {
        someProperty = value;
        RaisePropertyChanged("SomeProperty");
    }
}

I would prefer an implementation like this:

[RaisePropertyChanged]
public string SomeProperty { get; set; }

Implementing this is possible with a principle called AOP (Aspect Oriented Programming). Although C# is an OOP (Object Oriented Programming) language, various people and companies have made AOP in C# easily possible. As I am a passionate technologist, I off course wanted to understand how this “magic” works Glimlach.

Two options are possible: post compilation by modifying the compiled assemblies or dynamic creation of proxy classes at runtime. In this article, I will present how you can implement this yourself. But because various high quality tools and frameworks are available, I recommend not to implement this yourself for production code.

Post compilation can be done by an AOP tool like PostSharp. Dynamic proxy creation is done by libraries such as Castle DynamicProxy or by IOC containers like Microsoft Unity.

Dynamic proxy Creation

I have chose to use dynamic proxy creation in my example. I will not make my code generic, but instead I will hardcode the required functionality. I assume that you will be capable of generalizing this yourself, if required. The code relies on MVVM Light, but you could replace this library with any other MVVM library.

The API that allows this dynamic behavior is Reflection.Emit and it allows you to create classes at runtime using MSIL code. MSIL is the assembly of the .Net CLR. Most .Net programmers have probably never seen this language. By using a smart trick, only a limited knowledge is required.

Microsoft has an MSIL disassembler called “ILDASM”. You can use this tool easily if you launch it from the “Visual Studio 2010 command prompt”. To know the required MSIL code for our prototype, we first create what we want in c#, compile it and then look at the generated MSIL code with ILDASM.

The proxy we want to create dynamically (SampleViewModelExtended) looks like:

// Dynamic proxy created manually
public class SampleViewModelExtended : SampleViewModel
{
    public override string SomeProperty
    {
        get
        {
            return base.SomeProperty;
        }
        set
        {
            base.SomeProperty = value;
            RaisePropertyChanged("SomeProperty");
        }
    }
}

public class SampleViewModel : ViewModelBase
{
    [RaisePropertyChanged]
    public virtual string SomeProperty { get; set; }
}

Please note: we have to make the properties virtual in the base class, because otherwise we cannot overwrite the setters in the derived class.

If we compile this and look at the MSIL, we get:

ILDASM_required_MSIL_code

Having the required MSIL code, the only thing we now have to do is generating the MSIL dynamically using Reflection.Emit.

public static class ReflectionEmitViewModelFactory
{
    public static T CreateInstance<T>()
        where T : ViewModelBase
    {
        Type vmType = typeof(T);

        VerifyViewModelType(vmType);

        // Create everything required to get a module builder
        AssemblyName assemblyName = new AssemblyName("SmartViewModelDynamicAssembly");
        AppDomain domain = AppDomain.CurrentDomain;
        AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            //AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);

        string dynamicTypeName = Assembly.CreateQualifiedName(vmType.AssemblyQualifiedName, "Smart" + vmType.Name);

        TypeBuilder typeBuilder = moduleBuilder.DefineType(dynamicTypeName,
            TypeAttributes.Public | TypeAttributes.Class, vmType);

        MethodInfo raisePropertyChangedMethod = typeof(ViewModelBase).GetMethod("RaisePropertyChanged",
            BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string) }, null);

        foreach (PropertyInfo propertyInfo in FindNotifyPropertyChangCandidates<T>())
            UpdateProperty(propertyInfo, typeBuilder, raisePropertyChangedMethod);

        Type dynamicType = typeBuilder.CreateType();

        return (T)Activator.CreateInstance(dynamicType);
    }

    private static void VerifyViewModelType(Type vmType)
    {
        if (vmType.IsSealed)
            throw new InvalidOperationException("The specified view model type is not allowed to be sealed.");
    }

    private static IEnumerable<PropertyInfo> FindNotifyPropertyChangCandidates<T>()
    {
        return from p in typeof(T).GetProperties()
                where p.GetSetMethod() != null && p.GetSetMethod().IsVirtual &&
                p.GetCustomAttributes(typeof(RaisePropertyChangedAttribute), false).Length > 0
                select p;
    }

    private static void UpdateProperty(PropertyInfo propertyInfo, TypeBuilder typeBuilder,
        MethodInfo raisePropertyChangedMethod)
    {
        // Update the setter of the class
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name,
            PropertyAttributes.None, propertyInfo.PropertyType, null);

        // Create set method
        MethodBuilder builder = typeBuilder.DefineMethod("set_" + propertyInfo.Name,
            MethodAttributes.Public | MethodAttributes.Virtual, null, new Type[] { propertyInfo.PropertyType });
        builder.DefineParameter(1, ParameterAttributes.None, "value");
        ILGenerator generator = builder.GetILGenerator();

        // Add IL code for set method
        generator.Emit(OpCodes.Nop);
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Ldarg_1);
        generator.Emit(OpCodes.Call, propertyInfo.GetSetMethod());

        // Call property changed for object
        generator.Emit(OpCodes.Nop);
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Ldstr, propertyInfo.Name);
        generator.Emit(OpCodes.Callvirt, raisePropertyChangedMethod);
        generator.Emit(OpCodes.Nop);
        generator.Emit(OpCodes.Ret);
        propertyBuilder.SetSetMethod(builder);
    }
}

Once you know the MSIL to generate, using Reflection.Emit is very straightforward. By using the sample code provided above, we can know create dynamic ViewsModels very easy:

SampleViewModel viewModel = ReflectionEmitViewModelFactory.CreateInstance<SampleViewModel>();
Advertisements
  1. eduardo
    December 8, 2011 at 17:37

    I wrote an article about this, please give some feedback.
    http://www.codeproject.com/KB/cs/INotifyPropertyChanged.aspx
    Thank you!!!

  2. Ardi Huang
    January 3, 2014 at 19:21

    hi, could you help me. I try to call method RaisedPropertyChanged on Get Property of SampleViewModel class. But it cannot works, this is my code below:

    private static void UpdateProperty(PropertyInfo propertyInfo, TypeBuilder typeBuilder, MethodInfo raisePropertyChangedMethod)
    {
    System.Reflection.MethodAttributes propertyAttributes = System.Reflection.MethodAttributes.Public |
    System.Reflection.MethodAttributes.HideBySig |
    System.Reflection.MethodAttributes.SpecialName;

    PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name,
    PropertyAttributes.SpecialName, propertyInfo.PropertyType, null);

    MethodBuilder methodBuilderGetter = typeBuilder.DefineMethod(“get_” + propertyInfo.Name, propertyAttributes, propertyInfo.PropertyType, null);

    ILGenerator generator = methodBuilderGetter.GetILGenerator();

    /*
    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldstr “name”
    IL_0007: callvirt instance void ConsoleApplication4.ViewModelBase::RaisePropertyChanged(string)
    IL_000c: nop
    IL_000d: ldstr “”
    IL_0012: stloc.0
    IL_0013: br.s IL_0015
    IL_0015: ldloc.0
    IL_0016: ret

    */

    // Call property changed for object
    generator.Emit(OpCodes.Nop);
    generator.Emit(OpCodes.Ldarg_0);
    generator.Emit(OpCodes.Ldstr, propertyInfo.Name);
    generator.Emit(OpCodes.Callvirt, raisePropertyChangedMethod);
    generator.Emit(OpCodes.Nop);
    generator.Emit(OpCodes.Ldstr, “”); // return empty string
    generator.Emit(OpCodes.Stloc_0);

    //Label targetInstruction = generator.DefineLabel();
    //generator.MarkLabel(targetInstruction);
    //Type.generator.Emit(OpCodes.Br_S, targetInstruction);

    generator.Emit(OpCodes.Ldloc_0);

    generator.Emit(OpCodes.Ret);
    propertyBuilder.SetGetMethod(methodBuilderGetter);
    }
    }

  3. January 4, 2014 at 11:54

    Hi, thx, I already fix it after modify a liitle OpCodes:

    private static void UpdateProperty(PropertyInfo propertyInfo, TypeBuilder typeBuilder,
    MethodInfo raisePropertyChangedMethod)
    {
    System.Reflection.MethodAttributes propertyAttributes = System.Reflection.MethodAttributes.Public |
    System.Reflection.MethodAttributes.HideBySig |
    System.Reflection.MethodAttributes.Virtual |
    System.Reflection.MethodAttributes.SpecialName;

    // Update the setter of the class
    PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name,
    PropertyAttributes.SpecialName, propertyInfo.PropertyType, new Type[] {});

    MethodBuilder builder = typeBuilder.DefineMethod(“get_” + propertyInfo.Name,
    propertyAttributes, propertyInfo.PropertyType, new Type[] {});

    ILGenerator generator = builder.GetILGenerator();

    // Call property changed for object
    generator.Emit(OpCodes.Nop);
    generator.Emit(OpCodes.Ldarg_0);
    generator.Emit(OpCodes.Ldstr, propertyInfo.Name);
    generator.Emit(OpCodes.Callvirt, raisePropertyChangedMethod);
    generator.Emit(OpCodes.Nop);

    generator.Emit(OpCodes.Nop);
    generator.Emit(OpCodes.Ldarg_0);
    generator.Emit(OpCodes.Call, propertyInfo.GetGetMethod());
    generator.Emit(OpCodes.Ret);

    propertyBuilder.SetGetMethod(builder);

    }

    • pieterderycke
      January 8, 2014 at 08:33

      Sorry for the late reply. I am happy to read that you found the solution on your own 🙂
      I hope I convinced you of the power of Reflection.Emit!

  4. Michal
    June 25, 2014 at 21:33

    And now I can start actual coding with .NET! 🙂
    I have used this tutorial for custom database binding classes with repeated code in properties – changed it into specific custom attribute…
    Somehow replaces good old C macros – just a “bit” cumbersome.
    On the other hand it is somehow better – once it is properly defined (opcodes) – I can have classes not dealing with the database stuff and then just add this “aspect”…
    Thanks.

  5. Michal
    June 25, 2014 at 22:58

    One thing I have noticed:
    The advisory IL code should not be generated for Debug configuration.
    I do not think we are going to Debug generated code.
    Especially when the code is being generated as part of the running Release build.
    Nop instructions are ‘no operations’ and we do not need to Emit them.
    Also, code generated in Debug cfg use to contain some other redundant instructions, like put on stack, pop back, jump to the next instruction, etc…
    It just makes the generating code unnecessarily longer.

  1. July 13, 2011 at 19:03
  2. July 18, 2011 at 10:14
  3. July 18, 2011 at 21:31
  4. July 28, 2011 at 10:23

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: