在 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 每个我试图通知 属性 中的更改的时间。但是,它变得有点混乱,因为与 Orange 或 Apple classes 不同,它继承了 ObservableCollection(声明中的 ObservableCollection if我们想要具体)。我知道 ObservableCollection 实际上只是一个 Collection class 实现了 INotifyPropertyChanged 和 INotifyCollectionChanged 接口。现在.NET开源了,看到水暖真是太好了(赞美主)
无论如何,实现这个变得更加混乱,因为我一直看到这个:
警告 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 事件。
让我们把它分成两个任务:
- 添加、删除项目或更改项目重量时,应重新计算 TotalWeight。我们需要处理这些事件。
- 引发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 接口。你会发现很多如何做的例子。很简单。
我已经尝试查看与此相关的其他主题,但没有找到解决我的问题的可行方法。基本上,我有一个名为 "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 每个我试图通知 属性 中的更改的时间。但是,它变得有点混乱,因为与 Orange 或 Apple classes 不同,它继承了 ObservableCollection(声明中的 ObservableCollection if我们想要具体)。我知道 ObservableCollection 实际上只是一个 Collection class 实现了 INotifyPropertyChanged 和 INotifyCollectionChanged 接口。现在.NET开源了,看到水暖真是太好了(赞美主)
无论如何,实现这个变得更加混乱,因为我一直看到这个:
警告 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 事件。
让我们把它分成两个任务:
- 添加、删除项目或更改项目重量时,应重新计算 TotalWeight。我们需要处理这些事件。
- 引发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 接口。你会发现很多如何做的例子。很简单。