使用动态列填充Datagrid

Wr4*_*hon 24 .net c# wpf datagrid

我有一个需要动态填充的Datagrid.

tablelayout就像:

id | image | name | Description | Name-1 | Name-N
Run Code Online (Sandbox Code Playgroud)

前4列是静态的,其他列是动态的.用户应该能够根据需要添加尽可能多的用户.

我尝试通过将多个用户的数据放在表中彼此相邻来比较它们的数据.

现在我有一个Listbox whitch包含动态生成的Columns的名称和一个填充静态列的方法.我也可以为每个用户加载数据.现在我需要将它们合并到一个大表中.

现在的主要问题是:如何将"Userdata"和静态内容放在一个数据网格中.

McG*_*gle 37

至少有三种方法可以做到这一点:

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

    *建议简单


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

<DataGrid x:Name="grid">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding id}" Header="id" />
        <DataGridTextColumn Binding="{Binding image}" Header="image" />
    </DataGrid.Columns>
</DataGrid>
Run Code Online (Sandbox Code Playgroud)

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

// 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 
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

    public CustomUser() : base()
    {
        Custom = new Dictionary<string, object>();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

所以把它绑在一起,例如:

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);
Run Code Online (Sandbox Code Playgroud)

第二种方法:使用DataTable.这使用了引擎盖下的自定义类型基础结构,但更易于使用.只需将DataGrid绑定ItemsSourceDataTable.DefaultView属性:

<DataGrid ItemsSource="{Binding Data.DefaultView}" AutoGenerateColumns="True" />
Run Code Online (Sandbox Code Playgroud)

然后您可以根据需要定义列,例如:

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" });
Run Code Online (Sandbox Code Playgroud)

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

  • 我发现DataTable方法是最简单,最有效的解决方案. (4认同)
  • 有没有人使用方法3成功在DataGrid中自动生成列?我已经定义了一个自定义类型描述符,可以很好地绑定到它的属性,但是自动生成列不起作用。 (3认同)
  • 这似乎只有在初始化控件之前添加数据列时才有效。如果稍后更改列,如何使其反映在网格中? (2认同)

小智 5

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

这几乎可以工作,但是我没有绑定到DataTable.DefaultView属性,而是创建了DataView类型的属性并绑定到该属性。

<DataGrid ItemsSource="{Binding DataView, Mode=TwoWay}" AutoGenerateColumns="True" />
Run Code Online (Sandbox Code Playgroud)

这允许绑定有两种方式,绑定到DataTable.DefaultView不能是TwoWay绑定。在视图模型中

    public DataView DataView
    {
        get { return _dataView; }
        set
        {
            _dataView = value;
            OnPropertyChanged("DataView");
        }
    }
Run Code Online (Sandbox Code Playgroud)

通过这种设置,我不仅可以在初始化视图模型时动态定义列,而且可以随时动态更新和更改数据表。在使用上述McGarnagle定义的方法时,使用新数据源更新DataTable时,视图架构没有刷新。