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:
public class ObservableReadOnlyCollection<T> : ReadOnlyObservableCollection<T>
{
public new NotifyCollectionChangedEventHandler CollectionChanged;
public ObservableReadOnlyCollection(ObservableCollection<T> list) : base(list) { }
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.
Observe que para envolver a coleção interna com ReadOnlyObservableCollectiona coleção interna é forçado a derivar ObservableCollectionpelo construtor de 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 ReadOnlyObservableCollectionmuda). Em vez de lançar sua própria implementação, você apenas deseja expor o CollectionChangedde ReadOnlyObservableCollection. Em vez de forçar o consumidor a fazer uma suposição sobre a implementação do ReadOnlyObservableCollection, você simplesmente troca o ReadOnlyObservableCollectionpor este personalizado ObservableReadOnlyCollectione pronto.
O ObservableReadOnlyCollectionoculta ReadOnlyObservableCollection.CollectionChangedcom o seu próprio e simplesmente passa todos os eventos alterados da coleção para qualquer manipulador de eventos anexado.