WPF MVVM下DataGrid绑定ObservableCollection选中项最优实现方案
解决方案
你当前的绑定架构本身没有问题,完全不需要手动执行LINQ替换对象的操作,你之所以觉得需要手动同步回原集合,是因为几个基础配置没做对,调整后不仅能省掉手动同步的代码,还能原生兼容你后续需要的ListCollectionView分组功能。
- 核心认知纠正:WPF绑定引用类型对象时,
ComboBox.SelectedItem绑定到的chosenConcept是ConceptList集合中对应对象的直接内存引用,不是独立副本。你对这个对象下propertyList里任意属性的修改,本质就是直接修改原集合内的对应对象,根本不存在“修改后需要替换回集合”的场景。 - 第一步:给所有参与绑定的模型实现
INotifyPropertyChanged接口。你当前的EntityConcept和PropertyItem都是普通CLR属性,没有属性变更通知,值修改后无法触发链路同步,才会让你误以为修改没有落到原集合上。正确的模型实现如下:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; public class PropertyItem : INotifyPropertyChanged { private string _propertyName; private string _mapping; public string PropertyName { get => _propertyName; set { _propertyName = value; OnPropertyChanged(); } } public string Mapping { get => _mapping; set { _mapping = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public class EntityConcept : INotifyPropertyChanged { private ObservableCollection<PropertyItem> _propertyList; private string _ifcEntity; public ObservableCollection<PropertyItem> propertyList { get => _propertyList; set { _propertyList = value; OnPropertyChanged(); } } public string ifcEntity { get => _ifcEntity; set { _ifcEntity = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- 第二步:调整ViewModel实现,不要给
chosenConcept初始化空的新对象,同时直接基于原集合创建ListCollectionView满足后续分组需求,ViewModel本身也需要实现INotifyPropertyChanged:
public class ViewModel : INotifyPropertyChanged { // 原集合不需要改动 public ObservableCollection<EntityConcept> ConceptList { get; } = new ObservableCollection<EntityConcept>(); private EntityConcept _chosenConcept; public EntityConcept chosenConcept { get => _chosenConcept; set { _chosenConcept = value; OnPropertyChanged(); } } // 后续分组、排序、过滤直接操作这个视图即可 public ListCollectionView ConceptCollectionView { get; } public ViewModel() { ConceptCollectionView = new ListCollectionView(ConceptList); // 后续需要分组时,直接添加分组描述即可,示例: // ConceptCollectionView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(EntityConcept.ifcEntity))); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- 第三步:XAML只需要做少量调整,给DataGrid列的绑定加上
UpdateSourceTrigger=PropertyChanged实现编辑即时同步,ComboBox如果要使用集合视图能力直接绑定到ConceptCollectionView即可,DataGrid绑定逻辑不需要改动:
<ComboBox x:Name="ifcCombo" DisplayMemberPath="ifcEntity" SelectedItem="{Binding chosenConcept}" ItemsSource="{Binding ConceptCollectionView}"/> <DataGrid x:Name="propertiesTable" AutoGenerateColumns="False" ItemsSource="{Binding chosenConcept.propertyList}"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding PropertyName, UpdateSourceTrigger=PropertyChanged}"/> <DataGridTextColumn Binding="{Binding Mapping, UpdateSourceTrigger=PropertyChanged}"/> </DataGrid.Columns> </DataGrid>
补充说明:调整完成后,用户在DataGrid中做的所有属性修改会直接写入原
ObservableCollection<EntityConcept>集合,不需要任何额外的回写、替换逻辑。后续实现分组、排序、过滤功能时,直接操作ConceptCollectionView的对应配置即可,不会影响现有绑定链路。
内容的提问来源于stack exchange,提问作者Ryan Daley
相关产品推荐
相关产品推荐

