在ObservableCollection上触发InotifyPropertyChanged / CollectionChanged

我已经尝试过关于此的其他主题,但我还没有找到一个有效的实现问题。 基本上,我有一个名为“FruitBasket”的ObservableCollection,它包含不同种类的水果。 FruitBasket本身包含ObservableCollections,用于通过的每个相应类型的水果,以便它们可以用作ListViews的 ItemSources (由它们的名称“AppleContainer”和“OrangeContainer”表示),每个都显示一种水果。 因为水果类本身实现了INotifyPropertyChanged,修改它们的值会触发对ListView控件的更新,但是,FruitBasket具有从集合中所有其他水果的权重派生的“TotalWeight”属性。 我希望“TotalWeight”更新UI中的Label控件,而无需刷新UI。 触发关于实际ObservableCollection本身的属性更改的通知,而不仅仅是其组成成员更加困难,而且我还没有找到任何可行的解决方案(或者我已经正确实现)。

public class FruitBasket : ObservableCollection { private decimal _totalWeight; public FruitBasket() { this.Add(new OrangeContainer(this)); this.Add(new AppleContainer(this)); } public OrangeContainer Oranges { get { return (OrangeContainer)this.Items[0]; } } public AppleContainer Apples { get { return (AppleContainer)this.Items[1]; } } public decimal TotalWeight { get { return _totalWeight; } set { _totalWeight = value; } } internal void UpdateWeight(IFruit caller) { _totalWeight = 0; foreach (Orange orng in (OrangeContainer)this.Items[0]) { _totalWeight += orng.Weight; } foreach (Apple appl in (AppleContainer)this.Items[1]) { _totalWeight += appl.Weight; } } 

每当添加,删除项目或任何项目的权重属性发生更改时,您都需要调用FruitBasket的INotifyPropertyChanged.PropertyChanged事件。

让我们把它分成两个任务:

  1. 添加,移除项目或更改项目权重时,应重新计算TotalWeight。 我们需要处理这些事件。
  2. 提升FruitBasket.PropertyChanged事件

我将这两个任务分成两个类,以遵循单一责任原则:

1) – 这会处理项目的PropertyChanged事件:

 public abstract class ExtendedObservableCollection : ObservableCollection where T : INotifyPropertyChanged { protected override void ClearItems() { foreach (var item in Items) item.PropertyChanged -= ItemPropertyChanged; base.ClearItems(); } protected override void InsertItem(int index, T item) { item.PropertyChanged += ItemPropertyChanged; base.InsertItem(index, item); } protected override void RemoveItem(int index) { this[index].PropertyChanged -= ItemPropertyChanged; base.RemoveItem(index); } protected override void SetItem(int index, T item) { this[index].PropertyChanged -= ItemPropertyChanged; item.PropertyChanged += ItemPropertyChanged; base.SetItem(index, item); } abstract void ItemPropertyChanged(object sender, PropertyChangedEventArgs e); } 

2) – 必要时重新计算TotalWeight

 public class FruitBasket : ExtendedObservableCollection { protected override void ItemPropertyChanged(object sender, PropertyChangedEventArgs e){ UpdateWeight(); OnPropertyChanged("TotalWeight") } protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { UpdateWeight(); OnPropertyChanged("TotalWeight") base.OnCollectionChanged(e); } } 

当然你的Fruit应该实现INotifyPropertyChanged接口。 你会发现很多例子如何做到这一点。 这很简单。

我找到了问题的根源。 我将从最明显的开始:

我没有在UI中为水果篮可观察集合对象本身分配datacontext那么勤奋,因为我是它的集合成员(OrangeContainer和AppleContainer)。 在UI窗口的初始化中,将datacontext分配给ListView对象是第二天性。 我在XAML中的正确节点的datacontext与后面的代码中的初始化方法中的Fruit Basket对象不完全匹配(我之前应该检查过)。

由于在XAML和初始化方法之间未对齐datacontext / binding的赋值,因此我的水果篮可观察集合中的propertychanged事件从未触发,就像它是作为FruitBasket成员的OrangeContainer和AppleContainer集合中的Apple和Orange对象一样。 所以,在Orange类声明中我们有这个:

 public class Orange : INotifyPropertyChanged, IFruit 

而实施就像这样

  public event PropertyChangedEventHandler PropertyChanged; public void PropChange(string prop) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(prop)); } } 

并且当在Weight属性设置器中调用PropChange方法时, this.PropertyChanged将不为null,并且一切都会正常工作。

FruitBasket类有点棘手。 由于UI代码中存在不正确匹配的问题,每次我尝试通知属性更改时this.PropertyChanged都将返回null 。 但是,它有点混乱,因为与OrangeApple类不同,它inheritance了ObservableCollection(声明中的ObservableCollection,如果我们想要具体的话)。 我知道ObservableCollection实际上只是一个实现INotifyPropertyChanged和INotifyCollectionChanged接口的Collection类。 很高兴看到.NET是开源的管道(赞美领主)

http://referencesource.microsoft.com/#System/compmod/system/collections/objectmodel/observablecollection.cs

在任何情况下,实现这一点变得更加混乱,因为我一直看到这个:

警告1’MeatingObsColNotify.FruitBasket.PropertyChanged’隐藏inheritance的成员’System.Collections.ObjectModel.ObservableCollection.PropertyChanged’。 要使当前成员覆盖该实现,请添加override关键字。 否则添加新关键字。 C:\ Testing VS Project \ TestingObsColNotify \ TestingObsColNotify \ FruitBasket.cs 60 50 TestingObsColNotify

我仍然看到这一点,但我的实现工作,因为它是INotifyProperty的inheritance通过ObservableCollection更改的结果,如我的原始类声明中所示

 public class FruitBasket : ObservableCollection 

这只是为了让所有东西都工作的最后一个元素,这就是将INotifyPropertyChanged添加到类本身,就像这样:

 public class FruitBasket : ObservableCollection, INotifyPropertyChanged 

它似乎有点多余和不优雅,但我没有试图覆盖和与ObservableCollectioninheritanceINotifyPropertyChanged(或尽我所能)。

所以我们拥有它,现在一切正常,没有MVVM。 我肯定会在以后继续使用该模式,但很高兴解决这个问题,而不是懒惰只是在UI方面重新分配代码隐藏方法中的控件内容。

感谢那些来到这里并做出贡献的人,感谢您抽出宝贵时间作出回应。

如果使用绑定,请将接口INotifyPropertyChanged添加到您的类。 如果安装了ReSharper,请接受建议以实现该接口。 然后,每当您想要更新任何文本框时,请使用属性TotalWeight的名称调用PropertyChanged ,请参阅https://softwareengineering.stackexchange.com/questions/228067/where-do-put-inotifypropertychanged-interface-in-model- or-viewmodel 。 每当您更新任何ObservableCollections ,手动更新TotalWeight ,然后调用前面提到的PropertyChanged告诉UI更新自己。 我已经使用这种技术将更新从ViewModel推送到View(即从类到XAML)以用于一些相当复杂的场景,它运行得非常好。

我还建议遵循MVVM的学习曲线,以这种方式编写的项目往往更具可伸缩性,更易于维护,更易于使用。