我有一个需要动态填充的 Datagrid。
表格布局如下:
id | image | name | Description | Name-1 | Name-N
前 4 列是静态的,其他列是动态的。用户应该能够添加任意数量的用户。
我尝试通过将多个用户的数据放在表格中彼此相邻来比较它们。
现在我有一个列表框,其中包含动态生成的列的名称和填充静态列的方法。我还可以为每个用户加载数据。现在我需要将它们合并到一个大表中。
现在的主要问题是:如何将“用户数据”和静态内容放在一个数据网格中。
至少有三种方法可以做到这一点:
使用 CustomTypeDescriptor
*为简单起见推荐
第一种方法:使用代码隐藏在运行时生成 DataGrid 的列。这实现起来很简单,但可能会让人觉得有点骇人听闻,尤其是在您使用 MVVM 的情况下。因此,您将拥有带有固定列的 DataGrid:
<DataGrid x:Name="grid">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding id}" Header="id" />
<DataGridTextColumn Binding="{Binding image}" Header="image" />
</DataGrid.Columns>
</DataGrid>
准备好“名称”后,通过添加/删除列来修改网格,例如:
// 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
});
}
}
您需要创建一个包装类,它应该包含原始类,以及一个包含自定义属性的字典。假设您的主行类是“用户”,那么您需要一个类似这样的包装类:
public class CustomUser : User
{
public Dictionary<string, object> Custom { get; set; }
public CustomUser() : base()
{
Custom = new Dictionary<string, object>();
}
}
ItemsSource用这个新的“CustomUser”类的集合填充:
void PopulateRows(User[] users, Dictionary<string, object>[] customProps)
{
var customUsers = users.Select((user, index) => new CustomUser {
Custom = customProps[index];
});
grid.ItemsSource = customUsers;
}
因此,将其捆绑在一起,例如:
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);
第二种方法:使用DataTable。这在后台使用了自定义类型的基础架构,但更易于使用。只需将 DataGrid 绑定ItemsSource到一个DataTable.DefaultView属性:
<DataGrid ItemsSource="{Binding Data.DefaultView}" AutoGenerateColumns="True" />
然后你可以定义你喜欢的列,例如:
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”或您想要的任何其他属性。有关此方法的简单示例,请参见此处。
第二种方法:使用数据表。这在后台使用了自定义类型的基础架构,但更易于使用。只需将 DataGrid 的 ItemsSource 绑定到 DataTable.DefaultView 属性:
这几乎奏效了,但我没有绑定到 DataTable.DefaultView 属性,而是创建了一个 DataView 类型的属性并绑定到该属性。
<DataGrid ItemsSource="{Binding DataView, Mode=TwoWay}" AutoGenerateColumns="True" />
这允许绑定是双向的,绑定到 DataTable.DefaultView 不能是 TwoWay 绑定。在视图模型中
public DataView DataView
{
get { return _dataView; }
set
{
_dataView = value;
OnPropertyChanged("DataView");
}
}
通过这种设置,我不仅可以在初始化视图模型时动态定义列,还可以随时动态更新和更改数据表。在使用上述 McGarnagle 定义的方法时,当使用新数据源更新 DataTable 时,视图模式不会刷新。
我目前正在使用另一种方法。我不确定这样做是否正确,但它确实有效。我做了一个小样本。
请记住,要使其正常工作,Datagrid 中的每个条目都需要具有相同的动态列,这使其灵活性降低了一些。但是,如果您在每个条目中都有不同数量的列,那么 Datagrid 可能无论如何都是错误的方法。
这些是我的课:
public class Person
{
public ObservableCollection<Activity> Hobbys { get; set; }
public string Name { get; set; }
}
public class Activity
{
public string Name { get; set; }
}
这是背后的代码:
public MainWindow()
{
InitializeComponent();
DataContext = this;
ObservableCollection<Activity> hobbys = new ObservableCollection<Activity>();
hobbys.Add(new Activity() { Name = "Soccer" });
hobbys.Add(new Activity() { Name = "Basketball" });
Community = new ObservableCollection<Person>();
Community.Add(new Person() { Name = "James", Hobbys = hobbys });
Community.Add(new Person() { Name = "Carl", Hobbys = hobbys });
Community.Add(new Person() { Name = "Homer", Hobbys = hobbys });
MyGrid.Columns.Add(new DataGridTextColumn() { Header = "Name", Binding = new Binding("Name") }); //Static Column
int x = 0;
foreach (Activity act in Community[0].Hobbys) //Create the dynamic columns
{
MyGrid.Columns.Add(new DataGridTextColumn() { Header = "Activity", Binding = new Binding("Hobbys["+x+"].Name") });
x++;
}
}
在 .XAML 中很简单:
<DataGrid Name="MyGrid" ItemsSource="{Binding Community}" AutoGenerateColumns="False"/>
如果您不需要将其显示到单个大 DataGrid(table) 中,那么您可以拥有一个带有id、image、name、Description的DataGrid,当在该 DataGrid 上选择其中一条记录时,您可以使用与所选记录相关的图像名称