为什么要ReadOnlyObservableCollection.CollectionChanged
受保护而不是公开(与之相对应ObservableCollection.CollectionChanged
)?
INotifyCollectionChanged
如果无法访问CollectionChanged
事件,实现集合的用途是什么?
为什么要ReadOnlyObservableCollection.CollectionChanged
受保护而不是公开(与之相对应ObservableCollection.CollectionChanged
)?
INotifyCollectionChanged
如果无法访问CollectionChanged
事件,实现集合的用途是什么?
Answers:
解决方法如下:ReadOnlyObservableCollection上的CollectionChanged事件
你必须投收集到INotifyCollectionChanged。
我为您找到了一种方法:
ObservableCollection<string> obsCollection = new ObservableCollection<string>();
INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection);
collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);
您只需要通过INotifyCollectionChanged接口显式引用您的集合。
ReadOnlyObservableCollection
会发现:event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
。事件的显式接口实现。
我知道这篇文章很陈旧,但是,人们在发表评论之前应该花点时间了解.NET中使用的模式。只读集合是现有集合的包装,可以防止消费者直接对其进行修改,看看后ReadOnlyCollection
,您会发现它是IList<T>
可变的包装。不可变集合是另一回事,新的不可变集合库涵盖了
换句话说,只读与不变是不一样的!!!!
除此之外,ReadOnlyObservableCollection
应隐式实施INotifyCollectionChanged
。
绝对有充分的理由希望订阅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; }
}
}
以前对我来说效果很好。
您可能会投票支持Microsoft Connect上描述此问题的错误条目:https : //connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public
更新:
Microsoft已关闭Connect门户。因此,上面的链接不再起作用。
我的Win应用程序框架(WAF)库提供了一个解决方案:ReadOnlyObservableList类:
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; }
}
}
正如已经回答的那样,您有两个选择:可以将强制ReadOnlyObservableCollection<T>
转换为接口INotifyCollectionChanged
以访问显式实现的CollectionChanged
事件,或者可以创建自己的包装器类,该包装器类在构造函数中执行一次操作,并仅连接包装好的事件ReadOnlyObservableCollection<T>
。
关于为何尚未解决此问题的一些其他见解:
从源代码中可以看到,ReadOnlyObservableCollection<T>
是一个公共的,未密封的(即可继承的)类,其中标记了事件protected virtual
。
也就是说,可能存在带有从派生的类的已编译程序,这些类ReadOnlyObservableCollection<T>
具有被覆盖的事件定义,但具有protected
可见性。一旦将事件的可见性更改为public
基类中的内容,这些程序将包含无效的代码,因为不允许在派生类中限制事件的可见性。
因此,不幸的是,稍后进行protected virtual
事件public
是一个破坏二进制的更改,因此,如果没有很好的推理就无法完成,恐怕“我必须将对象强制转换一次以附加处理程序”根本不是。
这在Google上很受欢迎,所以我想我会添加解决方案,以防其他人查到。
使用上面的信息(有关需要转换为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;
}
例
物联网
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;
}
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
具有自己的隐藏,并简单地将所有已更改集合的事件传递给任何附加的事件处理程序。