Я пытаюсь отобразить результаты запроса в WPF datagrid. ItemsSource вводят, я связываю с, IEnumerable<dynamic>
. Поскольку возвращенные поля не определяются до времени выполнения, я не знаю тип данных, пока запрос не оценен. Каждая "строка" возвращается как ExpandoObject
с динамическими свойствами, представляющими поля.
Это была моя надежда это AutoGenerateColumns
(как ниже), был бы в состоянии генерировать столбцы от ExpandoObject
как он делает со статическим типом, но это, кажется, не.
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Results}"/>
Там должен так или иначе сделать это декларативно, или я должен сцепиться в обязательно с некоторым 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 });
Таким образом, теперь просто должен выяснить, как связать столбцы со значениями IDictionary.
В конечном счете, мне нужно было сделать две вещи:
После этого сработала встроенная привязка данных, и, похоже, не возникло проблем с получением значений свойств из ExpandoObject
.
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Results}" />
и
// 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);
}
мой ответ из Dynamic column binding in Xaml
I've used a approach that follow the pattern of this pseudocode
columns = New DynamicTypeColumnList()
columns.Add(New DynamicTypeColumn("Name", GetType(String)))
dynamicType = DynamicTypeHelper.GetDynamicType(columns)
DynamicTypeHelper. GetDynamicType() генерирует тип с простыми свойствами. Смотрите этот пост для получения подробностей о том, как генерировать такой тип
Затем, чтобы реально использовать этот тип, сделайте что-нибудь вроде этого
Dim rows as List(Of DynamicItem)
Dim row As DynamicItem = CType(Activator.CreateInstance(dynamicType), DynamicItem)
row("Name") = "Foo"
rows.Add(row)
dataGrid.DataContext = rows
Проблема здесь в том, что clr будет создавать колонки для самого ExpandoObject - но нет никакой гарантии, что группа ExpandoObject имеет одинаковые свойства друг у друга, нет правила, по которому движок знал бы, какие колонки нужно создавать.
Возможно, что-то вроде анонимных типов Linq подойдет вам лучше. Я не знаю, какой тип сетки данных вы используете, но привязка должна быть одинаковой для всех. Вот простой пример для telerik datagrid.
ссылка на telerik форумы
На самом деле это не очень динамично, типы должны быть известны во время компиляции - но это простой способ установить что-то подобное во время выполнения.
Если вы действительно понятия не имеете, какие именно поля вы будете отображать, проблема становится немного более волнообразной. Возможные решения:
With dynamic linq вы можете создавать анонимные типы, используя строку во время выполнения - которую вы можете собрать из результатов вашего запроса. Пример использования из второй ссылки:
var orders = db.Orders.Where("OrderDate > @0", DateTime.Now.AddDays(-30)).Select("new(OrderID, OrderDate)");
В любом случае, основная идея заключается в том, чтобы каким-то образом установить сетку элементов в коллекцию объектов, чьи общие публичные свойства можно найти по отражению.
.