Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filling a Datagrid with dynamic Columns

I have an Datagrid which needs to get filled dynamicly.

The tablelayout is like:

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

The first 4 columns are static the others are dynamic. The User should be able to add as many users as he wants.

I try to compare data of multiple users by putting them next to each other in the table.

Right now I have an Listbox whitch containes the Names of the dynamic generated Columns and an method that filles the static columns. I also can load the datas for each User. now I need to merge them to one big Table.

The main Problem is now: How to put the "Userdata" and the static content in one datagrid.

like image 671
Wr4thon Avatar asked Aug 26 '13 20:08

Wr4thon


People also ask

How do I create a dynamic grid in WPF?

The Grid class in WPF represents a Grid control. The following code snippet creates a Grid control, sets its width, horizontal alignment, vertical alignment, show grid lines, and background color. Grid DynamicGrid = new Grid();

How do I make a DataGrid read only?

To make individual columns or cells read-only, set the DataGridColumn. IsReadOnly or DataGridCell. IsReadOnly properties. If a conflict exists between the settings at the DataGrid, column, or cell levels, a value of true takes precedence over a value of false .

How do I add a column to a data grid?

To add a column using the designer ) on the upper-right corner of the DataGridView control, and then select Add Column. In the Add Column dialog box, choose the Databound Column option and select a column from the data source, or choose the Unbound Column option and define the column using the fields provided.


1 Answers

There are at least three ways of doing this:

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

    *recommended for simplicity


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>();     } } 

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); 

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

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.

like image 118
McGarnagle Avatar answered Oct 13 '22 19:10

McGarnagle