Por que é ReadOnlyObservableCollection.CollectionChanged
protegido e não público (como o correspondente ObservableCollection.CollectionChanged
)?
Qual é a utilidade de uma implementação de coleção INotifyCollectionChanged
se eu não consigo acessar o CollectionChanged
evento?
c#
.net
collections
Oskar
fonte
fonte
Respostas:
Aqui está a solução: Eventos CollectionChanged em ReadOnlyObservableCollection
Você deve lançar a coleção para INotifyCollectionChanged .
fonte
Eu encontrei uma maneira de como fazer isso:
ObservableCollection<string> obsCollection = new ObservableCollection<string>(); INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection); collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);
Você só precisa se referir à sua coleção explicitamente pela interface INotifyCollectionChanged .
fonte
ReadOnlyObservableCollection
você vai encontrar este:event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
. Implementação de interface explícita do evento.Eu sei que este post é antigo, no entanto, as pessoas devem levar um tempo para entender os padrões usados no .NET antes de comentar. Uma coleção somente leitura é um wrapper em uma coleção existente que impede os consumidores de modificá-la diretamente. Observe
ReadOnlyCollection
e você verá que é um wrapper em umIList<T>
que pode ou não ser mutável. Coleções imutáveis são um assunto diferente e são cobertas pela nova biblioteca de coleções imutáveisEm outras palavras, somente leitura não é o mesmo que imutável !!!!
Deixando isso de lado,
ReadOnlyObservableCollection
deve implementar implicitamenteINotifyCollectionChanged
.fonte
Definitivamente, existem boas razões para querer assinar notificações de alterações de coleção em um ReadOnlyObservableCollection . Portanto, como uma alternativa para meramente converter sua coleção como INotifyCollectionChanged , se você estiver subclassificando ReadOnlyObservableCollection , o seguinte fornece uma maneira mais sintaticamente conveniente de acessar o 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; } } }
Isso funcionou bem para mim antes.
fonte
Você pode votar na entrada do bug no Microsoft Connect que descreve esse problema: https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public
Atualizar:
O portal Connect foi encerrado pela Microsoft. Então o link acima não funciona mais.
A biblioteca My Win Application Framework (WAF) fornece uma solução: classe 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; } } }
fonte
Como já respondido, você tem duas opções: você pode lançar o
ReadOnlyObservableCollection<T>
para a interfaceINotifyCollectionChanged
para acessar oCollectionChanged
evento explicitamente implementado ou pode criar sua própria classe de wrapper que faz isso uma vez no construtor e apenas conecta os eventos do wrapperReadOnlyObservableCollection<T>
.Alguns insights adicionais sobre por que esse problema ainda não foi corrigido:
Como você pode ver no código-fonte ,
ReadOnlyObservableCollection<T>
é uma classe pública, não lacrada (ou seja, herdável), onde os eventos são marcadosprotected virtual
.Ou seja, pode haver programas compilados com classes derivadas de
ReadOnlyObservableCollection<T>
, com definições de eventos substituídas, mas comprotected
visibilidade. Esses programas conteriam código inválido uma vez que a visibilidade do evento fosse alterada parapublic
na classe base, porque não é permitido restringir a visibilidade de um evento em classes derivadas.Portanto, infelizmente, criar
protected virtual
eventospublic
posteriormente é uma alteração de quebra de binário e, portanto, não será feito sem um raciocínio muito bom, o que, infelizmente, "tenho que lançar o objeto uma vez para anexar manipuladores" não é.Fonte: comentário do GitHub por Nick Guererra, 19 de agosto de 2015
fonte
Este foi o maior sucesso no Google, então decidi adicionar minha solução no caso de outras pessoas procurarem.
Usando as informações acima (sobre a necessidade de transmitir para INotifyCollectionChanged ), fiz dois métodos de extensão para registrar e cancelar o registro.
Minha solução - Métodos de extensão
public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler) { collection.CollectionChanged += handler; } public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler) { collection.CollectionChanged -= handler; }
Exemplo
IThing.cs
public interface IThing { string Name { get; } ReadOnlyObservableCollection<int> Values { get; } }
Usando os métodos de extensão
public void AddThing(IThing thing) { //... thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged); } public void RemoveThing(IThing thing) { //... thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged); }
Solução 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; }
fonte
Solução
ReadOnlyObservableCollection.CollectionChanged
não é exposto (por razões válidas descritas em outras respostas), então vamos fazer nossa própria classe de wrapper que o expõe:/// <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); }
Explicação
As pessoas perguntam por que você deseja observar as alterações em uma coleção somente leitura, então explicarei uma das muitas situações válidas; quando a coleção somente leitura envolve uma coleção interna privada que pode mudar.
Aqui está um desses cenários:
Suponha que você tenha um serviço que permite adicionar e remover itens de fora do serviço para uma coleção interna. Agora suponha que você deseja expor os valores da coleção, mas não deseja que os consumidores manipulem a coleção diretamente; então você envolve a coleção interna em a
ReadOnlyObservableCollection
.Agora, suponha que você deseja notificar os consumidores sobre o serviço quando a coleção interna muda (e, portanto, quando a exposição
ReadOnlyObservableCollection
muda). Em vez de lançar sua própria implementação, você apenas deseja expor oCollectionChanged
deReadOnlyObservableCollection
. Em vez de forçar o consumidor a fazer uma suposição sobre a implementação doReadOnlyObservableCollection
, você simplesmente troca oReadOnlyObservableCollection
por este personalizadoObservableReadOnlyCollection
e pronto.O
ObservableReadOnlyCollection
ocultaReadOnlyObservableCollection.CollectionChanged
com o seu próprio e simplesmente passa todos os eventos alterados da coleção para qualquer manipulador de eventos anexado.fonte