如何在WPF DataGrid中动态生成列?

dka*_*man 31 wpf datagrid dynamic expandoobject

我试图在WPF数据网格中显示查询的结果.我绑定的ItemsSource类型是IEnumerable<dynamic>.由于返回的字段直到运行时才确定,因此在评估查询之前我不知道数据的类型.每个"行"都返回为ExpandoObject具有表示字段的动态属性.

我希望AutoGenerateColumns(如下所示)能够从ExpandoObject静态类型生成列,但它似乎不会.

<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Results}"/>
Run Code Online (Sandbox Code Playgroud)

无论如何要以声明方式执行此操作,还是必须使用某些C#进行必要的操作?

编辑

好的,这会给我正确的列:

// ExpandoObject implements IDictionary<string,object> 
IEnumerable<IDictionary<string, object>> rows = dataGrid1.ItemsSource.OfType<IDictionary<string, object>>();
IEnumerable<string> columns = rows.SelectMany(d => d.Keys).Distinct(StringComparer.OrdinalIgnoreCase);
foreach (string s in columns)
    dataGrid1.Columns.Add(new DataGridTextColumn { Header = s });
Run Code Online (Sandbox Code Playgroud)

所以现在只需要弄清楚如何将列绑定到IDictionary值.

dka*_*man 28

最终我需要做两件事:

  1. 从查询返回的属性列表中手动生成列
  2. 设置DataBinding对象

之后内置的数据绑定启动并运行良好,似乎没有任何问题获取属性值ExpandoObject.

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Results}" />
Run Code Online (Sandbox Code Playgroud)

// 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 columns
var 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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这很好用,但什么时候执行这段代码?在DataContextChanged上处理时,尚未设置ItemsSource (2认同)

Ego*_*gor 5

这里的问题是clr将为ExpandoObject本身创建列 - 但不能保证一组ExpandoObjects在彼此之间共享相同的属性,引擎无法知道需要创建哪些列.

也许像Linq匿名类型的东西对你来说会更好.我不知道你正在使用什么类型的数据网格,但绑定对于所有这些都应该是相同的.这是telerik数据网格的一个简单示例.
链接到telerik论坛

这实际上并不是真正动态的,需要在编译时知道类型 - 但这是在运行时设置类似这样的简单方法.

如果你真的不知道你将展示什么样的领域,问题就会变得多毛了.可能的解决方案是

使用动态linq,您可以在运行时使用字符串创建匿名类型 - 您可以根据查询结果进行汇编.第二个链接的示例用法:

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

无论如何,基本思想是以某种方式将itemgrid设置为一组对象,这些对象的共享公共属性可以通过反射找到.