ObservableCollection子项属性修改后视图不更新的问题排查
ObservableCollection子项属性修改后视图不更新的解决方法
问题描述
ObservableCollection能正常处理集合的增删操作并触发视图更新,但修改集合中ListItem的属性(如代码中的origin.Quantity--)时,视图无法自动刷新。已让ListItem继承实现INotifyPropertyChanged的BaseModel,且Quantity属性通过SetProperty实现,但问题仍存在。
相关代码片段
ViewModel中的ObservableCollection定义
public ObservableCollection<ListItem> Items { get { return primaryBin.Items; } set { primaryBin.Items = value; OnPropertyChanged(); } }
修改子项属性的业务代码
if (Found) { viewModel.IsBusy = true; // 检查是否已接收该产品 ListItem origin = viewModel.Items.FirstOrDefault(i => i.EAN == labelText); updateErp = false; SupplierOrderLineForScan p = viewModel.lines.FirstOrDefault(pr => pr.Ean.Trim() == labelText.Trim()); u = await Common.GetPickingUser(); // 检查手推车上是否已有该产品 ListItem bincontentDirection = viewModel.NotCurrentItems.FirstOrDefault(i => i.EAN == labelText); BinContent bincontentOrigin = viewModel.NotCurrentBinContents.Find(s => s.StockId == p.StockId); if (bincontentDirection == default(ListItem)) { bincontentDirection = new ListItem { Quantity = 0 , EAN = origin.EAN, Name = origin.Name}; viewModel.ItemsTransfer.Add(bincontentDirection); } bincontentDirection.Quantity++; origin.Quantity--; }
基础ViewModel的INotifyPropertyChanged实现
protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName]string propertyName = "", Action onChanged = null) { if (EqualityComparer<T>.Default.Equals(backingStore, value)) return false; backingStore = value; onChanged?.Invoke(); OnPropertyChanged(propertyName); return true; } #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { var changed = PropertyChanged; if (changed == null) return; changed.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #endregion
ListItem及BaseModel定义
public class BaseModel : INotifyPropertyChanged { protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "", Action onChanged = null) { if (EqualityComparer<T>.Default.Equals(backingStore, value)) return false; backingStore = value; onChanged?.Invoke(); OnPropertyChanged(propertyName); return true; } #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { var changed = PropertyChanged; if (changed == null) return; changed.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } #endregion } public class ListItem : BaseModel { // BinConten id public string Id { get; set; } public bool Block { get; set; } public string Name { get; set; } public bool IsBlockIconVisible { get; set; } public string CodeSupplier { get; set; } public bool IsHeader { get; set; } public string Entry { get; set; } public String ProductID { get; set; } public bool showDeleteButton { get; set; } public bool showTransferButton { get; set; } public int BinID { get; set; } public int Piece { get; set; } private int _quantity; public int Quantity { get { return _quantity; } set { SetProperty(ref _quantity, value); } } public int BookedQuantity { get; set; } public int QuantityDefect { get; set; } public Color bgColor { get; set; } int _quantityDefect; public string EAN { get; set; } public string QuantityDisplay { get { return $"Q: {Quantity}(@{BookedQuantity})"; } } public string QuantityExpectedDisplay { get { return $"{Quantity} / {BookedQuantity}"; } } public string EANReference { get { return $"{EAN} / {CodeSupplier}"; } } public Style Style { get; set; } }
问题分析与修复步骤
1. 修复BaseModel中重复触发PropertyChanged的问题
BaseModel的OnPropertyChanged方法重复触发了事件,可能导致通知异常,简化为以下代码:
protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
2. 为计算属性添加通知依赖
ListItem中的QuantityDisplay、QuantityExpectedDisplay属于计算属性,依赖Quantity和BookedQuantity,但原代码中这两个属性变化时不会触发计算属性的更新。需要修改如下:
private int _quantity; public int Quantity { get { return _quantity; } set { if (SetProperty(ref _quantity, value)) { OnPropertyChanged(nameof(QuantityDisplay)); OnPropertyChanged(nameof(QuantityExpectedDisplay)); } } } private int _bookedQuantity; public int BookedQuantity { get { return _bookedQuantity; } set { if (SetProperty(ref _bookedQuantity, value)) { OnPropertyChanged(nameof(QuantityDisplay)); OnPropertyChanged(nameof(QuantityExpectedDisplay)); } } }
3. 确保异步操作在UI线程更新属性
代码中存在异步调用await Common.GetPickingUser();,后续更新属性时需要切换到UI线程执行(以Xamarin为例):
Device.BeginInvokeOnMainThread(() => { bincontentDirection.Quantity++; origin.Quantity--; });
4. 确认视图绑定配置
确保视图中集合绑定为OneWay或TwoWay(默认集合绑定为OneWay),子项属性绑定正确指向Quantity或对应的计算属性。
内容的提问来源于stack exchange,提问作者Elgate
相关产品推荐
相关产品推荐

