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.
There are at least three ways of doing this:
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.