观察集合中项目的 PropertyChanged
我正在尝试挂钩集合中 INotifyPropertyChanged
对象的事件。
我见过的这个问题的每个答案都说要按如下方式处理:
void NotifyingItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if( e.NewItems != null )
{
foreach( INotifyPropertyChanged item in e.NewItems )
{
item.PropertyChanged += new PropertyChangedEventHandler(CollectionItemChanged);
}
}
if( e.OldItems != null )
{
foreach( ValidationMessageCollection item in e.OldItems )
{
item.PropertyChanged -= CollectionItemChanged;
}
}
}
我的问题是,每当开发人员在 NotifyingItems 集合上调用 Clear()
时,就会完全失败。发生这种情况时,将使用 e.Action == Reset
调用此事件处理程序,并且 e.NewItems
和 e.OldItems
都等于 null
(我希望后者包含所有项目)。
问题是这些项目不会消失,也不会被破坏,它们只是不再应该由当前类监视 - 但因为我从来没有机会取消映射它们的 PropertyChangedEventHandler
- 即使它们已从我的 NotifyingItems 列表中清除,它们仍会继续调用我的 CollectionItemChanged
处理程序。这种“既定”模式应该如何处理这种情况?
I'm trying to hook into an event on INotifyPropertyChanged
objects in a collection.
Every answer that I've ever seen to this question has said to handle it as follows:
void NotifyingItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if( e.NewItems != null )
{
foreach( INotifyPropertyChanged item in e.NewItems )
{
item.PropertyChanged += new PropertyChangedEventHandler(CollectionItemChanged);
}
}
if( e.OldItems != null )
{
foreach( ValidationMessageCollection item in e.OldItems )
{
item.PropertyChanged -= CollectionItemChanged;
}
}
}
My problem is that this completely fails whenever a developer calls Clear()
on the NotifyingItems collection. When that happens, this event handler is called with e.Action == Reset
and both e.NewItems
and e.OldItems
equal to null
(I would expect the latter to contain all items).
The problem is those items don't go away, and they aren't destroyed, they are just no longer supposed to be monitored by the current class - but since I never got the chance to unmap their PropertyChangedEventHandler
- they keep calling my CollectionItemChanged
handler even after they've been cleared from my NotifyingItems list. How is such a situation supposed to be handled with this 'well established' pattern?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
也许看看 这个答案
它建议不要使用
.Clear()
并实现 < code>.RemoveAll() 扩展方法将逐一删除项目如果这对您不起作用,链接中还发布了其他好的解决方案。
Perhaps take a look at this answer
It suggests not using
.Clear()
and implementing a.RemoveAll()
extension method that will remove the items one-by-oneIf that doesn't work for you, there are other good solutions posted in the link as well.
编辑:此解决方案不起作用
此解决方案来自Rachel 链接到的问题似乎很精彩:
如果我用重写可重写 Collection.ClearItems() 方法的继承类替换我的 NotifyingItems ObservableCollection,那么我可以拦截 NotifyCollectionChangedEventArgs 并将其替换为“删除”而不是“重置”操作,并传递已删除项目的列表:
太棒了,除了我自己的类之外,不需要在任何地方进行任何更改。
*编辑*
我喜欢这个解决方案,但它不起作用...您不允许引发具有更多内容的 NotifyCollectionChangedEventArgs除非操作是“重置”,否则更改的项目不会超过一项。您会收到以下运行时异常:
不支持范围操作
。我不知道为什么它必须对此如此挑剔,但现在除了一次删除每一项之外别无选择......为每一项触发一个新的 CollectionChanged 事件。真他妈的麻烦。Edit: This solution doesn't work
This solution from the question Rachel linked to appears to be brilliant:
If I replace my NotifyingItems ObservableCollection with an inheriting class that overrides the overrideable Collection.ClearItems() method, then I can intercept the NotifyCollectionChangedEventArgs and replace it with a Remove instead of a Reset operation, and pass the list of removed items:
Brilliant, and nothing needs to be changed anywhere except in my own class.
*edit*
I loved this solution, but it doesn't work... You're not allowed to raise a NotifyCollectionChangedEventArgs that has more than one item changed unless the action is "Reset". You get the following runtime exception:
Range actions are not supported
. I don't know why it has to be so damn picky about this, but now this leaves no option but to remove each item one at a time... firing a new CollectionChanged event for each one. What a damn hassle.发现最终解决方案
我找到了一种解决方案,它允许用户既可以利用一次添加或删除多个项目的效率,同时只触发一个事件 - 并满足 UIElements 的需求来获取Action.Reset 事件参数,而所有其他用户都希望添加和删除元素列表。
此解决方案涉及重写 CollectionChanged 事件。当我们触发这个事件时,我们实际上可以查看每个注册处理程序的目标并确定它们的类型。由于当多个项目发生更改时,只有 ICollectionView 类需要
NotifyCollectionChangedAction.Reset
参数,因此我们可以将它们挑出来,并为其他所有人提供包含已删除或添加的项目的完整列表的正确事件参数。下面是实现。感谢大家的建议和链接。如果没有看到其他人提出的所有逐渐更好的解决方案,我永远不会走到这一步。
Ultimate solution discovered
I've found a solution that allows the user to both capitalize on the efficiency of adding or removing many items at a time while only firing one event - and satisfy the needs of UIElements to get the Action.Reset event args while all other users would like a list of elements added and removed.
This solution involves overriding the CollectionChanged event. When we go to fire this event, we can actually look at the target of each registered handler and determine their type. Since only ICollectionView classes require
NotifyCollectionChangedAction.Reset
args when more than one item changes, we can single them out, and give everyone else proper event args that contain the full list of items removed or added. Below is the implementation.Thanks to everyone for their suggestions and links. I never would have gotten to this point without seeing all the incrementally better solutions other people came up with.
我通过创建自己的
ObservableCollection
子类解决了这个问题,它重写了ClearItems
方法。在调用基本实现之前,它会引发我在类上定义的CollectionChanging
事件。CollectionChanging
在集合实际被清除之前触发,因此您有机会订阅事件和取消订阅事件。例子:
I solved this problem by making my own subclass of
ObservableCollection<T>
which overrides theClearItems
method. Before calling the base implementation, it raises aCollectionChanging
event which I defined on my class.CollectionChanging
fires before the collection actually gets cleared, and thus you have the opportunity to subscribe to the event and unsubscribe from the events.Example:
重置不提供更改的项目。如果继续使用 Clear,则需要维护一个单独的集合来清除事件。
一个更简单、更节省内存的解决方案是创建自己的清除函数并删除每个项目,而不是调用集合的清除。
Reset does not provide the changed items. You would need to maintain a seperate collection to clear the events if you continued to use Clear.
A easier and more memory efficient solution would be to create your own clear function and remove each item instead of calling the collection's clear.