Как я динамично генерирую столбцы в WPF DataGrid?

Я пытаюсь отобразить результаты запроса в 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.

31
задан dkackman 2 January 2010 в 04:08
поделиться

3 ответа

В конечном счете, мне нужно было сделать две вещи:

  1. Сгенерировать столбцы вручную из списка свойств, возвращённых запросом
  2. Установить объект DataBinding

После этого сработала встроенная привязка данных, и, похоже, не возникло проблем с получением значений свойств из 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);
}
28
ответ дан 27 November 2019 в 22:38
поделиться

мой ответ из 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
4
ответ дан 27 November 2019 в 22:38
поделиться

Проблема здесь в том, что clr будет создавать колонки для самого ExpandoObject - но нет никакой гарантии, что группа ExpandoObject имеет одинаковые свойства друг у друга, нет правила, по которому движок знал бы, какие колонки нужно создавать.

Возможно, что-то вроде анонимных типов Linq подойдет вам лучше. Я не знаю, какой тип сетки данных вы используете, но привязка должна быть одинаковой для всех. Вот простой пример для telerik datagrid.
ссылка на telerik форумы

На самом деле это не очень динамично, типы должны быть известны во время компиляции - но это простой способ установить что-то подобное во время выполнения.

Если вы действительно понятия не имеете, какие именно поля вы будете отображать, проблема становится немного более волнообразной. Возможные решения:

  • Создавая отображение типов во время выполнения с помощью Reflection.Emit, я думаю, что можно создать общий конвертер значений, который будет принимать результаты ваших запросов, создавать новый тип (и поддерживать кэшированный список), и возвращать список объектов. Создание нового динамического типа будет осуществляться по тому же алгоритму, который вы уже использовали для создания ExpandoObjects

    MSDN на Reflection.Emit
    Старая, но полезная статья о коде-проекте

  • Using Dynamic Linq - это, наверное, более простой и быстрый способ.
    Using Dynamic Linq
    Getting around anonymous type headaches with dynamic linq

With dynamic linq вы можете создавать анонимные типы, используя строку во время выполнения - которую вы можете собрать из результатов вашего запроса. Пример использования из второй ссылки:

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

В любом случае, основная идея заключается в том, чтобы каким-то образом установить сетку элементов в коллекцию объектов, чьи общие публичные свойства можно найти по отражению.

.
5
ответ дан 27 November 2019 в 22:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: