Как разрешить сетке данных wpf динамически генерировать столбцы на основе содержимого списка? - PullRequest
0 голосов
/ 23 ноября 2018

У меня есть коллекция объектов, и каждый объект содержит свойство коллекции типов.Моя цель - динамически генерировать столбцы таблицы данных на основе содержимого коллекции, а также создавать столбцы для оставшихся свойств, которые являются базовыми типами.Важно, чтобы bool отображался как CheckBox.

. Моя проблема заключается в том, что результирующее содержимое ячеек динамически генерируемых столбцов будет объектом (Trait в моей структуре объектов), и я хочуодно из свойств этого объекта для отображения (Trait.Value).Когда я изменяю содержимое ячейки, объект должен обновляться.

  • Я думал о DataTable, но когда я добавляю строку, мне нужен ключ столбца и значение.Когда я установил значение для пользовательского объекта, я не увидел возможности отобразить и отредактировать одно свойство пользовательского объекта.
  • Второй подход - использование динамических объектов, как в следующей статье: Автоматическое создание столбцов DataGrid из DynamicObjects , но я вижу то же решение, что и в DataTable. Дополнительная информация:
  • Я использую mvvm (когда это необходимо, я бы нарушил этот шаблон)
  • таблица данных должна быть редактируемой

Структура моего объекта:

public class Model
{
    //ItemsSource
    public ObservableCollection<Person> Persons { get; set; }
}

public class Person
{
    public string Name { get; set; }

    //Generate Treats.Count columns
    public ObservableCollection<Treat> Treats { get; set; }
}

public class Treat
{
    //column header name
    public string Name { get; set; }

    //value that should be displayed
    public string Value { get; set; }
}

Моя ViewModel.cs с примерами данных:

public class ViewModel
{
    public Model Model { get; set; }

    public ViewModel()
    {
        #region Sample Data
        Model = new Model()
        {
            Persons = new ObservableCollection<Person>()
            {
                new Person()
                {
                    Name = "Peter",
                    Treats = new ObservableCollection<Treat>()
                    {
                        new Treat()
                        {
                            Name = "Look1",
                            Value = "Nice"
                        },
                        new Treat()
                        {
                            Name = "Look2",
                            Value = "Super Nice"
                        }
                    }
                },
                new Person()
                {
                    Name = "Manuel",
                    Treats = new ObservableCollection<Treat>()
                    {
                        new Treat()
                        {
                            Name = "Look1",
                            Value = "Bad"
                        },
                        new Treat()
                        {
                            Name = "Look2",
                            Value = "Super Bad"
                        }
                    }
                }
            }
        };
        #endregion
    }
}

Информация для класса Model.cs:

  • свойство Persons - это коллекция привязок, которую следует использовать в качестве ItemsSource
  • , столбцы сетки данных должны быть сгенерированы на основе объекта Person.Один столбец для Name и n столбцов для коллекции Treats.

Результат, основанный на моих данных выборки, выглядит примерно так: Resulting DataGrid

1 Ответ

0 голосов
/ 24 ноября 2018

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

ОБЗОР:

  1. СоздайтеIvalueConverter для преобразования вашего источника данных в список раскрывающихся объектов

  2. В коде за DataGrid (событие Loaded или SourceChanged) добавьте код для генерации столбцов вручную

КОДЫ:

Создать конвертер: ЧАСТЬ 1 Сначала нам нужно получить список всех возможных столбцов, которые могут появиться (поскольку мы незнать коллекции еще)

        ObservableCollection<Person> inputlist = (ObservableCollection<Person>)value;
        List<string> PossibleColumnList = new List<string>();
        PossibleColumnList.Add(nameof(Person.Name)); //since we need name header first.
        List<string> TempColumnList = new List<string>();
        foreach (Person P in inputlist)
        {
           foreach(Treat T in P.Treats)
            {
                if (TempColumnList.Contains(T.Name) == false) TempColumnList.Add(T.Name);
            }
        }
        TempColumnList.Sort();
        PossibleColumnList.AddRange(TempColumnList); //This way we get Name first and rest of the columns in sorted out manner

Создать конвертер: ЧАСТЬ 2 .Теперь создайте объект IDictionary со всеми доступными заголовками столбцов

 IDictionary<string, object> ColumnHeaderDictionary = new Dictionary<string, object>(); 
        foreach (string columnheader in PossibleColumnList)
        {
            if (ColumnHeaderDictionary.ContainsKey(columnheader) == false) ColumnHeaderDictionary.Add(columnheader, new object());
        }

Создать конвертер: PART 3 Теперь выполните итерацию по всем лицам и создайте IDictionary для каждой модели людей.Преобразовать идиктивный объект в расширенный формат и сохранить в окончательном списке

List<ExpandoObject> FinalList = new List<ExpandoObject>();

        foreach (Person p in inputlist)
        {
            ExpandoObject tempExpando = new ExpandoObject();
            IDictionary<string, object> TempDictionary = tempExpando as IDictionary<string, object>;
            foreach (var kvp in ColumnHeaderDictionary)
            {
                TempDictionary.Add(kvp);
            }
            TempDictionary[nameof(Person.Name)] = p.Name;
            foreach(Treat t in p.Treats)
            {
                TempDictionary[t.Name] = t.Value;
            }

            FinalList.Add(tempExpando);
        }
        return FinalList;

КОД XAML:

 <Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>
<Grid x:Name="grdMain" DataContext="{Binding}">
    <DataGrid x:Name="dgMain" ItemsSource="{Binding ElementName=grdMain,Path=DataContext.Model.Persons,Converter={StaticResource NewConverter}}" Loaded="dgMain_Loaded" />
    </Grid>

КОД ЗА СОБОЙ: ВРУЧНУЮ СОЗДАТЬ КОЛОННЫ

private void dgMain_Loaded(object sender, RoutedEventArgs e)
    {
        DataGrid workinggrid = sender as DataGrid;
        ExpandoObject SingleExpando = (workinggrid.ItemsSource as List<ExpandoObject>).FirstOrDefault();
        if (workinggrid == null) workinggrid = new DataGrid();


        List<string> ColumHeaders = (SingleExpando as IDictionary<string, object>).ToList().Select(p => p.Key).ToList();

        foreach (string ColumnName in ColumHeaders)
        {
            var newcolumn = new DataGridTextColumn() { Header = ColumnName, Binding = new Binding(ColumnName) };
            workinggrid.Columns.Add(newcolumn);
        }
    }

ЗАКЛЮЧИТЕЛЬНЫЙ ВЫХОД: enter image description here

...