Here’s a useful helper to build a dynamic wrapper / facade class from an arbitrary collection of properties.  The wrapper implements INotifiyPropertyChanged so can be used with two-way databinding in WPF, Silverlight and ASP.NET (probably other places too):

/// <summary>
/// IDynamicPropertyProvider interface
/// </summary>
public interface IDynamicPropertyProvider
{
    object GetPropertyValue( string name );
    void SetPropertyValue( string name, object value );
    event PropertyChangedEventHandler ValueChanged;
}

/// <summary>
/// DynamicTypeFactory class
/// </summary>
public class DynamicTypeFactory
{
    #region DynamicBase class

    /// <summary>
    /// Base for dynamic class
    /// </summary>
    public class DynamicBase : INotifyPropertyChanged, IDisposable
    {
        private IDynamicPropertyProvider provider;

        public DynamicBase( IDynamicPropertyProvider provider )
        {
            this.provider = provider;

            if ( provider != null ) provider.ValueChanged += OnPropertyChanged;
        }

        protected object GetValue( string name ) { return provider.GetPropertyValue( name ); }

        protected void SetValue( string name, object value )
        {
            provider.SetPropertyValue( name, value );
        }

        private void OnPropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            if ( PropertyChanged != null ) PropertyChanged( this, e );
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if ( provider != null ) provider.ValueChanged -= OnPropertyChanged;
        }

        #endregion
    }

    #endregion

    /// <summary>
    /// Creates the type.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <param name="properties">The properties.</param>
    /// <returns></returns>
    public static Type CreateType( string name, IEnumerable<string> properties )
    {
        // Define dynamic assembly
        var assemblyName = new AssemblyName( "DynamicAssembly" );

        var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, AssemblyBuilderAccess.Run );
        var module = assemblyBuilder.DefineDynamicModule( "DynamicModule" );

        // Define type
        var typeBuilder = module.DefineType(
            name,
            TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
            typeof( DynamicBase ) );

        var baseConstructor = typeof( DynamicBase ).GetConstructor( new[] { typeof( IDynamicPropertyProvider ) } );

        // Define constructor
        var constructor = typeBuilder.DefineConstructor(
            MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
            CallingConventions.Standard,
            new[] { typeof( IDynamicPropertyProvider ) } );

        var ilGen = constructor.GetILGenerator();
        ilGen.Emit( OpCodes.Ldarg_0 );
        ilGen.Emit( OpCodes.Ldarg_1 );
        ilGen.Emit( OpCodes.Call, baseConstructor );
        ilGen.Emit( OpCodes.Ret );

        // Prepare to define properties
        var getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
        var getMethodInfo = typeof( DynamicBase ).GetMethod( "GetValue", BindingFlags.NonPublic | BindingFlags.Instance );
        var setMethodInfo = typeof( DynamicBase ).GetMethod( "SetValue", BindingFlags.NonPublic | BindingFlags.Instance );

        foreach ( var property in properties )
        {
            // Define property get method
            var getMethod = typeBuilder.DefineMethod( "get_" + property, getSetAttr, typeof( object ), Type.EmptyTypes );
            ilGen = getMethod.GetILGenerator();
            ilGen.Emit( OpCodes.Ldarg_0 );
            ilGen.Emit( OpCodes.Ldstr, property );
            ilGen.Emit( OpCodes.Callvirt, getMethodInfo );
            ilGen.Emit( OpCodes.Ret );

            // Define property set method
            var setMethod = typeBuilder.DefineMethod( "set_" + property, getSetAttr, null, new[] { typeof( object ) } );
            ilGen = setMethod.GetILGenerator();
            ilGen.Emit( OpCodes.Ldarg_0 );
            ilGen.Emit( OpCodes.Ldstr, property );
            ilGen.Emit( OpCodes.Ldarg_1 );
            ilGen.Emit( OpCodes.Callvirt, setMethodInfo );
            ilGen.Emit( OpCodes.Ret );

            // Define property
            var propertyBuilder = typeBuilder.DefineProperty( property, PropertyAttributes.HasDefault, typeof( object ), Type.EmptyTypes );

            propertyBuilder.SetGetMethod( getMethod );
            propertyBuilder.SetSetMethod( setMethod );
        }

        // Materialize type
        return typeBuilder.CreateType();
    }
}

To use it you’ll need to implement the IDynamicPropertyProvider interface on your own “provider” class. Its job is to get and set a property value and raise “ValueChanged” events when values are changed (externally or by SetPropertyValue).

A basic provider could look something like this (see CreateDynamicWrapper for usage details):

public class MyDynamicProvider : IDynamicPropertyProvider
{
    public Dictionary<string, object> MyValues { get; private set; }

    public MyDynamicProvider()
    {
        MyValues = new Dictionary<string, object>();
    }

    public object CreateDynamicWrapper()
    {
        var type = DynamicTypeFactory.CreateType( "MyWrapper", MyValues.Keys.ToArray() );
        return Activator.CreateInstance( type, this );
    }

    public object GetPropertyValue( string name )
    {
        return MyValues[ name ];
    }

    public void SetPropertyValue( string name, object value )
    {
        MyValues[ name ] = value;
        if ( ValueChanged != null ) ValueChanged( this, new PropertyChangedEventArgs( name ) );
    }

    public event PropertyChangedEventHandler ValueChanged;
}

From there of course you can bind directly to the wrapper in XAML, ASP.NET or whatever and maybe avoid some typing :)

About these ads