| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="ObservableCollectionDecorator.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections; |
| | | 9 | | using System.Collections.Generic; |
| | | 10 | | using System.Collections.Specialized; |
| | | 11 | | using System.ComponentModel; |
| | | 12 | | |
| | | 13 | | namespace MyNet.Collections; |
| | | 14 | | |
| | | 15 | | /// <summary> |
| | | 16 | | /// An observable collection that schedules notifications on a specific scheduler (typically UI thread). |
| | | 17 | | /// It only owns notification dispatching responsibility. |
| | | 18 | | /// </summary> |
| | | 19 | | /// <typeparam name="T">The type of items in the collection.</typeparam> |
| | | 20 | | public abstract class ObservableCollectionDecorator<T> : IObservableRangeCollection<T>, IDisposable |
| | | 21 | | { |
| | | 22 | | /// <summary> |
| | | 23 | | /// Initializes a new instance of the <see cref="ObservableCollectionDecorator{T}"/> class with an existing inner co |
| | | 24 | | /// </summary> |
| | | 25 | | /// <param name="inner">The wrapped collection.</param> |
| | | 26 | | protected ObservableCollectionDecorator(IObservableRangeCollection<T> inner) |
| | | 27 | | { |
| | 51 | 28 | | Inner = inner ?? throw new ArgumentNullException(nameof(inner)); |
| | | 29 | | |
| | 48 | 30 | | Inner.CollectionChanged += OnCollectionChanged; |
| | 48 | 31 | | Inner.PropertyChanged += OnPropertyChanged; |
| | 48 | 32 | | } |
| | | 33 | | |
| | | 34 | | /// <summary> |
| | | 35 | | /// Gets the inner observable range collection that actually holds the items and raises notifications. |
| | | 36 | | /// </summary> |
| | | 37 | | protected IObservableRangeCollection<T> Inner { get; } |
| | | 38 | | |
| | | 39 | | /// <inheritdoc /> |
| | | 40 | | public event NotifyCollectionChangedEventHandler? CollectionChanged; |
| | | 41 | | |
| | | 42 | | /// <inheritdoc /> |
| | | 43 | | public event PropertyChangedEventHandler? PropertyChanged; |
| | | 44 | | |
| | | 45 | | /// <inheritdoc /> |
| | 3 | 46 | | public virtual int Count => Inner.Count; |
| | | 47 | | |
| | | 48 | | /// <inheritdoc /> |
| | 0 | 49 | | public virtual bool IsReadOnly => false; |
| | | 50 | | |
| | | 51 | | /// <inheritdoc /> |
| | | 52 | | public virtual T this[int index] |
| | | 53 | | { |
| | 3 | 54 | | get => Inner[index]; |
| | 3 | 55 | | set => Inner[index] = value; |
| | | 56 | | } |
| | | 57 | | |
| | | 58 | | /// <inheritdoc /> |
| | 18 | 59 | | public virtual void Add(T item) => Inner.Add(item); |
| | | 60 | | |
| | | 61 | | /// <summary> |
| | | 62 | | /// Adds a range of items to the collection. |
| | | 63 | | /// </summary> |
| | 3 | 64 | | public virtual void AddRange(IEnumerable<T> items) => Inner.AddRange(items); |
| | | 65 | | |
| | | 66 | | /// <inheritdoc /> |
| | 0 | 67 | | public virtual void Clear() => Inner.Clear(); |
| | | 68 | | |
| | | 69 | | /// <inheritdoc /> |
| | 0 | 70 | | public virtual bool Contains(T item) => Inner.Contains(item); |
| | | 71 | | |
| | | 72 | | /// <inheritdoc /> |
| | 3 | 73 | | public virtual void CopyTo(T[] array, int arrayIndex) => Inner.CopyTo(array, arrayIndex); |
| | | 74 | | |
| | | 75 | | /// <inheritdoc /> |
| | 15 | 76 | | public virtual IEnumerator<T> GetEnumerator() => Inner.GetEnumerator(); |
| | | 77 | | |
| | | 78 | | /// <inheritdoc /> |
| | 3 | 79 | | public virtual int IndexOf(T item) => Inner.IndexOf(item); |
| | | 80 | | |
| | | 81 | | /// <inheritdoc /> |
| | 3 | 82 | | public virtual void Insert(int index, T item) => Inner.Insert(index, item); |
| | | 83 | | |
| | | 84 | | /// <summary> |
| | | 85 | | /// Moves an item from one index to another. |
| | | 86 | | /// </summary> |
| | 3 | 87 | | public virtual void Move(int oldIndex, int newIndex) => Inner.Move(oldIndex, newIndex); |
| | | 88 | | |
| | | 89 | | /// <inheritdoc /> |
| | 3 | 90 | | public virtual bool Remove(T item) => Inner.Remove(item); |
| | | 91 | | |
| | | 92 | | /// <inheritdoc /> |
| | 0 | 93 | | public virtual void RemoveAt(int index) => Inner.RemoveAt(index); |
| | | 94 | | |
| | | 95 | | /// <summary> |
| | | 96 | | /// Replaces the current items with the specified items. |
| | | 97 | | /// </summary> |
| | 3 | 98 | | public virtual void Load(IEnumerable<T> items) => Inner.Load(items); |
| | | 99 | | |
| | | 100 | | /// <summary> |
| | | 101 | | /// Removes a range of items from the collection. |
| | | 102 | | /// </summary> |
| | 0 | 103 | | public virtual void RemoveRange(int index, int count) => Inner.RemoveRange(index, count); |
| | | 104 | | |
| | | 105 | | /// <summary> |
| | | 106 | | /// Exposes RemoveAll from the wrapped range collection. |
| | | 107 | | /// </summary> |
| | 3 | 108 | | public virtual int RemoveAll(Func<T, bool> predicate) => Inner.RemoveAll(predicate); |
| | | 109 | | |
| | | 110 | | /// <summary> |
| | | 111 | | /// Exposes InsertRange from the wrapped range collection. |
| | | 112 | | /// </summary> |
| | 3 | 113 | | public virtual void InsertRange(IEnumerable<T> items, int index) => Inner.InsertRange(items, index); |
| | | 114 | | |
| | | 115 | | /// <summary> |
| | | 116 | | /// Exposes capacity configuration from the wrapped range collection. |
| | | 117 | | /// </summary> |
| | 3 | 118 | | public virtual void SetCapacity(int capacity) => Inner.SetCapacity(capacity); |
| | | 119 | | |
| | | 120 | | /// <summary> |
| | | 121 | | /// Gets the current capacity of the wrapped range collection. |
| | | 122 | | /// </summary> |
| | 0 | 123 | | public virtual int Capacity => Inner.Capacity; |
| | | 124 | | |
| | | 125 | | /// <inheritdoc /> |
| | 0 | 126 | | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
| | | 127 | | |
| | | 128 | | /// <summary> |
| | | 129 | | /// Suspends count notifications, allowing batch updates without triggering count change events until the returned d |
| | | 130 | | /// </summary> |
| | | 131 | | /// <returns>A disposable that, when disposed, resumes count notifications.</returns> |
| | 0 | 132 | | public virtual IDisposable SuspendCount() => Inner.SuspendCount(); |
| | | 133 | | |
| | | 134 | | /// <summary> |
| | | 135 | | /// Suspends collection and property change notifications, allowing batch updates without triggering events until th |
| | | 136 | | /// </summary> |
| | | 137 | | /// <returns>A disposable that, when disposed, resumes collection and property change notifications.</returns> |
| | 3 | 138 | | public virtual IDisposable SuspendNotifications() => Inner.SuspendNotifications(); |
| | | 139 | | |
| | | 140 | | /// <summary> |
| | | 141 | | /// Handles collection changed events from the inner collection and dispatches them on the specified scheduler. |
| | | 142 | | /// </summary> |
| | | 143 | | /// <param name="sender">The source of the event.</param> |
| | | 144 | | /// <param name="e">The event data.</param> |
| | | 145 | | protected virtual void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) |
| | | 146 | | { |
| | 8202 | 147 | | var collectionChanged = CollectionChanged; |
| | | 148 | | |
| | 8202 | 149 | | collectionChanged?.Invoke(this, e); |
| | 18 | 150 | | } |
| | | 151 | | |
| | | 152 | | /// <summary> |
| | | 153 | | /// Handles property changed events from the inner collection and dispatches them on the specified scheduler. |
| | | 154 | | /// </summary> |
| | | 155 | | /// <param name="sender">The source of the event.</param> |
| | | 156 | | /// <param name="e">The event data.</param> |
| | | 157 | | protected virtual void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) |
| | | 158 | | { |
| | 16341 | 159 | | var propertyChanged = PropertyChanged; |
| | | 160 | | |
| | 16341 | 161 | | propertyChanged?.Invoke(this, e); |
| | 6 | 162 | | } |
| | | 163 | | |
| | | 164 | | #region IDisposable Support |
| | | 165 | | |
| | | 166 | | private bool _isDisposed; |
| | | 167 | | |
| | | 168 | | /// <summary> |
| | | 169 | | /// Disposes the object and releases all resources. |
| | | 170 | | /// </summary> |
| | | 171 | | /// <param name="disposing">Indicates whether the method is called from Dispose (true) or from a finalizer (false).< |
| | | 172 | | protected virtual void Dispose(bool disposing) |
| | | 173 | | { |
| | 18 | 174 | | if (_isDisposed) |
| | 0 | 175 | | return; |
| | | 176 | | |
| | 18 | 177 | | if (disposing) |
| | | 178 | | { |
| | 18 | 179 | | Inner.CollectionChanged -= OnCollectionChanged; |
| | 18 | 180 | | Inner.PropertyChanged -= OnPropertyChanged; |
| | | 181 | | } |
| | | 182 | | |
| | 18 | 183 | | _isDisposed = true; |
| | 18 | 184 | | } |
| | | 185 | | |
| | | 186 | | /// <summary> |
| | | 187 | | /// Disposes the object and releases all resources. |
| | | 188 | | /// </summary> |
| | | 189 | | public void Dispose() |
| | | 190 | | { |
| | 18 | 191 | | Dispose(true); |
| | 18 | 192 | | GC.SuppressFinalize(this); |
| | 18 | 193 | | } |
| | | 194 | | |
| | | 195 | | #endregion IDisposable Support |
| | | 196 | | } |
| | | 197 | | |