在ObservableCollection

时间:2015-05-25 19:30:08

标签: c# wpf observablecollection

我已经尝试过关于此问题的其他主题,但我没有找到一个有效的实现问题。基本上,我有一个名为“FruitBasket”的ObservableCollection,它包含不同种类的水果。 FruitBasket本身包含 ObservableCollections ,用于通过的每种相应类型的水果,以便它们可以用作 ListViews ItemSources (由其名称表示) AppleContainer“和”OrangeContainer“),每个都展示一种水果。因为水果类本身实现了INotifyPropertyChanged,修改它们的值会触发对ListView控件的更新,但是,FruitBasket具有从集合中所有其他水果的权重派生的“TotalWeight”属性。我希望“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;
        }
    }

3 个答案:

答案 0 :(得分:2)

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

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

  1. 添加,删除项目或更改项目权重时,应重新计算TotalWeight。我们需要处理这些事件。
  2. 提升FruitBasket.PropertyChanged事件
  3. 我已将这两项任务分成两类,以遵循单一责任原则:

    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接口。你会发现很多例子如何做到这一点。这很简单。

答案 1 :(得分:0)

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

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

由于在XAML和初始化方法之间未对齐datacontext / binding的赋值,因此我的水果篮可观察集合中的propertychanged事件从未触发,就像OrangeContainer和AppleContainer集合中的Apple和Orange对象一样。 FruitBasket。所以,在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 。但是,它有点混乱,因为与 Orange Apple 类不同,它继承了ObservableCollection(如果我们想要特定的话,声明中的ObservableCollection)。我知道 ObservableCollection 实际上只是一个实现INotifyPropertyChanged和INotifyCollectionChanged接口的 Collection 类。现在看.NET管道开源(赞美主)

真的很高兴

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

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

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

我仍然看到这一点,但我的实现工作正常,因为它是INotifyProperty的继承通过ObservableCollection更改的结果,如我原来的类声明中所见

public class FruitBasket : ObservableCollection<IFruit>

这只是为了让所有东西都能正常工作的最后一个元素,这就是将INotifyPropertyChanged添加到类本身,如下所示:

public class FruitBasket : ObservableCollection<IFruit>, INotifyPropertyChanged

它看起来有点冗余和不优雅,但我没有尝试覆盖和冥想来自ObservableCollection的INotifyPropertyChanged的继承(或者我能理解的最好)。

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

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

答案 2 :(得分:-1)

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

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