using System.Collections; using System.Collections.Specialized; namespace ObservableCollections { public partial class ObservableSortedSet : IReadOnlyCollection, IObservableCollection { public ISynchronizedView CreateView(Func transform) { return new View(this, transform); } sealed class View : ISynchronizedView { public ISynchronizedViewFilter Filter { get { lock (SyncRoot) return filter; } } readonly ObservableSortedSet source; readonly Func selector; readonly Dictionary dict; int filteredCount; ISynchronizedViewFilter filter; public event NotifyViewChangedEventHandler? ViewChanged; public event Action? RejectedViewChanged; public event Action? CollectionStateChanged; public object SyncRoot { get; } public View(ObservableSortedSet source, Func selector) { this.source = source; this.selector = selector; this.filter = SynchronizedViewFilter.Null; this.SyncRoot = new object(); lock (source.SyncRoot) { this.dict = source._set.ToDictionary(x => x, x => (x, selector(x))); this.filteredCount = dict.Count; this.source.CollectionChanged += SourceCollectionChanged; } } public int Count { get { lock (SyncRoot) { return filteredCount; } } } public int UnfilteredCount { get { lock (SyncRoot) { return dict.Count; } } } public void AttachFilter(ISynchronizedViewFilter filter) { if (filter.IsNullFilter()) { ResetFilter(); return; } lock (SyncRoot) { this.filter = filter; this.filteredCount = 0; foreach (var (_, (value, view)) in dict) { if (filter.IsMatch(value, view)) { filteredCount++; } } ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyCollectionChangedAction.Reset, true)); } } public void ResetFilter() { lock (SyncRoot) { this.filter = SynchronizedViewFilter.Null; this.filteredCount = dict.Count; ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyCollectionChangedAction.Reset, true)); } } public ISynchronizedViewList ToViewList() { return new FiltableSynchronizedViewList(this, isSupportRangeFeature: true); } public NotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged() { return new FiltableSynchronizedViewList(this, isSupportRangeFeature: false); } public NotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher) { return new FiltableSynchronizedViewList(this, isSupportRangeFeature: false, collectionEventDispatcher); } public IEnumerator GetEnumerator() { lock (SyncRoot) { foreach (var item in dict) { if (filter.IsMatch(item.Value)) { yield return item.Value.Item2; } } } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IEnumerable<(T Value, TView View)> Filtered { get { lock (SyncRoot) { foreach (var item in dict) { if (filter.IsMatch(item.Value)) { yield return item.Value; } } } } } public IEnumerable<(T Value, TView View)> Unfiltered { get { lock (SyncRoot) { foreach (var item in dict) { yield return item.Value; } } } } public void Dispose() { this.source.CollectionChanged -= SourceCollectionChanged; } private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) { lock (SyncRoot) { switch (e.Action) { case NotifyCollectionChangedAction.Add: if (e.IsSingleItem) { var v = (e.NewItem, selector(e.NewItem)); dict.Add(e.NewItem, v); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, -1); } else { var i = e.NewStartingIndex; foreach (var item in e.NewItems) { var v = (item, selector(item)); dict.Add(item, v); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, i++); } } break; case NotifyCollectionChangedAction.Remove: if (e.IsSingleItem) { if (dict.Remove(e.OldItem, out var value)) { this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, value, -1); } } else { foreach (var item in e.OldItems) { if (dict.Remove(item, out var value)) { this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, value, -1); } } } break; case NotifyCollectionChangedAction.Reset: dict.Clear(); this.InvokeOnReset(ref filteredCount, ViewChanged); break; case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Move: default: break; } CollectionStateChanged?.Invoke(e.Action); } } } } }