Почему ReadOnlyObservableCollection.CollectionChanged
защищено, а не публично (как соответствующее ObservableCollection.CollectionChanged
)?
Какая польза от реализации коллекции, INotifyCollectionChanged
если я не могу получить доступ к CollectionChanged
событию?
c#
.net
collections
Оскар
источник
источник
Ответы:
Вот решение: события CollectionChanged в ReadOnlyObservableCollection
Вы должны привести коллекцию к 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
Обновить:
Портал Connect был закрыт Microsoft. Так что ссылка выше больше не работает.
Библиотека My Win Application Framework (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
позже - это изменение, нарушающее двоичный код, и, следовательно, это не будет сделано без очень веских аргументов, а я боюсь, что «я должен привести объект один раз, чтобы присоединить обработчики» попросту не является.Источник: комментарий GitHub Ника Герерры, 19 августа 2015 г.
источник
Это было самым популярным в 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; }
пример
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; }
источник
Решение
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
изменениях). Вместо того , чтобы качению своей собственной реализации , вы просто хотите , чтобы разоблачитьCollectionChanged
изReadOnlyObservableCollection
. Вместо того, чтобы заставлять потребителя делать предположения о реализацииReadOnlyObservableCollection
, вы просто меняете местамиReadOnlyObservableCollection
этот обычайObservableReadOnlyCollection
, и все готово.Он
ObservableReadOnlyCollection
скрываетсяReadOnlyObservableCollection.CollectionChanged
со своим собственным и просто передает все события изменения коллекции любому присоединенному обработчику событий.источник