¿Por qué está ReadOnlyObservableCollection.CollectionChanged
protegido y no es público (como lo ObservableCollection.CollectionChanged
es el correspondiente )?
¿De qué sirve implementar una colección INotifyCollectionChanged
si no puedo acceder al CollectionChanged
evento?
c#
.net
collections
Oskar
fuente
fuente
Respuestas:
Aquí está la solución: eventos CollectionChanged en ReadOnlyObservableCollection
Tienes que convertir la colección a INotifyCollectionChanged .
fuente
Encontré una manera para ti de cómo hacer esto:
ObservableCollection<string> obsCollection = new ObservableCollection<string>(); INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection); collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);
Solo necesita consultar su colección explícitamente mediante la interfaz INotifyCollectionChanged .
fuente
ReadOnlyObservableCollection
se encuentra esta:event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
. Implementación de interfaz explícita del evento.Sé que esta publicación es antigua, sin embargo, las personas deberían tomarse su tiempo para comprender los patrones utilizados en .NET antes de comentar. Una colección de solo lectura es una envoltura de una colección existente que evita que los consumidores la modifiquen directamente, mire
ReadOnlyCollection
y verá que es una envoltura de una colección queIList<T>
puede ser mutable o no. Las colecciones inmutables son un asunto diferente y están cubiertas por la nueva biblioteca de colecciones inmutablesEn otras palabras, solo lectura no es lo mismo que inmutable.
Aparte de eso,
ReadOnlyObservableCollection
debería implementar implícitamenteINotifyCollectionChanged
.fuente
Definitivamente hay buenas razones para querer suscribirse a las notificaciones de cambios de colección en ReadOnlyObservableCollection . Entonces, como una alternativa a simplemente convertir su colección como INotifyCollectionChanged , si está subclasificando ReadOnlyObservableCollection , entonces lo siguiente proporciona una forma más conveniente sintácticamente para acceder al evento 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; } } }
Esto me ha funcionado bien antes.
fuente
Puede votar por la entrada de error en Microsoft Connect que describe este problema: https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public
Actualizar:
Microsoft ha cerrado el portal Connect. Entonces el enlace de arriba ya no funciona.
Mi biblioteca Win Application Framework (WAF) proporciona una solución: clase 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; } } }
fuente
Como ya se respondió, tiene dos opciones: puede enviar el
ReadOnlyObservableCollection<T>
a la interfazINotifyCollectionChanged
para acceder alCollectionChanged
evento implementado explícitamente , o puede crear su propia clase contenedora que hace eso una vez en el constructor y simplemente conecta los eventos del empaquetadoReadOnlyObservableCollection<T>
.Algunas ideas adicionales sobre por qué este problema aún no se ha solucionado:
Como puede ver en el código fuente ,
ReadOnlyObservableCollection<T>
es una clase pública, no sellada (es decir, heredable), donde se marcan los eventosprotected virtual
.Es decir, puede haber programas compilados con clases derivadas
ReadOnlyObservableCollection<T>
, con definiciones de eventos anuladas peroprotected
visibilidad. Esos programas contendrían código inválido una vez que la visibilidad del evento se cambie apublic
la visibilidad del evento en la clase base, porque no se permite restringir la visibilidad de un evento en clases derivadas.Entonces, desafortunadamente, hacer
protected virtual
eventospublic
más adelante es un cambio de ruptura binaria y, por lo tanto, no se hará sin un muy buen razonamiento, que me temo "Tengo que lanzar el objeto una vez para adjuntar controladores" simplemente no lo es.Fuente: Comentario de GitHub por Nick Guererra, 19 de agosto de 2015
fuente
Este fue el mayor éxito en Google, así que pensé en agregar mi solución en caso de que otras personas busquen esto.
Usando la información anterior (sobre la necesidad de transmitir a INotifyCollectionChanged ), hice dos métodos de extensión para registrar y cancelar el registro.
Mi solución : métodos de extensión
public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler) { collection.CollectionChanged += handler; } public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler) { collection.CollectionChanged -= handler; }
Ejemplo
IThing.cs
public interface IThing { string Name { get; } ReadOnlyObservableCollection<int> Values { get; } }
Usando los métodos de extensión
public void AddThing(IThing thing) { //... thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged); } public void RemoveThing(IThing thing) { //... thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged); }
Solución de 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; }
Alternativa 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; }
fuente
Solución
ReadOnlyObservableCollection.CollectionChanged
no está expuesto (por razones válidas descritas en otras respuestas), así que hagamos nuestra propia clase contenedora que lo exponga:/// <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); }
Explicación
La gente ha preguntado por qué querría observar cambios en una colección de solo lectura, así que explicaré una de las muchas situaciones válidas; cuando la colección de solo lectura envuelve una colección interna privada que puede cambiar.
Aquí hay uno de esos escenarios:
Suponga que tiene un servicio que permite agregar y eliminar elementos a una colección interna desde fuera del servicio. Ahora suponga que desea exponer los valores de la colección pero no quiere que los consumidores manipulen la colección directamente; por lo que envuelve la colección interna en un
ReadOnlyObservableCollection
.Ahora suponga que desea notificar a los consumidores del servicio cuando cambia la colección interna (y, por lo tanto, cuando
ReadOnlyObservableCollection
cambia la expuesta ). En lugar de lanzar su propia implementación, solo desea exponerCollectionChanged
elReadOnlyObservableCollection
. En lugar de obligar al consumidor a hacer una suposición sobre la implementación delReadOnlyObservableCollection
, simplemente intercambia elReadOnlyObservableCollection
con este personalizadoObservableReadOnlyCollection
y listo.Se
ObservableReadOnlyCollection
ocultaReadOnlyObservableCollection.CollectionChanged
con los suyos y simplemente pasa todos los eventos modificados de la colección a cualquier controlador de eventos adjunto.fuente