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