为什么ReadOnlyObservableCollection.CollectionChanged
受到保护而不是公开(因为相应的ObservableCollection.CollectionChanged
是)?
如果我无法访问INotifyCollectionChanged
事件,实施CollectionChanged
的集合有什么用途?
答案 0 :(得分:52)
以下是解决方案:CollectionChanged events on ReadOnlyObservableCollection
您必须强制集合到 INotifyCollectionChanged 。
答案 1 :(得分:14)
我找到了一种方法让你知道如何做到这一点:
ObservableCollection<string> obsCollection = new ObservableCollection<string>();
INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection);
collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);
您只需要通过 INotifyCollectionChanged 界面明确引用您的收藏。
答案 2 :(得分:5)
您可以投票选择Microsoft Connect上描述此问题的错误条目:https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public
<强>更新强>
Microsoft已关闭Connect门户。所以上面的链接不再起作用了。
我的Win应用程序框架(WAF)库提供了一个解决方案:ReadOnlyObservableList class:
public class ReadOnlyObservableList<T>
: ReadOnlyObservableCollection<T>, IReadOnlyObservableList<T>
{
public ReadOnlyObservableList(ObservableCollection<T> list)
: base(list)
{
}
public new event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
public new event PropertyChangedEventHandler PropertyChanged
{
add { base.PropertyChanged += value; }
remove { base.PropertyChanged -= value; }
}
}
答案 3 :(得分:5)
想要在 ReadOnlyObservableCollection 上订阅集合更改通知,肯定有充分的理由。因此,作为仅仅将您的集合转换为 INotifyCollectionChanged 的替代方法,如果您碰巧是 ReadOnlyObservableCollection 的子类,那么以下提供了一种更加语法上方便的方式来访问 CollectionChanged 事件:
public class ReadOnlyObservableCollectionWithCollectionChangeNotifications<T> : ReadOnlyObservableCollection<T>
{
public ReadOnlyObservableCollectionWithCollectionChangeNotifications(ObservableCollection<T> list)
: base(list)
{
}
event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged2
{
add { CollectionChanged += value; }
remove { CollectionChanged -= value; }
}
}
这对我来说效果很好。
答案 4 :(得分:5)
我知道这篇文章很老,但是,在评论之前,人们应该花时间了解.NET中使用的模式。只读集合是现有集合的包装器,它阻止消费者直接修改它,查看ReadOnlyCollection
,您将看到它是IList<T>
的包装器,可能是也可能不是可变的。不可变集合是另一回事,并由新immutable collections library
换句话说,只读是不可变的!!!!
除此之外,ReadOnlyObservableCollection
应隐式实施INotifyCollectionChanged
。
答案 5 :(得分:1)
正如已经回答的那样,您有两个选择:您可以将ReadOnlyObservableCollection<T>
强制转换为接口INotifyCollectionChanged
来访问显式实现的CollectionChanged
事件,也可以创建自己的包装器类只需在构造函数中执行一次操作,然后就将包装的ReadOnlyObservableCollection<T>
的事件挂接起来。
一些其他有关为何尚未解决此问题的见解:
从the source code中可以看到,ReadOnlyObservableCollection<T>
是一个公共的,未密封的(即可继承的)类,其中事件标记为protected virtual
。
也就是说,可能存在具有从ReadOnlyObservableCollection<T>
派生的类的已编译程序,这些类具有覆盖的事件定义,但可见性为protected
。一旦事件的可见性在基类中更改为public
,这些程序将包含无效代码,因为不允许在派生类中限制事件的可见性。
很不幸,稍后再进行protected virtual
个事件public
是一个破坏二进制的更改,因此,如果没有很好的推理就无法完成,恐怕“我必须强制转换对象一次附加处理程序”根本不是。
答案 6 :(得分:0)
这是谷歌的热门话题,所以我想我会添加我的解决方案以防其他人查看。
使用上面的信息(关于需要转换为 INotifyCollectionChanged ),我做了两个扩展方法来注册和取消注册。
我的解决方案 - 扩展方法
public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
collection.CollectionChanged += handler;
}
public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
collection.CollectionChanged -= handler;
}
实施例
IThing.cs
public interface IThing
{
string Name { get; }
ReadOnlyObservableCollection<int> Values { get; }
}
使用扩展方法
public void AddThing(IThing thing)
{
//...
thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged);
}
public void RemoveThing(IThing thing)
{
//...
thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged);
}
OP解决方案
public void AddThing(IThing thing)
{
//...
INotifyCollectionChanged thingCollection = thing.Values;
thingCollection.CollectionChanged += this.HandleThingCollectionChanged;
}
public void RemoveThing(IThing thing)
{
//...
INotifyCollectionChanged thingCollection = thing.Values;
thingCollection.CollectionChanged -= this.HandleThingCollectionChanged;
}
备选方案2
public void AddThing(IThing thing)
{
//...
(thing.Values as INotifyCollectionChanged).CollectionChanged += this.HandleThingCollectionChanged;
}
public void RemoveThing(IThing thing)
{
//...
(thing.Values as INotifyCollectionChanged).CollectionChanged -= this.HandleThingCollectionChanged;
}
答案 7 :(得分:0)
ReadOnlyObservableCollection.CollectionChanged
没有公开(出于其他答案中概述的正当原因),因此让我们创建自己的包装器类来公开它:
/// <summary>A wrapped <see cref="ReadOnlyObservableCollection{T}"/> that exposes the internal <see cref="CollectionChanged"/>"/>.</summary>
public class ObservableReadOnlyCollection<T> : ReadOnlyObservableCollection<T>
{
public new NotifyCollectionChangedEventHandler CollectionChanged;
public ObservableReadOnlyCollection(ObservableCollection<T> list) : base(list) { /* nada */ }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) =>
CollectionChanged?.Invoke(this, args);
}
人们问了为什么要观察对只读集合的更改,因此,我将解释许多有效情况之一。当只读集合包装可以更改的私有内部集合时。
这是一个这样的场景:
假设您有一项服务,该服务允许从服务外部向内部集合添加和删除项目。现在,假设您要公开集合的值,但又不希望使用者直接操作集合。因此您可以将内部集合包装在ReadOnlyObservableCollection
中。
请注意,为了用
ReadOnlyObservableCollection
包装内部集合,内部集合必须由ObservableCollection
的构造函数从ReadOnlyObservableCollection
派生。
现在,假设您要在内部集合更改时(因此在公开的ReadOnlyObservableCollection
更改时)将服务通知给使用者。与其发布自己的实现,不如只公开CollectionChanged
中的ReadOnlyObservableCollection
。不必强迫消费者对ReadOnlyObservableCollection
的实现做一个假设,您只需将ReadOnlyObservableCollection
与此自定义ObservableReadOnlyCollection
交换,就可以了。
ObservableReadOnlyCollection
自身隐藏了ReadOnlyObservableCollection.CollectionChanged
,并简单地将所有已更改集合的事件传递给任何附加的事件处理程序。