使用动态列填充Datagrid

时间:2013-08-26 20:03:53

标签: c# .net wpf datagrid

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

tablelayout就像:

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

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

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

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

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

4 个答案:

答案 0 :(得分:32)

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

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

    *为简单起见建议


  5. 第一种方法:使用代码隐藏在运行时生成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>
    

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

    // 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" });
    

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

答案 1 :(得分:4)

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

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

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

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

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

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

答案 2 :(得分:1)

我当前正在使用另一种方法,我不确定这样做是否正确,但是它可以工作。我做了一个小样本。

请记住,要执行此操作,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"/>

答案 3 :(得分:0)

如果您不需要将其显示在一个大的DataGrid(表格)中,那么您可以使用 DataGrid id,image,name,Description 在该DataGrid上选择记录,然后显示/刷新ListBox,其中包含与所选记录相关的图像名称