You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中基于ObservableCollection动态生成可更新表格的实现方案

WPF动态行列表格实现(支持集合增删变更)

问题说明

需要将ObservableCollection<Condition>数据以表格形式展示:

  • 列头对应每个Condition的Name属性
  • 行头对应每个Property的Name属性
  • 单元格显示对应Property的Value
    要求Conditions集合增删实例、Condition.Properties增删实例时,表格能自动更新行列。

原数据结构:

public class Condition
{
    public string Name { get; set; }
    public ObservableCollection<Property> Properties { get; set; }

    public class Property
    {
        public string Name { get; set; }
        public double Value { get; set; }
    }
}

实现步骤

1. 重构ViewModel,适配行优先的表格模型

原数据是列优先结构,表格需要行优先的模型。新增行模型类,并在主ViewModel中维护行集合、列头集合,同时监听所有集合变更事件:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class TableRowViewModel : INotifyPropertyChanged
{
    public string RowHeader { get; set; }
    private readonly Dictionary<string, double> _columnValues = new();

    // 索引器,用于绑定动态列的值
    public double this[string columnName]
    {
        get => _columnValues.TryGetValue(columnName, out var val) ? val : 0;
        set
        {
            _columnValues[columnName] = value;
            OnPropertyChanged(columnName);
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class TableResultsViewViewModel : INotifyPropertyChanged
{
    public ObservableCollection<TableRowViewModel> Rows { get; } = new();
    public ObservableCollection<string> ColumnHeaders { get; } = new();
    private readonly ObservableCollection<Condition> _conditions;

    public TableResultsViewViewModel(ObservableCollection<Condition> conditions)
    {
        _conditions = conditions;
        // 初始化表格数据
        SyncTableData();
        // 监听Conditions集合变更
        _conditions.CollectionChanged += Conditions_CollectionChanged;
        // 监听每个Condition的Properties集合变更
        foreach (var cond in _conditions)
        {
            cond.Properties.CollectionChanged += Properties_CollectionChanged;
        }
    }

    private void Conditions_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        // 为新增的Condition绑定Properties变更监听
        if (e.NewItems != null)
        {
            foreach (Condition cond in e.NewItems)
            {
                cond.Properties.CollectionChanged += Properties_CollectionChanged;
            }
        }
        // 移除旧Condition的监听
        if (e.OldItems != null)
        {
            foreach (Condition cond in e.OldItems)
            {
                cond.Properties.CollectionChanged -= Properties_CollectionChanged;
            }
        }
        // 重新同步表格数据
        SyncTableData();
    }

    private void Properties_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        // 任意Properties变更时,重新同步表格
        SyncTableData();
    }

    // 将原列优先数据转换为行优先的表格数据
    private void SyncTableData()
    {
        ColumnHeaders.Clear();
        Rows.Clear();

        if (_conditions.Count == 0 || _conditions.First().Properties.Count == 0)
            return;

        // 添加所有列头
        foreach (var cond in _conditions)
        {
            ColumnHeaders.Add(cond.Name);
        }

        // 获取所有行头(所有Condition的Property名称一致)
        var rowNames = _conditions.First().Properties.Select(p => p.Name).ToList();

        // 生成每一行数据
        foreach (var rowName in rowNames)
        {
            var row = new TableRowViewModel { RowHeader = rowName };
            foreach (var cond in _conditions)
            {
                var prop = cond.Properties.First(p => p.Name == rowName);
                row[cond.Name] = prop.Value;
            }
            Rows.Add(row);
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. 实现DataGrid动态列生成

使用DataGrid替代ListView更适合动态列场景,通过后台代码监听列头集合变化,动态生成表格列:

XAML代码

<Window x:Class="WpfTableDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Dynamic Table" Height="450" Width="800">
    <Grid>
        <DataGrid x:Name="ResultDataGrid"
                  ItemsSource="{Binding Rows}"
                  AutoGenerateColumns="False"
                  Margin="10"/>
    </Grid>
</Window>

主窗口后台代码

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

public partial class MainWindow : Window
{
    private TableResultsViewViewModel _viewModel;

    public MainWindow()
    {
        InitializeComponent();
        var conditions = CreateSampleConditions();
        _viewModel = new TableResultsViewViewModel(conditions);
        DataContext = _viewModel;
        // 监听列头集合变化,动态生成列
        _viewModel.ColumnHeaders.CollectionChanged += ColumnHeaders_CollectionChanged;
        // 初始化列
        GenerateTableColumns();
    }

    private void ColumnHeaders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        GenerateTableColumns();
    }

    private void GenerateTableColumns()
    {
        ResultDataGrid.Columns.Clear();
        // 添加行头列
        ResultDataGrid.Columns.Add(new DataGridTextColumn
        {
            Header = "-",
            Binding = new System.Windows.Data.Binding("RowHeader"),
            IsReadOnly = true,
            Width = new DataGridLength(1, DataGridLengthUnitType.Auto)
        });
        // 添加动态数据列
        foreach (var colHeader in _viewModel.ColumnHeaders)
        {
            ResultDataGrid.Columns.Add(new DataGridTextColumn
            {
                Header = colHeader,
                Binding = new System.Windows.Data.Binding($"[{colHeader}]"),
                IsReadOnly = true,
                Width = new DataGridLength(1, DataGridLengthUnitType.Auto)
            });
        }
    }

    // 生成示例数据
    private ObservableCollection<Condition> CreateSampleConditions()
    {
        return new ObservableCollection<Condition>
        {
            new Condition
            {
                Name = "c1",
                Properties = new ObservableCollection<Condition.Property>
                {
                    new Condition.Property { Name = "p1", Value = 5 },
                    new Condition.Property { Name = "p2", Value = 10 },
                    new Condition.Property { Name = "p3", Value = 15 }
                }
            },
            new Condition
            {
                Name = "c2",
                Properties = new ObservableCollection<Condition.Property>
                {
                    new Condition.Property { Name = "p1", Value = 8 },
                    new Condition.Property { Name = "p2", Value = 12 },
                    new Condition.Property { Name = "p3", Value = 20 }
                }
            },
            new Condition
            {
                Name = "c3",
                Properties = new ObservableCollection<Condition.Property>
                {
                    new Condition.Property { Name = "p1", Value = 11 },
                    new Condition.Property { Name = "p2", Value = 17 },
                    new Condition.Property { Name = "p3", Value = 25 }
                }
            }
        };
    }
}

3. 功能验证

  • 新增/删除Condition实例:表格会自动添加/删除对应列
  • 新增/删除任意Condition.Properties中的Property实例:表格会自动添加/删除对应行
  • 所有数据变更都会实时同步到表格中

内容的提问来源于stack exchange,提问作者Justin8051

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 13:44:57