如何让WPF DataGrid基于列表的内容动态生成列?

时间:2018-11-23 08:29:04

标签: c# wpf datagrid wpfdatagrid

我有一个对象集合,每个对象都包含类型集合的一个属性。我的目标是根据集合内容动态生成datagrid列,并为其余属性(即基本类型)生成列。将bool显示为CheckBox是很重要的。

我的问题是:动态生成的列的最终单元格内容将是一个对象(我的对象结构中的Trait),并且我希望显示其中一个对象属性(Trait.Value) 。当我更改单元格的内容时,后面的对象应该更新。

  • 我想到了DataTable,但是当我添加一行时,我需要列键和值。将值设置为自定义对象时,看不到显示和编辑该自定义对象的单个属性的可能性。
  • 第二种方法将使用动态对象,如下面的文章中所示: Auto-Generating DataGrid Columns From 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的一列,集合Treats的n列。

基于我的样本数据的结果是这样的: Resulting DataGrid

1 个答案:

答案 0 :(得分:1)

既然如此,您已经说过可以破坏MVVM模式,请尝试以下方法。

概述:

  1. 创建一个IvalueConverter,将您的商品来源转换为expandoobejcts列表

  2. 在DataGrid(已加载事件或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());
        }

创建转换器:第3部分现在遍历所有人,并为每个人模型创建一个IDictionary。将字典转换为expando对象并存储在最终列表中

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