带有从嵌套集合中的项目动态创建的列的 DataGrid [英] DataGrid with columns created dynamically from the items in nested collection

查看:27
本文介绍了带有从嵌套集合中的项目动态创建的列的 DataGrid的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须创建一个表 看起来像这样 - 有一个固定的绑定到集合中的项目属性的列集设置为 DataGrid 的 ItemsSource,但还必须有基于嵌套集合(前面提到的集合内的集合)中的项目数创建的动态列.

对于主集合中的每个项目,这些嵌套集合中的项目数将相同.此外,一旦创建了 MainData 的实例,嵌套集合中的项目数将不会改变.此外,它们的 Name 属性将是相同的(动态列的标题应该绑定到它们),只有 Value 属性的值会不同,并且可以在重新计算主对象的值.

以下是课程的样子:

公共类 MainData{公共 ValueWithName Property1 { 获取;}公共 ValueWithName Property2 { 获取;}公共 ValueWithName Property3 { 获取;}公共 ValueWithName Property4 { 获取;}公共 ICollection嵌套数据 { 获取;}}公共类 ValueWithName{公共字符串名称{获取;}公共字符串值 { 获取;}}

在我的视图模型中,我有一个类似于下面的集合:

public ObservableCollection数据集合 { 获取;}

在视图中,我将所有固定列创建为 DataGridTextColumn:

<DataGrid.Columns><DataGridTextColumn Header="{Binding Property1.Name}";绑定={绑定属性1.值}"/><DataGridTextColumn Header="{Binding Property2.Name}";Binding="{Binding Property2.Value}";/><DataGridTextColumn Header="{Binding Property3.Name}";Binding="{Binding Property3.Value}";/><DataGridTextColumn Header="{Binding Property4.Name}";Binding="{Binding Property4.Value}";/></DataGrid.Columns></DataGrid>

现在的问题是:是否可以使用描述的结构创建这些动态列?我在这里和网上浏览了许多类似的帖子,但没有一个符合我的要求.到目前为止,我已经尝试了几种不同的方法(例如附加行为),但都没有成功.

解决方案

一段时间后,我设法使用混合行为和 EldHasp 答案中的部分代码实现了预期结果.

代码可能不是最漂亮的,但它现在似乎可以工作.这是新创建的类:

public class DataGridAttachedBehavior : Behavior{公共 IList价值观{得到 =>GetValue(FactorsProperty) as IList;设置 =>SetValue(FactorsProperty, value);}public static readonly DependencyProperty ValuesProperty =DependencyProperty.Register(nameof(Values), typeof(IList), typeof(DataGridAttachedBehavior));受保护的覆盖 void OnAttached(){base.OnAttached();AssociatedObject.Loaded += OnLoaded;AssociatedObject.LoadingRow += OnLoadingRow;}受保护的覆盖 void OnDetaching(){base.OnDetaching();AssociatedObject.Loaded -= OnLoaded;AssociatedObject.LoadingRow -= OnLoadingRow;}私有无效 AddValuesColumns(){if (Values == null || !Values.Any())返回;var columns = AssociatedObject.Columns;var headers = columns.Select(c => c.Header is TextBlock tb ? tb.Text : c.Header?.ToString());for (int i = 0; i < Values.Count; i++){ValueWithName item = Values[i];if (headers.Contains(item.Name))继续;columns.Add(new DataGridTextColumn(){标题 = item.Name,Binding = new Binding($"{nameof(MainData.NestedData)}[{i}].{nameof(ValueWithName.Value)}")});}}private void OnLoadingRow(object sender, DataGridRowEventArgs e){添加值列();}private void OnLoaded(object sender, EventArgs e){添加值列();}}

视图中只有一点点补充:

<local:DataGridAttachedBehavior Values="{Binding DataCollection/NestedData}";/></i:Interaction.Behaviors>

其余的不需要任何更改.

I have to create a table that looks like this - there is a fixed set of columns that are bound to properties of the items from a collection set as DataGrid's ItemsSource, but there also have to be dynamic columns that are created based on the number of items in the nested collection (collection inside previously mentioned collection).

The number of items in these nested collections will be the same for every item in the main collection. Additionally, once the instance of MainData is created, the number of items in the nested collection won't change. Also, their Name properties will be the same (headers of the dynamic columns should be bound to them), only the values of Value properties will be different and can change when the main object's values are recalculated.

Here's how the classes look like:

public class MainData
{
    public ValueWithName Property1 { get; }
    public ValueWithName Property2 { get; }
    public ValueWithName Property3 { get; }
    public ValueWithName Property4 { get; }
    
    public ICollection<ValueWithName> NestedData { get; }
}

public class ValueWithName
{
    public string Name { get; }
    public string Value { get; }
}

In my viewmodel I have a collection similar to the one below:

public ObservableCollection<MainData> DataCollection { get; }

In the view I created all of the fixed columns as DataGridTextColumn:

<DataGrid ItemsSource="{Binding DataCollection}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="{Binding Property1.Name}" Binding="{Binding Property1.Value}" />
        <DataGridTextColumn Header="{Binding Property2.Name}" Binding="{Binding Property2.Value}" />
        <DataGridTextColumn Header="{Binding Property3.Name}" Binding="{Binding Property3.Value}" />
        <DataGridTextColumn Header="{Binding Property4.Name}" Binding="{Binding Property4.Value}" />
    </DataGrid.Columns>
</DataGrid>

Now the question is: is it possible to create these dynamic columns using described structure? I've looked through many similar posts here and over the web, but none of them matched my requirements. Until now I've tried a few different approaches (e.g. attached behaviors) but without success.

解决方案

After some time I've managed to achieve the expected result using blend behaviors and a part of code from EldHasp's answer.

The code may not be the most beautiful but it seems to be working for now. Here's the newly created class:

public class DataGridAttachedBehavior : Behavior<DataGrid>
{
    public IList<ValueWithName> Values
    {
        get => GetValue(FactorsProperty) as IList<ValueWithName>;
        set => SetValue(FactorsProperty, value);
    }

    public static readonly DependencyProperty ValuesProperty =
        DependencyProperty.Register(nameof(Values), typeof(IList<ValueWithName>), typeof(DataGridAttachedBehavior));

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += OnLoaded;
        AssociatedObject.LoadingRow += OnLoadingRow;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Loaded -= OnLoaded;
        AssociatedObject.LoadingRow -= OnLoadingRow;
    }

    private void AddValuesColumns()
    {
        if (Values == null || !Values.Any())
            return;

        var columns = AssociatedObject.Columns;
        var headers = columns.Select(c => c.Header is TextBlock tb ? tb.Text : c.Header?.ToString());

        for (int i = 0; i < Values.Count; i++)
        {
            ValueWithName item = Values[i];

            if (headers.Contains(item.Name))
                continue;

            columns.Add(new DataGridTextColumn()
            {
                Header = item.Name,
                Binding = new Binding($"{nameof(MainData.NestedData)}[{i}].{nameof(ValueWithName.Value)}")
            });
        }
    }

    private void OnLoadingRow(object sender, DataGridRowEventArgs e)
    {
        AddValuesColumns();
    }

    private void OnLoaded(object sender, EventArgs e)
    {
        AddValuesColumns();
    }
}

There was only a small addition to the view:

<i:Interaction.Behaviors>
    <local:DataGridAttachedBehavior Values="{Binding DataCollection/NestedData}" />
</i:Interaction.Behaviors>

The rest did not require any changes.

这篇关于带有从嵌套集合中的项目动态创建的列的 DataGrid的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆