How do I dynamically generate columns in a WPF DataGrid? How do I dynamically generate columns in a WPF DataGrid? wpf wpf

How do I dynamically generate columns in a WPF DataGrid?


Ultimately I needed to do two things:

  1. Generate the columns manually from the list of properties returned by the query
  2. Set up a DataBinding object

After that the built-in data binding kicked in and worked fine and didn't seem to have any issue getting the property values out of the ExpandoObject.

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Results}" />

and

// Since there is no guarantee that all the ExpandoObjects have the // same set of properties, get the complete list of distinct property names// - this represents the list of columnsvar rows = dataGrid1.ItemsSource.OfType<IDictionary<string, object>>();var columns = rows.SelectMany(d => d.Keys).Distinct(StringComparer.OrdinalIgnoreCase);foreach (string text in columns){    // now set up a column and binding for each property    var column = new DataGridTextColumn     {        Header = text,        Binding = new Binding(text)    };    dataGrid1.Columns.Add(column);}


The problem here is that the clr will create columns for the ExpandoObject itself - but there is no guarantee that a group of ExpandoObjects share the same properties between each other, no rule for the engine to know which columns need to be created.

Perhaps something like Linq anonymous types would work better for you. I don't know what kind of a datagrid you are using, but binding should should be identical for all of them. Here is a simple example for the telerik datagrid.
link to telerik forums

This isn't actually truly dynamic, the types need to be known at compile time - but this is an easy way of setting something like this at runtime.

If you truly have no idea what kind of fields you will be displaying the problem gets a little more hairy. Possible solutions are:

With dynamic linq you can create anonymous types using a string at runtime - which you can assemble from the results of your query. Example usage from the second link:

var orders = db.Orders.Where("OrderDate > @0", DateTime.Now.AddDays(-30)).Select("new(OrderID, OrderDate)");

In any case, the basic idea is to somehow set the itemgrid to a collection of objects whose shared public properties can be found by reflection.


my answer from Dynamic column binding in Xaml

I've used an approach that follows the pattern of this pseudocode

columns = New DynamicTypeColumnList()columns.Add(New DynamicTypeColumn("Name", GetType(String)))dynamicType = DynamicTypeHelper.GetDynamicType(columns)

DynamicTypeHelper.GetDynamicType() generates a type with simple properties. See this post for the details on how to generate such a type

Then to actually use the type, do something like this

Dim rows as List(Of DynamicItem)Dim row As DynamicItem = CType(Activator.CreateInstance(dynamicType), DynamicItem)row("Name") = "Foo"rows.Add(row)dataGrid.DataContext = rows