WPF MVVM中BindingList元素属性变更后更新AvgPricePerRoom
解决WPF MVVM中DataGrid修改属性后实时更新计算属性的问题
嘿,刚上手WPF和MVVM碰到这个问题太正常了!我来给你捋清楚怎么实现实时更新AvgPricePerRoom的逻辑,一步步来:
第一步:确保HouseViewModel实现INotifyPropertyChanged
BindingList能感知元素属性变化的前提是,元素本身会触发属性变更通知。所以你的HouseViewModel必须实现INotifyPropertyChanged接口,并且在YearBuilt、Price、Rooms这三个可编辑属性的setter里触发PropertyChanged事件:
public class HouseViewModel : INotifyPropertyChanged { private int _yearBuilt; public int YearBuilt { get => _yearBuilt; set { if (_yearBuilt != value) { _yearBuilt = value; OnPropertyChanged(nameof(YearBuilt)); } } } private decimal _price; public decimal Price { get => _price; set { if (_price != value) { _price = value; OnPropertyChanged(nameof(Price)); } } } private int _rooms; public int Rooms { get => _rooms; set { if (_rooms != value) { _rooms = value; OnPropertyChanged(nameof(Rooms)); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
第二步:在MainViewModel中监听HouseList的变化
接下来要在MainViewModel里处理两个关键场景:
- 切换选中的
StreetViewModel时,订阅/取消订阅其HouseList的事件 - 当
HouseList中的元素添加、删除,或者元素属性变更时,通知AvgPricePerRoom更新
具体代码实现如下:
public class MainViewModel : INotifyPropertyChanged { private StreetViewModel _selectedStreet; public StreetViewModel SelectedStreet { get => _selectedStreet; set { if (_selectedStreet != value) { // 取消订阅之前街道的HouseList事件,避免内存泄漏 if (_selectedStreet != null) { _selectedStreet.HouseList.ListChanged -= HouseList_ListChanged; } _selectedStreet = value; OnPropertyChanged(nameof(SelectedStreet)); // 订阅新选中街道的HouseList事件 if (_selectedStreet != null) { _selectedStreet.HouseList.ListChanged += HouseList_ListChanged; // 切换街道后立即计算一次平均值 OnPropertyChanged(nameof(AvgPricePerRoom)); } } } } public double AvgPricePerRoom { get { // 边界情况处理:没有选中街道、房屋列表为空、房间数为0的情况 if (SelectedStreet?.HouseList == null || !SelectedStreet.HouseList.Any()) return 0; var validHouses = SelectedStreet.HouseList.Where(h => h.Rooms > 0); return validHouses.Any() ? validHouses.Average(h => (double)h.Price / h.Rooms) : 0; } } private void HouseList_ListChanged(object sender, ListChangedEventArgs e) { // 只要列表有任何变化(元素增删、属性修改),就通知UI更新平均值 OnPropertyChanged(nameof(AvgPricePerRoom)); } // INotifyPropertyChanged接口实现 public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
第三步:确认StreetViewModel的HouseList类型
最后要保证StreetViewModel里的HouseList是BindingList<HouseViewModel>类型,这样才能触发ListChanged事件:
public class StreetViewModel { // 初始化BindingList,确保它能感知元素属性变化 public BindingList<HouseViewModel> HouseList { get; } = new BindingList<HouseViewModel>(); // 你的其他街道属性... }
为什么这样能生效?
当你在DataGrid里修改某个房屋的Price或Rooms时:
HouseViewModel的属性setter会触发PropertyChanged事件BindingList会捕获到这个事件,进而触发自身的ListChanged事件(ListChangedType为ItemChanged)MainViewModel中订阅的HouseList_ListChanged方法会被调用,触发AvgPricePerRoom的属性变更通知- UI上绑定
AvgPricePerRoom的TextBox就会自动更新显示最新的计算值
内容的提问来源于stack exchange,提问作者Sebastian




