WPF DataGrid未自动刷新ObservableCollection更新值的问题求助
问题背景
基于C#、WPF和MVVM开发Autodesk Revit插件,需求是选中3D元素后,在窗口展示并编辑自定义筛选的参数列表。当前ObservableCollection已同步Revit API返回的参数更新值,且触发了集合变更事件,但DataGrid并未自动重绘显示新值——只有窗口最小化、点击单元格或滚动后才会刷新。已尝试自定义ObservableCollection实现INPC、设置绑定Mode与UpdateSourceTrigger、改用基础DataGridTextColumn等方案,均无效。需要符合MVVM原则的解决办法,无需清空重置ObservableCollection即可强制DataGrid单元格重绘。
可行解决方案
1. 确保参数项模型正确实现INotifyPropertyChanged
集合本身的变更事件只负责通知元素的增删,单个元素的属性更新需要元素自身触发PropertyChanged事件。你的参数模型类必须实现INotifyPropertyChanged,且赋值时通过属性setter触发通知:
public class ParameterItem : INotifyPropertyChanged { private string _paramName; private string _paramValue; public string ParamName { get => _paramName; set { if (_paramName != value) { _paramName = value; OnPropertyChanged(nameof(ParamName)); } } } public string ParamValue { get => _paramValue; set { if (_paramValue != value) { _paramValue = value; OnPropertyChanged(nameof(ParamValue)); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } }
注意:更新参数值时必须调用属性setter,不能直接修改私有字段,否则不会触发通知。
2. 通过ICollectionView强制刷新视图
在ViewModel中使用ICollectionView包装ObservableCollection,更新完成后调用Refresh()方法强制视图重绘:
// ViewModel字段与属性 private ObservableCollection<ParameterItem> _paramList; private ICollectionView _paramListView; public ICollectionView ParamListView { get => _paramListView; private set { _paramListView = value; OnPropertyChanged(nameof(ParamListView)); } } // 初始化逻辑 _paramList = new ObservableCollection<ParameterItem>(); _paramListView = CollectionViewSource.GetDefaultView(_paramList); // 参数值更新后调用 _paramListView.Refresh();
将DataGrid的ItemsSource绑定到ParamListView而非直接绑定ObservableCollection,这样调用Refresh()会强制DataGrid重新渲染所有单元格。
3. 手动更新单个绑定表达式(针对局部刷新)
如果只需要刷新特定参数项,可通过BindingOperations获取单元格的绑定表达式并更新。MVVM场景下建议用行为(Behavior)封装,避免ViewModel直接操作View:
// 示例:在行为或View的代码后台中触发 foreach (var item in YourViewModel.ParamList) { var row = YourDataGrid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow; if (row == null) continue; var targetColumn = YourDataGrid.Columns.First(c => c.Header.ToString() == "参数值"); var cell = targetColumn.GetCellContent(row) as TextBlock; if (cell == null) continue; BindingOperations.GetBindingExpression(cell, TextBlock.TextProperty)?.UpdateTarget(); }
4. 确保在UI线程更新集合
Revit API操作通常在后台线程执行,而WPF UI更新必须在主线程。更新参数值时要切换到UI线程:
// 在Revit事件回调或后台线程中 Application.Current.Dispatcher.Invoke(() => { // 遍历集合更新参数值 foreach (var paramItem in _paramList) { paramItem.ParamValue = GetUpdatedValueFromRevitAPI(); } });
如果跨线程更新未切换到UI线程,即使触发了INPC事件,WPF也不会处理更新,导致DataGrid不刷新。
内容的提问来源于stack exchange,提问作者jnyp

