在 ObservableCollection 上触发 InotifyPropertyChanged/CollectionChanged

Trigger InotifyPropertyChanged/CollectionChanged on ObservableCollection

我已经尝试查看与此相关的其他主题,但没有找到解决我的问题的可行方法。基本上,我有一个名为 "FruitBasket" 的 ObservableCollection,其中包含不同种类的水果。 FruitBasket 本身包含 ObservableCollections 用于通过的每种水果,因此它们可以用作 ItemSources for ListViews(用他们的名字表示"AppleContainer"和"OrangeContainer"),每个显示一种水果。因为水果 类 本身实现了 INotifyPropertyChanged,所以修改它们的值会触发对 ListView 控件的更新就好了,但是,FruitBasket 有一个 "TotalWeight" 属性 从collections。我希望 "TotalWeight" 更新 UI 中的 Label 控件,而无需刷新 UI。在实际 ObservableCollection 本身的 属性 更改上触发通知,而不仅仅是其组成成员更困难,到目前为止我还没有找到任何有效的解决方案(或我已经正确实施了)。

public class FruitBasket : ObservableCollection<IFruit>
{
    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;
        }
    }

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

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

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

我在 UI 中为 Fruit Basket 可观察集合对象本身分配 datacontext 并不像我为它的集合成员(OrangeContainer 和 AppleContainer)分配数据上下文那样勤奋。在 UI window 的初始化中,将数据上下文分配给 ListView 对象是第二天性。我没有将 XAML 中正确节点的数据上下文与后面代码中初始化方法中的 Fruit Basket 对象完全匹配(我真的应该早点检查)。

由于 datacontext/binding 的未对齐分配,在 XAML 和初始化方法之间,属性changed 事件从未像为OrangeContainer 和 AppleContainer 集合中的 Apple 和 Orange 对象是 FruitBasket 的成员。所以,在 Orange class 声明中,我们会有这样的:

public class Orange : INotifyPropertyChanged, IFruit

和这样的实现

    public event PropertyChangedEventHandler PropertyChanged;

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

而当PropChange方法在Weight属性setter,this.PropertyChanged 不会为空,一切都会正常。

FruitBasket class 有点棘手。由于上述 UI 代码中匹配不当的问题,this.PropertyChanged 将 return null 每个我试图通知 属性 中的更改的时间。但是,它变得有点混乱,因为与 OrangeApple classes 不同,它继承了 ObservableCollection(声明中的 ObservableCollection if我们想要具体)。我知道 ObservableCollection 实际上只是一个 Collection class 实现了 INotifyPropertyChanged 和 INotifyCollectionChanged 接口。现在.NET开源了,看到水暖真是太好了(赞美主)

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

无论如何,实现这个变得更加混乱,因为我一直看到这个:

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

我仍然看到这个,但我的实现有效,因为虽然它是从 INotifyProperty 继承的结果,但通过 ObservableCollection 改变了,如我原来的 class 声明

public class FruitBasket : ObservableCollection<IFruit>

这还差最后一个元素需要让一切正常,它是将 INotifyPropertyChanged 添加到 class 本身,如下所示:

public class FruitBasket : ObservableCollection<IFruit>, INotifyPropertyChanged

这似乎有点多余和不雅,但我并没有深入尝试覆盖和处理 ObservableCollection 的 INotifyPropertyChanged 继承(或者尽我所能理解)。

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

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

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

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

  1. 添加、删除项目或更改项目重量时,应重新计算 TotalWeight。我们需要处理这些事件。
  2. 引发FruitBasket.PropertyChanged事件

为了遵循单一职责原则,我将这两个任务拆分为两个 类:

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

public abstract class ExtendedObservableCollection<T> : ObservableCollection<T> 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<IFruit>
{
    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 接口。你会发现很多如何做的例子。很简单。