用动态列填充数据网格

本文关键字:数据网 网格 数据 填充 动态 | 更新日期: 2023-09-27 18:29:03

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

表格布局如下:

id | image | name | Description | Name-1 | Name-N

前4列是静态的,其他列是动态的。用户应该能够添加任意数量的用户。

我试图通过将多个用户的数据放在表中的相邻位置来比较它们。

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

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

用动态列填充数据网格

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

  1. 从代码隐藏手动修改DataGrid的列
  2. 使用DataTable作为ItemsSource*
  3. 使用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 
        });
    }
}

您将希望创建一个包装类,该包装类应包含原始类,再加上一个包含自定义属性的字典。假设你的主行类是"User",那么你想要一个包装类,比如:

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

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

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",或者其他你想要的名字。有关此方法的简单示例,请参见此处。

第二种方法:使用DataTable。这就利用了后台的自定义类型基础设施,但更易于使用。只需将DataGrid的ItemsSource绑定到DataTable.DefaultView属性:

这几乎奏效了,但我没有绑定到DataTable.DefaultView属性属性,而是创建了一个DataView类型的属性并绑定到该属性。

<DataGrid ItemsSource="{Binding DataView, Mode=TwoWay}" AutoGenerateColumns="True" />

这允许绑定是双向的,绑定到DataTable.DefaultView不能是双向绑定。视图模型

    public DataView DataView
    {
        get { return _dataView; }
        set
        {
            _dataView = value;
            OnPropertyChanged("DataView");
        }
    }

有了这个设置,我不仅可以在视图模型初始化时动态定义列,还可以随时动态更新和更改数据表。在使用上面由McGarnagle定义的方法时,当使用新的数据源更新DataTable时,视图模式没有刷新。

我目前正在使用另一种方法。我不确定这样做是否正确,但它有效。我做了一个小样品。

请记住,为了实现这一点,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(表)中,那么您可以有一个带有id、image、name、DescriptionDataGrid,当在该DataGrid上选择了其中一条记录时,您将显示/刷新一个带有与所选记录相关的图像名称的ListBox