如何在C#WPF中创建一个DataGrid动态列,该列在运行时绑定到动态属性类型的可观察集合

如何在C#WPF中创建一个DataGrid动态列,该列在运行时绑定到动态属性类型的可观察集合,c#,dynamic,properties,binding,datagrid,C#,Dynamic,Properties,Binding,Datagrid,我试图在C#WPF中创建一个数据网格,其中动态列绑定到运行时创建的动态属性类型的可观察集合 这是我的代码: 查看WPF <DataGrid ItemsSource="{Binding MyCollectionVM, Mode=OneWay}" AutoGenerateColumns="True"> </DataGrid> 然后在我的视图模型中: public class MyStatiClass { public int ID { get;

我试图在C#WPF中创建一个数据网格,其中动态列绑定到运行时创建的动态属性类型的可观察集合

这是我的代码:

查看WPF

<DataGrid
    ItemsSource="{Binding MyCollectionVM, Mode=OneWay}"
    AutoGenerateColumns="True">
</DataGrid>

然后在我的视图模型中:

public class MyStatiClass
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
}

// Main View Model, using MVVMLight library
public class MainViewModel : ViewModelBase
{
    private ObservableCollection<MyStatiClass> _myCollectionVM = new ObservableCollection<MyStatiClass>();
    public ObservableCollection<MyStatiClass> MyCollectionVM
    {
        get => _myCollectionVM;
        set => Set(nameof(MyCollectionVM), ref _myCollectionVM, value);
    }

    public MainViewModel()
    {
        MyCollectionVM.Add(new MyStatiClass() { ID = 1, Name = "Name1", Address = "15 Hollywood Street"});
    }
}
公共类MyStatiClass
{
公共int ID{get;set;}
公共字符串名称{get;set;}
公共字符串地址{get;set;}
}
//主视图模型,使用MVVMLight库
公共类MainViewModel:ViewModelBase
{
私有ObservableCollection_myCollectionVM=新ObservableCollection();
公共可观察收集MyCollectionVM
{
get=>\u myCollectionVM;
set=>set(nameof(MyCollectionVM),ref\u MyCollectionVM,value);
}
公共主视图模型()
{
MyCollectionVM.Add(新的MyStatiClass(){ID=1,Name=“Name1”,Address=“15 Hollywood Street”});
}
}
MyStatiClass
包含三个属性作为示例,但我希望在运行时动态生成所需的属性。这些属性将在其他地方生成,以满足某些业务需求

我尝试了几种方法,比如使用
列表
字典
扩展对象
,但是每次,使用反射的
DataGrid
都显示在类型
MyStatiClass
中传递的第一级属性,而不是我想要的
MyStatiClass
的真实属性

我的问题是,我该怎么做

谢谢你的帮助。
关于

我过去也遇到过同样的问题,并根据来自的优秀文章找到了这个解决方案

这个秘密依赖于使用
System.Reflection.Emit
,它提供的类允许编译器或工具发出元数据和Microsoft中间语言(MSIL),并可以选择在磁盘上生成PE文件。这些类的主要客户端是脚本引擎和编译器

对于好奇和热情的人,你可以继续:和

解决方案

public class MyStatiClass
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
}

// Main View Model, using MVVMLight library
public class MainViewModel : ViewModelBase
{
    private ObservableCollection<MyStatiClass> _myCollectionVM = new ObservableCollection<MyStatiClass>();
    public ObservableCollection<MyStatiClass> MyCollectionVM
    {
        get => _myCollectionVM;
        set => Set(nameof(MyCollectionVM), ref _myCollectionVM, value);
    }

    public MainViewModel()
    {
        MyCollectionVM.Add(new MyStatiClass() { ID = 1, Name = "Name1", Address = "15 Hollywood Street"});
    }
}
List
Dictionary
ExpandoObject
无法工作,因为反射将在实例类的第一级层次结构上暂停
mystaticclass
。 我找到的唯一解决方案是在运行时动态创建完整的
MyStatiClass
,包括实例名称空间、类名、属性名、属性等

这是适合您问题的ViewModel代码:

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";
    }
}
/// <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);
    }
}
public类MainViewModel:ViewModelBase
{
私有ObservableCollectionEx_myCollectionVM=新ObservableCollectionEx();
公共可观测集合Ex MyCollectionVM
{
get=>\u myCollectionVM;
set=>set(nameof(MyCollectionVM),ref\u MyCollectionVM,value);
}
公共主视图模型()
{
MyClassBuilder MyClassBuilder=新的MyClassBuilder(“动态类”);
var myDynamicClass=myClassBuilder.CreateObject(新字符串[3]{“ID”、“Name”、“Address”},新类型[3]{typeof(int)、typeof(string)、typeof(string)});
MyCollectionVM.Add(myDynamicClass);
//您可以按如下方式更改属性值
myDynamicClass.ID=1;
myDynamicClass.Name=“John”;
myDynamicClass.Address=“好莱坞大道”;
}
}
备注:编译检查和Intellisense对于动态类型无效,因此请注意属性语法,否则会在运行时引发异常

然后是动态类工厂生成器,它将在运行时构建完整的类:

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";
    }
}
/// <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);
    }
}
//
///动态类工厂生成器
/// 
公共类MyClassBuilder
{
AssemblyName;
公共MyClassBuilder(字符串类名称)
{
asemblyName=新程序集名称(类名称);
}
公共动态CreateObject(字符串[]属性名称,类型[]类型)
{
if(PropertyNames.Length!=类型.Length)
{
抛出新异常(“属性名称的数量应与其对应的提示类型编号相匹配”);
}
TypeBuilder DynamicClass=CreateClass();
CreateConstructor(DynamicClass);
对于(int ind=0;ind