且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

用动态列填充数据网格

更新时间:2023-02-25 21:16:00

至少有以下三种方法:

  1. 从代码隐藏中手动修改 DataGrid 的列
  2. 使用 DataTable 作为 ItemsSource *
  3. 使用 CustomTypeDescriptor

  1. Modify the DataGrid's columns manually from code-behind
  2. Use a DataTable as the ItemsSource *
  3. Use a CustomTypeDescriptor

*为简单起见推荐


第一种方法:使用代码隐藏在运行时生成 DataGrid 的列.这很容易实现,但可能感觉有点hackish,尤其是在您使用MVVM 时.所以你的 DataGrid 有固定的列:


1st approach: use code-behind to generate the DataGrid's columns at runtime. This is simple to implement, but maybe feels a bit hackish, especially if you're using MVVM. So you'd have your DataGrid with fixed columns:

<DataGrid x:Name="grid">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding id}" Header="id" />
        <DataGridTextColumn Binding="{Binding image}" Header="image" />
    </DataGrid.Columns>
</DataGrid>

准备好名称"后,通过添加/删除列来修改网格,例如:

When you have your "Names" ready, then modify the grid by adding/removing columns, eg:

// add new columns to the data grid
void AddColumns(string[] newColumnNames)
{
    foreach (string name in newColumnNames)
    {
        grid.Columns.Add(new DataGridTextColumn { 
            // bind to a dictionary property
            Binding = new Binding("Custom[" + name + "]"), 
            Header = name 
        });
    }
}

您需要创建一个包装类,它应该包含原始类,以及一个包含自定义属性的字典.假设您的主行类是用户",那么您需要一个类似这样的包装类:

You'll want to create a wrapper class, which should contain the original class, plus a dictionary to contain the custom properties. Let's say your main row class is "User", then you'd want a wrapper class something like this:

public class CustomUser : User
{
    public Dictionary<string, object> Custom { get; set; }

    public CustomUser() : base()
    {
        Custom = new Dictionary<string, object>();
    }
}

用这个新的CustomUser"类的集合填充 ItemsSource:

Populate the ItemsSource with a collection of this new "CustomUser" class:

void PopulateRows(User[] users, Dictionary<string, object>[] customProps)
{
    var customUsers = users.Select((user, index) => new CustomUser {
        Custom = customProps[index];
    });
    grid.ItemsSource = customUsers;
}

因此将它们捆绑在一起,例如:

So tying it together, for example:

var newColumnNames = new string[] { "Name1", "Name2" };
var users = new User[] { new User { id="First User" } };
var newProps = new Dictionary<string, object>[] {
    new Dictionary<string, object> { 
        "Name1", "First Name of First User",
        "Name2", "Second Name of First User",
    },
};
AddColumns(newColumnNames);
PopulateRows(users, newProps);


第二种方法:使用 数据表.这在引擎盖下使用了自定义类型的基础设施,但更易于使用.只需将 DataGrid 的 ItemsSource 绑定到 DataTable.DefaultView 属性:


2nd approach: use a DataTable. This makes use of the custom-type infrastructure under the hood, but is easier to use. Just bind the DataGrid's ItemsSource to a DataTable.DefaultView property:

<DataGrid ItemsSource="{Binding Data.DefaultView}" AutoGenerateColumns="True" />

然后你可以定义你喜欢的列,例如:

Then you can define the columns however you like, eg:

Data = new DataTable();

// create "fixed" columns
Data.Columns.Add("id");
Data.Columns.Add("image");

// create custom columns
Data.Columns.Add("Name1");
Data.Columns.Add("Name2");

// add one row as an object array
Data.Rows.Add(new object[] { 123, "image.png", "Foo", "Bar" });


第三种方法:利用 .Net 类型系统的可扩展性.具体来说,使用 CustomTypeDescriptor.这允许您在运行时创建自定义类型;这反过来又使您能够告诉 DataGrid 您的类型具有属性Name1"、Name2"、...NameN"或您想要的任何其他属性.有关此方法的简单示例,请参阅此处.


3rd approach: make use of the extensibility of .Net's type system. Specifically, use a CustomTypeDescriptor. This allows you to create a custom type at runtime; which in turn enables you to tell the DataGrid that your type has the properties "Name1", "Name2", ... "NameN", or whatever others you want. See here for a simple example of this approach.