How to create in C# WPF a DataGrid dynamic columns binded to an observable collection of dynamic properties types at runtime

后端 未结 1 1393
情深已故
情深已故 2021-01-23 09:22

I\'m trying to create in C# WPF a DataGrid with dynamic columns binded to an observable collection of dynamic properties types created at runtime.

This is my code :

相关标签:
1条回答
  • 2021-01-23 10:17

    I faced the same issue by the past and found this solution based on the Excellent article from Kailash Chandra Behera.

    The secret relies on using System.Reflection.Emit which provides classes that allow a compiler or tool to emit metadata and Microsoft intermediate language (MSIL) and optionally generate a PE file on disk. The primary clients of these classes are script engines and compilers.

    For the curious and passionate, you can go ahead : System.Reflection.Emit Namespace and Introduction to Creating Dynamic Types with Reflection.Emit

    Solution :

    List<dynamic>, Dictionary<> , ExpandoObject cannot works because, reflection will be stalled on the first level hierarchy of your instance class MyStatiClass. The only solution I found is to create dynamically the complete MyStatiClass at runtime including as instance namespace, class name, properties names, attributes, etc. .

    This is the ViewModel code fitting your question :

    public class MainViewModel : ViewModelBase
    {
        private ObservableCollectionEx<dynamic> _myCollectionVM = new ObservableCollectionEx<dynamic>();
        public ObservableCollectionEx<dynamic> MyCollectionVM
        {
            get => _myCollectionVM;
            set => Set(nameof(MyCollectionVM), ref _myCollectionVM, value);
        }
    
        public MainViewModel()
        {
            MyClassBuilder myClassBuilder = new MyClassBuilder("DynamicClass");
            var myDynamicClass = myClassBuilder.CreateObject(new string[3] { "ID", "Name", "Address" }, new Type[3] { typeof(int), typeof(string), typeof(string) });
    
            MyCollectionVM.Add(myDynamicClass);
    
            // You can either change properties value like the following
            myDynamicClass.ID = 1;
            myDynamicClass.Name = "John";
            myDynamicClass.Address = "Hollywood boulevard";
        }
    }
    

    Remark : Compilation checks and Intellisense won't work will dynamic types, so take care of your properties syntax, otherwise you'd get an exception raised at runtime.

    Then the Dynamic Class Factory Builder which will build the full class at runtime :

    /// <summary>
    /// Dynamic Class Factory Builder
    /// </summary>
    public class MyClassBuilder
    {
        AssemblyName asemblyName;
    
        public MyClassBuilder(string ClassName)
        {
            asemblyName = new AssemblyName(ClassName);
        }
    
        public dynamic CreateObject(string[] PropertyNames, Type[] Types)
        {
            if (PropertyNames.Length != Types.Length)
            {
                throw new Exception("The number of property names should match their corresopnding types number");
            }
    
            TypeBuilder DynamicClass = CreateClass();
            CreateConstructor(DynamicClass);
            for (int ind = 0; ind < PropertyNames.Count(); ind++)
                CreateProperty(DynamicClass, PropertyNames[ind], Types[ind]);
            Type type = DynamicClass.CreateType();
    
            return Activator.CreateInstance(type);
        }
    
        private TypeBuilder CreateClass()
        {
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder typeBuilder = moduleBuilder.DefineType(asemblyName.FullName
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , null);
            return typeBuilder;
        }
    
        private void CreateConstructor(TypeBuilder typeBuilder)
        {
            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
        }
    
        private void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
        {
            FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
    
            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
            ILGenerator getIl = getPropMthdBldr.GetILGenerator();
    
            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);
    
            MethodBuilder setPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new[] { propertyType });
    
            ILGenerator setIl = setPropMthdBldr.GetILGenerator();
            Label modifyProperty = setIl.DefineLabel();
            Label exitSet = setIl.DefineLabel();
    
            setIl.MarkLabel(modifyProperty);
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);
    
            setIl.Emit(OpCodes.Nop);
            setIl.MarkLabel(exitSet);
            setIl.Emit(OpCodes.Ret);
    
            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }
    

    Enjoy it.

    0 讨论(0)
提交回复
热议问题