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
相关产品推荐
相关产品推荐

