Как DataGridView получает список свойств в классе?

Когда вы устанавливаете список или, что еще лучше, BindingList в качестве источника DataGridView, он получает список свойств и использует их для создания столбцов.

Если я сделаю следующее:

        StatsList = new BindingList<ExpandoObject>();

        // Add seed record to generate columns in DataGridView
        dynamic ds = new ExpandoObject();

        ds.key = "0000";
        ds.Name = "Bob";
        ds.Number = "2255442";

        ds.key = "0001";
        ds.Name = "Nathan";
        ds.Number = "1217479";

        StatsList.Add(ds);

        dgvListView.DataSource = StatsList;

Ничего не произошло. В DataGridView никогда не добавляются столбцы или строки. Я предполагаю, что это связано с отсутствием метода, позволяющего DataGridView получить набор свойств.

Если я запущу тот же код, но заменю ExpandoObject на MyCustomClass в приведенном выше примере кода, как определено ниже, DataGridView заполнится нормально.

public class MyCustomClass
{
    public string key { get; set; }
    public string name { get; set; }
    public string number { get; set; }
}

Однако, поскольку я читаю свои данные из источника, который может измениться, мне нужно использовать динамический класс. Я пробовал несколько методов, включая BindingList BindigList>, ни один из которых не привязывает динамические элементы к столбцу. Я даже пытался создать класс, который наследует DynamicObject с переопределением для TryGetMember и TrySetMember.

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

Предостережения: я собираю данные, обобщаю их и отображаю. Мне не нужно добавлять или удалять данные после их отображения. Но я постараюсь отфильтровать и отсортировать. Однако я перейду этот мост, как только придумаю, как его показать.

В моем примере показаны простые известные значения и типы, но мои фактические исходные данные будут менее предсказуемыми, и они анализируются из JSON с использованием Newtonsoft JSON со следующей структурой:

[ { "match_number": 1, "set_number": 1, "key": "2017onbar_f1m1", "score_breakdown": { "blue": { "totalPoints": 236, "foulCount": 1, "adjustPoints": 0, «сверхурочные»: true }, «red»: { «totalPoints»: 236, «foulCount»: 1, «adjustPoints»: 0, «сверхурочные»: true } }, «teammembers»: { «blue»: { «teams ": ["участник1", "участник2", "участник3" ] }, "красный": { "команды": [ "участник4", "участник5", "участник6" ] } } }]

Однако поля score_breakdown будут различаться.


person Prdufresne    schedule 14.04.2017    source источник
comment
Какова цель использования dynamic для представления данных? DataGridView.DataSource будет коллекция любого другого строго типизированного объекта.   -  person Fabio    schedule 14.04.2017
comment
Похоже на проблему XY, можете ли вы показать, какие данные (формат) вы получаете из этого источника?   -  person Fabio    schedule 14.04.2017
comment
Исходные данные немного велики, но источником является JSON, преобразованный с помощью Newtonsoft.JSON. Некоторые из структур будут постоянными, но некоторые будут меняться.   -  person Prdufresne    schedule 14.04.2017


Ответы (2)


Попробуйте преобразовать BindingList в DataTable.

    protected void Page_Load(object sender, EventArgs e)
    {
        var StatsList = new BindingList<ExpandoObject>();

        // Add seed record to generate columns in DataGridView
        dynamic ds = new ExpandoObject();

        ds.key = "0000";
        ds.Name = "Bob";
        ds.Number = "2255442";

        dynamic ds2 = new ExpandoObject();

        ds2.key = "0001";
        ds2.Name = "Nathan";
        ds2.Number = "1217479";

        StatsList.Add(ds);
        StatsList.Add(ds2);


        GridView1.DataSource = ExpandoListToDataTable(StatsList);

        GridView1.DataBind();
    }

    protected DataTable ExpandoListToDataTable(BindingList<ExpandoObject> d)
    {
        var dt = new DataTable();

        foreach (var a in d)
        {
            foreach (var key in ((IDictionary<string, object>)a).Keys)
            {
                if (!dt.Columns.Contains(key))
                {
                    dt.Columns.Add(key);
                }
            }

            dt.Rows.Add(((IDictionary<string, object>)a).Values.ToArray());
        }

        return dt;

    }
person Jury Golubev    schedule 14.04.2017
comment
Я попробую. Спасибо. - person Prdufresne; 14.04.2017
comment
Спасибо @Jury_Golubev. Я думаю, вы подтолкнули меня в другом направлении, чем я планировал, но я думаю, что этот вариант в конце концов сработает лучше. Использование ExpandoObject, вероятно, является неправильным подходом, учитывая то, что я пытаюсь сделать, я думаю, мне будет лучше использовать список словарей с методом преобразования их для представления его в виде DataTable. Гораздо чище. Спасибо! - person Prdufresne; 15.04.2017

В случае, если данный источник данных является экземпляром некоторого типа (не экземпляром Type), установщик DatagridView.DataSource будет использовать source.GetType().GetProperties() для получения свойств, которые будут использоваться для создания столбцов.

Проблема в том, что в вашем случае тип ExpandoObject вернет пустую коллекцию

dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";

ds.GetType().GetProperties() // return empty collection

Поскольку вы знаете имя свойства во время компиляции, вы можете использовать новые функции C# и создавать кортежи

var ds = ( key: "0000", Name: "Bob", Number: "2255442" );

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

person Fabio    schedule 14.04.2017
comment
В том же коде, который я предоставил, я просто добавляю некоторые образцы данных, но в моем реальном приложении я анализирую данные из объекта JSON, поэтому свойства неизвестны до времени выполнения. Я попытался создать класс с внутренним словарем и настраиваемым методом GetPropoerties, но мне это не помогло, вероятно, потому, что я неправильно форматировал Ilist. - person Prdufresne; 14.04.2017