WinUI3 DataGrid修改单元格A后单元格B不更新问题求助
我在WinUI3项目中使用CommunityToolkit.WinUI.UI.Controls.DataGrid,将ObservableCollection<ExampleClass>绑定到DataGrid。ExampleClass包含字段Amount int, Price int, TotalPrice int(对应德语Anzahl、Preis、TotalPreis)。我给DataGrid添加了CellEditEnded事件,用于将TotalPrice更新为Amount * Price。
运行时用户修改Anzahl或Preis后,ObservableCollection中的TotalPreis值已正确更新,但DataGrid的TotalPreis单元格仍显示旧值(例如初始Preis为10,修改Anzahl为2后仍显示0)。只有刷新页面或开始编辑TotalPreis单元格时,才会显示正确值;手动修改TotalPreis时也会自动恢复为计算后的正确值。
尝试过修改绑定模式、UpdateSourceTrigger等XAML属性,均无效,寻求解决方案(临时方案也可接受)。
相关XAML代码
<controls:DataGrid AutoGenerateColumns="False" GridLinesVisibility="Horizontal" ItemsSource="{x:Bind ViewModel.kgr.Tabelle1, Mode=OneWay}" Grid.Column="0" Grid.Row="0" CellEditEnded="DataGrid_CellEditEnded"> <controls:DataGrid.Resources> <SolidColorBrush x:Key="DataGridColumnHeaderBackgroundColor" Color="Transparent" /> </controls:DataGrid.Resources> <controls:DataGrid.Columns> <controls:DataGridTextColumn Binding="{Binding Name}" Header="Bereich" /> <controls:DataGridTextColumn Binding="{Binding Anzahl}" Header="Fläche in m^2" /> <controls:DataGridTextColumn Binding="{Binding Preis}" Header="€ pro m^2" /> <controls:DataGridTextColumn Binding="{Binding TotalPreis}" Header="Gesamt"/> <!-- IsReadOnly="True" --> </controls:DataGrid.Columns> </controls:DataGrid>
解决方案
1. 根本解决:让实体类实现INotifyPropertyChanged
ObservableCollection仅监听集合的增删改操作,不会自动感知集合内对象的属性变化。让ExampleClass实现INotifyPropertyChanged接口,在属性更新时触发通知,UI会自动刷新对应的单元格。
public class ExampleClass : INotifyPropertyChanged { private int _anzahl; public int Anzahl { get => _anzahl; set { if (_anzahl != value) { _anzahl = value; OnPropertyChanged(nameof(Anzahl)); UpdateTotalPreis(); } } } private int _preis; public int Preis { get => _preis; set { if (_preis != value) { _preis = value; OnPropertyChanged(nameof(Preis)); UpdateTotalPreis(); } } } private int _totalPreis; public int TotalPreis { get => _totalPreis; set { if (_totalPreis != value) { _totalPreis = value; OnPropertyChanged(nameof(TotalPreis)); } } } private void UpdateTotalPreis() { TotalPreis = Anzahl * Preis; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
使用这种方式后,无需依赖CellEditEnded事件,修改Anzahl或Preis时会自动计算并更新TotalPreis,UI同步刷新,符合MVVM设计模式。
2. 临时方案:手动刷新TotalPreis单元格
如果暂时不想修改实体类,可以在CellEditEnded事件中手动找到对应单元格并强制刷新绑定:
private void DataGrid_CellEditEnded(object sender, DataGridCellEditEndedEventArgs e) { var dataGrid = sender as DataGrid; if (dataGrid == null || e.Row.DataContext == null) return; var item = e.Row.DataContext as ExampleClass; if (item == null) return; // 计算更新TotalPreis item.TotalPreis = item.Anzahl * item.Preis; // 定位TotalPreis列 var totalColumn = dataGrid.Columns.FirstOrDefault(c => c.Header.ToString() == "Gesamt"); if (totalColumn == null) return; // 获取对应单元格并刷新绑定 var cell = dataGrid.GetCell(e.Row, totalColumn); cell?.BindingExpression?.UpdateTarget(); } // 扩展方法:获取指定行和列的DataGridCell public static DataGridCell GetCell(this DataGrid dataGrid, DataGridRow row, DataGridColumn column) { if (row == null || column == null) return null; var presenter = row.FindVisualChild<DataGridCellsPresenter>(); if (presenter == null) { row.ApplyTemplate(); presenter = row.FindVisualChild<DataGridCellsPresenter>(); } return presenter != null ? column.GetCellContent(presenter)?.Parent as DataGridCell : null; } // 辅助扩展方法:查找视觉树中的子元素 public static T FindVisualChild<T>(this DependencyObject parent) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var child = VisualTreeHelper.GetChild(parent, i); if (child is T tChild) return tChild; var result = FindVisualChild<T>(child); if (result != null) return result; } return null; }
额外优化
建议将TotalPreis列设置为只读,避免用户手动修改后自动恢复的突兀体验:
<controls:DataGridTextColumn Binding="{Binding TotalPreis}" Header="Gesamt" IsReadOnly="True"/>
内容的提问来源于stack exchange,提问作者EddyChicken

