private static void RegisterConfiguration(Type type, Configuration.IViewModelConfiguration configuration) { if (configuration == null) { configuration = (Configuration.IViewModelConfiguration)Activator.CreateInstance(typeof(Configuration.ViewModelConfiguration <>).MakeGenericType(type)); } _ViewModelMap[type] = new Lazy <Type>(() => CreateViewModelProxyType(configuration)); }
private static Type CreateViewModelProxyType(Configuration.IViewModelConfiguration configuration) { if (configuration.Commands.Count == 0 && configuration.Properties.Count == 0 && configuration.OnCreatedMethod == null) { return(configuration.Type); } var isViewModel = typeof(ViewModelBase).IsAssignableFrom(configuration.Type); var parentType = (configuration.Type ?? typeof(object)); var fullName = parentType.FullName.Append("Proxy"); ILGenerator ilGen = null; var moduleBuilder = mCreateDynamicModuleBuilder; var typeAttribs = TypeAttributes.Public; if (parentType != typeof(object)) { if (parentType.IsAbstract) { typeAttribs |= TypeAttributes.Abstract; } if (parentType.IsSealed) { typeAttribs |= TypeAttributes.Sealed; } if (parentType.IsUnicodeClass) { typeAttribs |= TypeAttributes.UnicodeClass; } } var typeBuilder = moduleBuilder.Value.DefineType(fullName, typeAttribs); typeBuilder.SetParent(parentType); var mConfigBuilder = typeBuilder.DefineMethod(".ConfigProxy", MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Private | MethodAttributes.SpecialName, typeof(void), Type.EmptyTypes); var configILGen = mConfigBuilder.GetILGenerator(); #region ParentConstructors var baseCtors = parentType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly).Where(c => !c.IsPrivate); foreach (var ctor in baseCtors) { var ctorParams = ctor.GetParameters(); var mCtorBuilder = typeBuilder.DefineConstructor(ctor.Attributes, ctor.CallingConvention, ctorParams.Select(p => p.ParameterType).ToArray()); ilGen = mCtorBuilder.GetILGenerator(); ilGen.Emit(OpCodes.Ldarg_0); for (int pIndex = 1; pIndex <= ctorParams.Length; pIndex++) { ilGen.Emit(OpCodes.Ldarg, pIndex); } ilGen.Emit(OpCodes.Call, ctor); ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Call, mConfigBuilder); ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ret); } #endregion #region Properties foreach (var propConfig in configuration.Properties) { //if (!TypeHelper.IsVirtualProperty(propConfig.BaseProperty)) //{ // throw new InvalidOperationException($"The property '{propConfig.BaseProperty.Name}' must be declared virtual."); //} var isOverridable = TypeHelper.IsVirtualProperty(propConfig.BaseProperty); var mFieldGet = propConfig.BaseProperty.GetGetMethod(true); if (mFieldGet != null && mFieldGet.IsPrivate) { mFieldGet = null; } var mFieldSet = propConfig.BaseProperty.GetSetMethod(true); if (mFieldSet != null && mFieldSet.IsPrivate) { mFieldSet = null; } var propertyBuilder = typeBuilder.DefineProperty(propConfig.BaseProperty.Name, PropertyAttributes.None, propConfig.BaseProperty.PropertyType, null); var mGetSetAttribs = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Final; if (!isOverridable) { mGetSetAttribs |= MethodAttributes.NewSlot; } #region Get var getMethodBuilder = typeBuilder.DefineMethod(string.Concat("get_", propertyBuilder.Name), mGetSetAttribs, CallingConventions.HasThis, propertyBuilder.PropertyType, Type.EmptyTypes); ilGen = getMethodBuilder.GetILGenerator(); ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ldarg_0); if (mFieldGet != null) { ilGen.Emit(OpCodes.Call, mFieldGet); ilGen.Emit(OpCodes.Ret); if (isOverridable) { typeBuilder.DefineMethodOverride(getMethodBuilder, mFieldGet); } } propertyBuilder.SetGetMethod(getMethodBuilder); #endregion #region Set if (mFieldSet != null) { var setMethodBuilder = typeBuilder.DefineMethod(string.Concat("set_", propertyBuilder.Name), mGetSetAttribs, CallingConventions.HasThis, null, new[] { propertyBuilder.PropertyType }); ilGen = setMethodBuilder.GetILGenerator(); #region CheckIfMustChange var lblRet = ilGen.DefineLabel(); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Call, mFieldGet); if (propConfig.BaseProperty.PropertyType != typeof(object)) { ilGen.Emit(OpCodes.Box, propConfig.BaseProperty.PropertyType); } ilGen.Emit(OpCodes.Ldarg_1); if (propConfig.BaseProperty.PropertyType != typeof(object)) { ilGen.Emit(OpCodes.Box, propConfig.BaseProperty.PropertyType); } ilGen.Emit(OpCodes.Call, mObjectEquals.Value); ilGen.Emit(OpCodes.Ldc_I4_1); ilGen.Emit(OpCodes.Ceq); ilGen.Emit(OpCodes.Brtrue_S, lblRet); ilGen.Emit(OpCodes.Nop); #endregion #region Changing if (propConfig.ChangingMethod != null) { ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Call, propConfig.ChangingMethod); } #endregion #region SetProperty ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Call, mFieldSet); #endregion #region ValidateProperty if (isViewModel && mValidateProperty.Value != null) { ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Ldstr, propertyBuilder.Name); ilGen.Emit(OpCodes.Callvirt, mValidateProperty.Value); } #endregion #region NotifyPropertyChanged ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Ldstr, propertyBuilder.Name); ilGen.Emit(OpCodes.Callvirt, mRaisePropertyChanged.Value); #endregion #region Changed if (propConfig.ChangedMethod != null) { ilGen.Emit(OpCodes.Nop); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Call, propConfig.ChangedMethod); } #endregion #region DependencyProperties if (propConfig.DependencyProperties.Count > 0) { ilGen.Emit(OpCodes.Nop); foreach (var name in propConfig.DependencyProperties) { ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Ldstr, name); ilGen.Emit(OpCodes.Callvirt, mRaisePropertyChanged.Value); } } #endregion ilGen.MarkLabel(lblRet); ilGen.Emit(OpCodes.Ret); if (isOverridable) { typeBuilder.DefineMethodOverride(setMethodBuilder, mFieldSet); } propertyBuilder.SetSetMethod(setMethodBuilder); } #endregion #region DefaultValue if (propConfig.HasDefaultValue) { var valueType = propConfig.DefaultValue.GetType(); configILGen.Emit(OpCodes.Nop); configILGen.Emit(OpCodes.Ldarg_0); #region Type if (propConfig.DefaultValue is Type) { var dvType = (Type)propConfig.DefaultValue; if (dvType.IsArray) { configILGen.Emit(OpCodes.Ldc_I4_0); configILGen.Emit(OpCodes.Newarr, dvType.GetElementType()); } else { var pCtor = dvType.GetConstructor(Type.EmptyTypes); configILGen.Emit(OpCodes.Newobj, pCtor); } } #endregion #region String else if (valueType == typeof(string)) { if (propConfig.BaseProperty.PropertyType == typeof(string)) { EmitPushValue(propConfig.DefaultValue, configILGen); } else if ("".Equals(propConfig.DefaultValue)) { var pCtor = propConfig.BaseProperty.PropertyType.GetConstructor(Type.EmptyTypes); configILGen.Emit(OpCodes.Newobj, pCtor); } } #endregion #region DateTime else if (TypeHelper.IsDateTime(propConfig.BaseProperty.PropertyType)) { var ticks = ((DateTime)propConfig.DefaultValue).Ticks; EmitPushValue(ticks, configILGen); var pCtor = typeof(DateTime).GetConstructor(new[] { typeof(long) }); if (propConfig.BaseProperty.PropertyType == typeof(DateTime?)) { configILGen.Emit(OpCodes.Newobj, pCtor); pCtor = typeof(DateTime?).GetConstructor(new[] { typeof(DateTime) }); } configILGen.Emit(OpCodes.Newobj, pCtor); } #endregion #region Decimal else if (TypeHelper.IsDecimalType(propConfig.BaseProperty.PropertyType)) { var decimalValue = (valueType == typeof(decimal) ? (decimal)propConfig.DefaultValue : Convert.ToDecimal(propConfig.DefaultValue)); var bits = decimal.GetBits(decimalValue); bool sign = (bits[3] & 0x80000000) != 0; int scale = (byte)((bits[3] >> 16) & 0x7F); configILGen.Emit(OpCodes.Ldc_I4, bits[0]); configILGen.Emit(OpCodes.Ldc_I4, bits[1]); configILGen.Emit(OpCodes.Ldc_I4, bits[2]); configILGen.Emit((sign ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); configILGen.Emit(OpCodes.Ldc_I4, scale); var pCtor = typeof(decimal).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) }); if (propConfig.BaseProperty.PropertyType == typeof(decimal?)) { configILGen.Emit(OpCodes.Newobj, pCtor); pCtor = typeof(decimal?).GetConstructor(new[] { typeof(decimal) }); } configILGen.Emit(OpCodes.Newobj, pCtor); } #endregion #region Guid else if (valueType == typeof(Guid)) { var bytes = ((Guid)propConfig.DefaultValue).ToByteArray(); bytes.ForEach(b => EmitPushValue(b, configILGen)); var pCtor = typeof(Guid).GetConstructor(new[] { typeof(byte[]) }); if (propConfig.BaseProperty.PropertyType == typeof(Guid?)) { configILGen.Emit(OpCodes.Newobj, pCtor); pCtor = typeof(Guid?).GetConstructor(new[] { typeof(Guid) }); } configILGen.Emit(OpCodes.Newobj, pCtor); } #endregion #region TimeSpan else if (valueType == typeof(TimeSpan)) { var ticks = ((TimeSpan)propConfig.DefaultValue).Ticks; EmitPushValue(ticks, configILGen); var pCtor = typeof(TimeSpan).GetConstructor(new[] { typeof(long) }); if (propConfig.BaseProperty.PropertyType == typeof(TimeSpan?)) { configILGen.Emit(OpCodes.Newobj, pCtor); pCtor = typeof(TimeSpan?).GetConstructor(new[] { typeof(TimeSpan) }); } configILGen.Emit(OpCodes.Newobj, pCtor); } #endregion #region IntPtr else if (valueType == typeof(IntPtr)) { var lPtr = ((IntPtr)propConfig.DefaultValue).ToInt64(); EmitPushValue(lPtr, configILGen); var pCtor = typeof(IntPtr).GetConstructor(new[] { typeof(long) }); if (propConfig.BaseProperty.PropertyType == typeof(IntPtr?)) { configILGen.Emit(OpCodes.Newobj, pCtor); pCtor = typeof(IntPtr?).GetConstructor(new[] { typeof(IntPtr) }); } configILGen.Emit(OpCodes.Newobj, pCtor); } #endregion #region UIntPtr else if (valueType == typeof(UIntPtr)) { var ulPtr = ((UIntPtr)propConfig.DefaultValue).ToUInt64(); EmitPushValue(ulPtr, configILGen); var pCtor = typeof(UIntPtr).GetConstructor(new[] { typeof(ulong) }); if (propConfig.BaseProperty.PropertyType == typeof(UIntPtr?)) { configILGen.Emit(OpCodes.Newobj, pCtor); pCtor = typeof(UIntPtr?).GetConstructor(new[] { typeof(UIntPtr) }); } configILGen.Emit(OpCodes.Newobj, pCtor); } #endregion #region Otherwise else { EmitPushValue(propConfig.DefaultValue, configILGen); if (!EmitCanPushType(propConfig.BaseProperty.PropertyType)) { var pCtor = propConfig.BaseProperty.PropertyType.GetConstructor(new Type[] { valueType }); configILGen.Emit(OpCodes.Newobj, pCtor); } } #endregion configILGen.Emit(OpCodes.Call, mFieldSet); configILGen.Emit(OpCodes.Nop); } #endregion } #endregion #region Commands if (configuration.Commands.Count > 0) { foreach (var cmdConfig in configuration.Commands) { if (cmdConfig.Method.IsPrivate) { throw new InvalidOperationException($"The method '{cmdConfig.Method.Name}' must be declared public or protected."); } var fieldBuilder = typeBuilder.DefineField(string.Concat("_", cmdConfig.Name), typeof(ICommand), FieldAttributes.Private | FieldAttributes.InitOnly); #region CommandProperty var propertyBuilder = typeBuilder.DefineProperty(cmdConfig.Name, PropertyAttributes.None, typeof(ICommand), null); { var getMethodBuilder = typeBuilder.DefineMethod( string.Concat("get_", propertyBuilder.Name), MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, typeof(ICommand), Type.EmptyTypes); ilGen = getMethodBuilder.GetILGenerator(); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Ldfld, fieldBuilder); ilGen.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getMethodBuilder); } #endregion #region CommandMethod { Type cmdType = null, cmdParamType = null; var cmdParamTypes = new List <Type>(); configILGen.Emit(OpCodes.Ldarg_0); configILGen.Emit(OpCodes.Ldarg_0); configILGen.Emit(OpCodes.Ldftn, cmdConfig.Method); if (cmdConfig.FirstParameterType != null) { cmdParamType = typeof(Action <>).MakeGenericType(cmdConfig.FirstParameterType); cmdType = typeof(DelegateCommand <>).MakeGenericType(cmdConfig.FirstParameterType); configILGen.Emit(OpCodes.Newobj, cmdParamType.GetConstructors()[0]); cmdParamTypes.Add(cmdParamType); if (cmdConfig.CanExecuteMethod != null) { configILGen.Emit(OpCodes.Ldarg_0); configILGen.Emit(OpCodes.Ldftn, cmdConfig.CanExecuteMethod); var canCmdType = typeof(Func <,>).MakeGenericType(cmdConfig.FirstParameterType, typeof(bool)); configILGen.Emit(OpCodes.Newobj, canCmdType.GetConstructors()[0]); cmdParamTypes.Add(canCmdType); } } else { cmdParamType = typeof(Action); cmdType = typeof(DelegateCommand); configILGen.Emit(OpCodes.Newobj, cmdParamType.GetConstructors()[0]); cmdParamTypes.Add(cmdParamType); if (cmdConfig.CanExecuteMethod != null) { configILGen.Emit(OpCodes.Ldarg_0); configILGen.Emit(OpCodes.Ldftn, cmdConfig.CanExecuteMethod); var canCmdType = typeof(Func <>).MakeGenericType(typeof(bool)); configILGen.Emit(OpCodes.Newobj, canCmdType.GetConstructors()[0]); cmdParamTypes.Add(canCmdType); } } configILGen.Emit(OpCodes.Newobj, cmdType.GetConstructor(cmdParamTypes.ToArray())); configILGen.Emit(OpCodes.Stfld, fieldBuilder); } #endregion } configILGen.Emit(OpCodes.Nop); } #endregion #region OnCreatedMethod if (configuration.OnCreatedMethod != null) { configILGen.Emit(OpCodes.Nop); configILGen.Emit(OpCodes.Ldarg_0); configILGen.Emit(OpCodes.Call, configuration.OnCreatedMethod); } #endregion configILGen.Emit(OpCodes.Ret); return(typeBuilder.CreateType()); }