From 295cef5ae54441671bfcad836b70362e44eb62de Mon Sep 17 00:00:00 2001 From: neuecc Date: Tue, 27 Aug 2024 18:36:01 +0900 Subject: [PATCH] Dict, HashSet, Queue, RingBuffer, Stack --- .../ObservableDictionary.Views.cs | 152 +++++++++++------- .../ObservableDictionary.cs | 3 +- .../ObservableHashSet.Views.cs | 117 +++++++++----- .../ObservableList.Views.cs | 1 - .../ObservableQueue.Views.cs | 139 +++++++++------- .../ObservableRingBuffer.Views.cs | 132 +++++++++------ .../ObservableStack.Views.cs | 122 ++++++++------ 7 files changed, 418 insertions(+), 248 deletions(-) diff --git a/src/ObservableCollections/ObservableDictionary.Views.cs b/src/ObservableCollections/ObservableDictionary.Views.cs index c80914b..4bf347f 100644 --- a/src/ObservableCollections/ObservableDictionary.Views.cs +++ b/src/ObservableCollections/ObservableDictionary.Views.cs @@ -9,7 +9,7 @@ namespace ObservableCollections { public sealed partial class ObservableDictionary { - public ISynchronizedView, TView> CreateView(Func, TView> transform, bool _ = false) + public ISynchronizedView, TView> CreateView(Func, TView> transform) { // reverse is no used. return new View(this, transform); @@ -19,32 +19,45 @@ namespace ObservableCollections { readonly ObservableDictionary source; readonly Func, TView> selector; - ISynchronizedViewFilter, TView> filter; + ISynchronizedViewFilter> filter; readonly Dictionary dict; + int filteredCount; public View(ObservableDictionary source, Func, TView> selector) { this.source = source; this.selector = selector; - this.filter = SynchronizedViewFilter, TView>.Null; + this.filter = SynchronizedViewFilter>.Null; this.SyncRoot = new object(); lock (source.SyncRoot) { this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x))); + this.filteredCount = dict.Count; this.source.CollectionChanged += SourceCollectionChanged; } } public object SyncRoot { get; } - public event NotifyCollectionChangedEventHandler>? RoutingCollectionChanged; + public event Action, TView>>? ViewChanged; public event Action? CollectionStateChanged; - public ISynchronizedViewFilter, TView> CurrentFilter + public ISynchronizedViewFilter> Filter { get { lock (SyncRoot) return filter; } } public int Count + { + get + { + lock (SyncRoot) + { + return filteredCount; + } + } + } + + public int UnfilteredCount { get { @@ -60,76 +73,104 @@ namespace ObservableCollections this.source.CollectionChanged -= SourceCollectionChanged; } - public void AttachFilter(ISynchronizedViewFilter, TView> filter, bool invokeAddEventForCurrentElements = false) + public void AttachFilter(ISynchronizedViewFilter> filter) { + if (filter.IsNullFilter()) + { + ResetFilter(); + return; + } + lock (SyncRoot) { this.filter = filter; + this.filteredCount = 0; foreach (var v in dict) { var value = new KeyValuePair(v.Key, v.Value.Item1); - var view = v.Value.Item2; - if (invokeAddEventForCurrentElements) + if (filter.IsMatch(value)) { - filter.InvokeOnAdd(value, view, -1); - } - else - { - filter.InvokeOnAttach(value, view); + filteredCount++; } } + + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs, TView>(NotifyViewChangedAction.FilterReset)); } } - public void ResetFilter(Action, TView>? resetAction) + public void ResetFilter() { lock (SyncRoot) { - this.filter = SynchronizedViewFilter, TView>.Null; - if (resetAction != null) - { - foreach (var v in dict) - { - resetAction(new KeyValuePair(v.Key, v.Value.Item1), v.Value.Item2); - } - } + this.filter = SynchronizedViewFilter>.Null; + this.filteredCount = dict.Count; + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs, TView>(NotifyViewChangedAction.FilterReset)); } } + public ISynchronizedViewList ToViewList() + { + return new SynchronizedViewList, TView>(this); + } + public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged() { - lock (SyncRoot) - { - return new NotifyCollectionChangedSynchronizedView, TView>(this, null); - } + return new NotifyCollectionChangedSynchronizedView, TView>(this, null); } public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher) { - lock (SyncRoot) - { - return new NotifyCollectionChangedSynchronizedView, TView>(this, collectionEventDispatcher); - } + return new NotifyCollectionChangedSynchronizedView, TView>(this, collectionEventDispatcher); } - public IEnumerator<(KeyValuePair, TView)> GetEnumerator() + public IEnumerator GetEnumerator() { lock (SyncRoot) { foreach (var item in dict) { var v = (new KeyValuePair(item.Key, item.Value.Item1), item.Value.Item2); - if (filter.IsMatch(v.Item1, v.Item2)) + if (filter.IsMatch(v.Item1)) { - yield return v; + yield return v.Item2; } } } } - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IEnumerable<(KeyValuePair Value, TView View)> Filtered { - return GetEnumerator(); + get + { + lock (SyncRoot) + { + foreach (var item in dict) + { + var v = (new KeyValuePair(item.Key, item.Value.Item1), item.Value.Item2); + if (filter.IsMatch(v.Item1)) + { + yield return v; + } + } + } + } + } + + public IEnumerable<(KeyValuePair Value, TView View)> Unfiltered + { + get + { + lock (SyncRoot) + { + foreach (var item in dict) + { + var v = (new KeyValuePair(item.Key, item.Value.Item1), item.Value.Item2); + yield return v; + } + } + } } private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs> e) @@ -140,41 +181,40 @@ namespace ObservableCollections switch (e.Action) { case NotifyCollectionChangedAction.Add: - { - var v = selector(e.NewItem); - dict.Add(e.NewItem.Key, (e.NewItem.Value, v)); - filter.InvokeOnAdd(e.NewItem, v, -1); - } + { + var v = selector(e.NewItem); + dict.Add(e.NewItem.Key, (e.NewItem.Value, v)); + this.InvokeOnAdd(ref filteredCount, ViewChanged, e.NewItem, v, -1); + } break; case NotifyCollectionChangedAction.Remove: - { - if (dict.Remove(e.OldItem.Key, out var v)) { - filter.InvokeOnRemove(e.OldItem, v.Item2, -1); + if (dict.Remove(e.OldItem.Key, out var v)) + { + this.InvokeOnRemove(ref filteredCount, ViewChanged, e.OldItem, v.Item2, -1); + } } - } break; case NotifyCollectionChangedAction.Replace: - { - var v = selector(e.NewItem); - dict.Remove(e.OldItem.Key, out var ov); - dict[e.NewItem.Key] = (e.NewItem.Value, v); - - filter.InvokeOnReplace(e.NewItem, v, e.OldItem, ov.Item2, -1); - } + { + var v = selector(e.NewItem); + dict.Remove(e.OldItem.Key, out var ov); + dict[e.NewItem.Key] = (e.NewItem.Value, v); + + this.InvokeOnReplace(ref filteredCount, ViewChanged, e.NewItem, v, e.OldItem, ov.Item2, -1); + } break; case NotifyCollectionChangedAction.Reset: - { - dict.Clear(); - filter.InvokeOnReset(); - } + { + dict.Clear(); + this.InvokeOnReset(ref filteredCount, ViewChanged); + } break; case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation. default: break; } - RoutingCollectionChanged?.Invoke(e); CollectionStateChanged?.Invoke(e.Action); } } diff --git a/src/ObservableCollections/ObservableDictionary.cs b/src/ObservableCollections/ObservableDictionary.cs index 118fc87..bb9e2f8 100644 --- a/src/ObservableCollections/ObservableDictionary.cs +++ b/src/ObservableCollections/ObservableDictionary.cs @@ -7,8 +7,7 @@ using System.Linq; namespace ObservableCollections { - public sealed partial class ObservableDictionary : IDictionary, - IReadOnlyObservableDictionary + public sealed partial class ObservableDictionary : IDictionary, IReadOnlyObservableDictionary where TKey : notnull { readonly Dictionary dictionary; diff --git a/src/ObservableCollections/ObservableHashSet.Views.cs b/src/ObservableCollections/ObservableHashSet.Views.cs index 5bf9123..ea846f2 100644 --- a/src/ObservableCollections/ObservableHashSet.Views.cs +++ b/src/ObservableCollections/ObservableHashSet.Views.cs @@ -4,19 +4,20 @@ using System.Collections.Generic; using System.Collections.Specialized; using System; using System.Linq; +using System.Threading.Tasks; namespace ObservableCollections { public sealed partial class ObservableHashSet : IReadOnlyCollection, IObservableCollection { - public ISynchronizedView CreateView(Func transform, bool _ = false) + public ISynchronizedView CreateView(Func transform) { return new View(this, transform); } sealed class View : ISynchronizedView { - public ISynchronizedViewFilter CurrentFilter + public ISynchronizedViewFilter Filter { get { lock (SyncRoot) return filter; } } @@ -24,10 +25,11 @@ namespace ObservableCollections readonly ObservableHashSet source; readonly Func selector; readonly Dictionary dict; + int filteredCount; - ISynchronizedViewFilter filter; + ISynchronizedViewFilter filter; - public event NotifyCollectionChangedEventHandler? RoutingCollectionChanged; + public event Action>? ViewChanged; public event Action? CollectionStateChanged; public object SyncRoot { get; } @@ -36,16 +38,28 @@ namespace ObservableCollections { this.source = source; this.selector = selector; - this.filter = SynchronizedViewFilter.Null; + 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 { @@ -56,65 +70,62 @@ namespace ObservableCollections } } - public void AttachFilter(ISynchronizedViewFilter filter, bool invokeAddEventForCurrentElements = false) + 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 (invokeAddEventForCurrentElements) + if (filter.IsMatch(value)) { - filter.InvokeOnAdd((value, view), -1); - } - else - { - filter.InvokeOnAttach(value, view); + filteredCount++; } } } } - public void ResetFilter(Action? resetAction) + public void ResetFilter() { lock (SyncRoot) { - this.filter = SynchronizedViewFilter.Null; - if (resetAction != null) - { - foreach (var (_, (value, view)) in dict) - { - resetAction(value, view); - } - } + this.filter = SynchronizedViewFilter.Null; + this.filteredCount = dict.Count; + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyViewChangedAction.FilterReset)); } } + public ISynchronizedViewList ToViewList() + { + return new SynchronizedViewList(this); + } + public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged() { - lock (SyncRoot) - { - return new NotifyCollectionChangedSynchronizedView(this, null); - } + return new NotifyCollectionChangedSynchronizedView(this, null); } public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher) { - lock (SyncRoot) - { - return new NotifyCollectionChangedSynchronizedView(this, collectionEventDispatcher); - } + return new NotifyCollectionChangedSynchronizedView(this, collectionEventDispatcher); } - public IEnumerator<(T, TView)> GetEnumerator() + public IEnumerator GetEnumerator() { lock (SyncRoot) { foreach (var item in dict) { - if (filter.IsMatch(item.Value.Item1, item.Value.Item2)) + if (filter.IsMatch(item.Value.Item1)) { - yield return item.Value; + yield return item.Value.Item2; } } } @@ -122,6 +133,37 @@ namespace ObservableCollections IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerable<(T Value, TView View)> Filtered + { + get + { + lock (SyncRoot) + { + foreach (var item in dict) + { + if (filter.IsMatch(item.Value.Item1)) + { + 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; @@ -138,7 +180,7 @@ namespace ObservableCollections { var v = (e.NewItem, selector(e.NewItem)); dict.Add(e.NewItem, v); - filter.InvokeOnAdd(v, -1); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, -1); } else { @@ -147,7 +189,7 @@ namespace ObservableCollections { var v = (item, selector(item)); dict.Add(item, v); - filter.InvokeOnAdd(v, i++); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++); } } break; @@ -156,7 +198,7 @@ namespace ObservableCollections { if (dict.Remove(e.OldItem, out var value)) { - filter.InvokeOnRemove(value, -1); + this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1); } } else @@ -165,14 +207,14 @@ namespace ObservableCollections { if (dict.Remove(item, out var value)) { - filter.InvokeOnRemove(value, -1); + this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1); } } } break; case NotifyCollectionChangedAction.Reset: dict.Clear(); - filter.InvokeOnReset(); + this.InvokeOnReset(ref filteredCount, ViewChanged); break; case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Move: @@ -180,7 +222,6 @@ namespace ObservableCollections break; } - RoutingCollectionChanged?.Invoke(e); CollectionStateChanged?.Invoke(e.Action); } } diff --git a/src/ObservableCollections/ObservableList.Views.cs b/src/ObservableCollections/ObservableList.Views.cs index b2e0bc0..6048a27 100644 --- a/src/ObservableCollections/ObservableList.Views.cs +++ b/src/ObservableCollections/ObservableList.Views.cs @@ -98,7 +98,6 @@ namespace ObservableCollections lock (SyncRoot) { this.filter = filter; - this.filteredCount = 0; for (var i = 0; i < list.Count; i++) { diff --git a/src/ObservableCollections/ObservableQueue.Views.cs b/src/ObservableCollections/ObservableQueue.Views.cs index 37bdfbc..b6d9601 100644 --- a/src/ObservableCollections/ObservableQueue.Views.cs +++ b/src/ObservableCollections/ObservableQueue.Views.cs @@ -4,14 +4,15 @@ using System.Collections.Specialized; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace ObservableCollections { public sealed partial class ObservableQueue : IReadOnlyCollection, IObservableCollection { - public ISynchronizedView CreateView(Func transform, bool reverse = false) + public ISynchronizedView CreateView(Func transform) { - return new View(this, transform, reverse); + return new View(this, transform); } class View : ISynchronizedView @@ -20,34 +21,46 @@ namespace ObservableCollections readonly Func selector; readonly bool reverse; protected readonly Queue<(T, TView)> queue; + int filteredCount; - ISynchronizedViewFilter filter; + ISynchronizedViewFilter filter; - public event NotifyCollectionChangedEventHandler? RoutingCollectionChanged; + public event Action>? ViewChanged; public event Action? CollectionStateChanged; public object SyncRoot { get; } - public ISynchronizedViewFilter CurrentFilter + public ISynchronizedViewFilter Filter { get { lock (SyncRoot) return filter; } } - public View(ObservableQueue source, Func selector, bool reverse) + public View(ObservableQueue source, Func selector) { this.source = source; this.selector = selector; - this.reverse = reverse; - this.filter = SynchronizedViewFilter.Null; + this.filter = SynchronizedViewFilter.Null; this.SyncRoot = new object(); lock (source.SyncRoot) { this.queue = new Queue<(T, TView)>(source.queue.Select(x => (x, selector(x)))); + this.filteredCount = queue.Count; this.source.CollectionChanged += SourceCollectionChanged; } } public int Count + { + get + { + lock (SyncRoot) + { + return filteredCount; + } + } + } + + public int UnfilteredCount { get { @@ -58,79 +71,63 @@ namespace ObservableCollections } } - public void AttachFilter(ISynchronizedViewFilter filter, bool invokeAddEventForCurrentElements = false) + public void AttachFilter(ISynchronizedViewFilter filter) { + if (filter.IsNullFilter()) + { + ResetFilter(); + return; + } + lock (SyncRoot) { this.filter = filter; - var i = 0; + this.filteredCount = 0; foreach (var (value, view) in queue) { - if (invokeAddEventForCurrentElements) + if (filter.IsMatch(value)) { - filter.InvokeOnAdd(value, view, i++); - } - else - { - filter.InvokeOnAttach(value, view); + filteredCount++; } } + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyViewChangedAction.FilterReset)); } } - public void ResetFilter(Action? resetAction) + public void ResetFilter() { lock (SyncRoot) { - this.filter = SynchronizedViewFilter.Null; - if (resetAction != null) - { - foreach (var (item, view) in queue) - { - resetAction(item, view); - } - } + this.filter = SynchronizedViewFilter.Null; + this.filteredCount = queue.Count; + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyViewChangedAction.FilterReset)); } } + public ISynchronizedViewList ToViewList() + { + return new SynchronizedViewList(this); + } + public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged() { - lock (SyncRoot) - { - return new NotifyCollectionChangedSynchronizedView(this, null); - } + return new NotifyCollectionChangedSynchronizedView(this, null); } public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher) { - lock (SyncRoot) - { - return new NotifyCollectionChangedSynchronizedView(this, collectionEventDispatcher); - } + return new NotifyCollectionChangedSynchronizedView(this, collectionEventDispatcher); } - public IEnumerator<(T, TView)> GetEnumerator() + public IEnumerator GetEnumerator() { lock (SyncRoot) { - if (!reverse) + foreach (var item in queue) { - foreach (var item in queue) + if (filter.IsMatch(item.Item1)) { - if (filter.IsMatch(item.Item1, item.Item2)) - { - yield return item; - } - } - } - else - { - foreach (var item in queue.AsEnumerable().Reverse()) - { - if (filter.IsMatch(item.Item1, item.Item2)) - { - yield return item; - } + yield return item.Item2; } } } @@ -138,6 +135,37 @@ namespace ObservableCollections IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerable<(T Value, TView View)> Filtered + { + get + { + lock (SyncRoot) + { + foreach (var item in queue) + { + if (filter.IsMatch(item.Item1)) + { + yield return item; + } + } + } + } + } + + public IEnumerable<(T Value, TView View)> Unfiltered + { + get + { + lock (SyncRoot) + { + foreach (var item in queue) + { + yield return item; + } + } + } + } + public void Dispose() { this.source.CollectionChanged -= SourceCollectionChanged; @@ -155,7 +183,7 @@ namespace ObservableCollections { var v = (e.NewItem, selector(e.NewItem)); queue.Enqueue(v); - filter.InvokeOnAdd(v, e.NewStartingIndex); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex); } else { @@ -164,7 +192,7 @@ namespace ObservableCollections { var v = (item, selector(item)); queue.Enqueue(v); - filter.InvokeOnAdd(v, i++); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++); } } break; @@ -173,7 +201,7 @@ namespace ObservableCollections if (e.IsSingleItem) { var v = queue.Dequeue(); - filter.InvokeOnRemove(v.Item1, v.Item2, 0); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); } else { @@ -181,13 +209,13 @@ namespace ObservableCollections for (int i = 0; i < len; i++) { var v = queue.Dequeue(); - filter.InvokeOnRemove(v.Item1, v.Item2, 0); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); } } break; case NotifyCollectionChangedAction.Reset: queue.Clear(); - filter.InvokeOnReset(); + this.InvokeOnReset(ref filteredCount, ViewChanged); break; case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Move: @@ -195,7 +223,6 @@ namespace ObservableCollections break; } - RoutingCollectionChanged?.Invoke(e); CollectionStateChanged?.Invoke(e.Action); } } diff --git a/src/ObservableCollections/ObservableRingBuffer.Views.cs b/src/ObservableCollections/ObservableRingBuffer.Views.cs index b19c409..95a1b78 100644 --- a/src/ObservableCollections/ObservableRingBuffer.Views.cs +++ b/src/ObservableCollections/ObservableRingBuffer.Views.cs @@ -4,51 +4,63 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; +using System.Threading.Tasks; namespace ObservableCollections { public sealed partial class ObservableRingBuffer { - public ISynchronizedView CreateView(Func transform, bool reverse = false) + public ISynchronizedView CreateView(Func transform) { - return new View(this, transform, reverse); + return new View(this, transform); } // used with ObservableFixedSizeRingBuffer internal sealed class View : ISynchronizedView { - public ISynchronizedViewFilter CurrentFilter + public ISynchronizedViewFilter Filter { get { lock (SyncRoot) return filter; } } readonly IObservableCollection source; readonly Func selector; - readonly bool reverse; readonly RingBuffer<(T, TView)> ringBuffer; + int filteredCount; - ISynchronizedViewFilter filter; + ISynchronizedViewFilter filter; - public event NotifyCollectionChangedEventHandler? RoutingCollectionChanged; + public event Action>? ViewChanged; public event Action? CollectionStateChanged; public object SyncRoot { get; } - public View(IObservableCollection source, Func selector, bool reverse) + public View(IObservableCollection source, Func selector) { this.source = source; this.selector = selector; - this.reverse = reverse; - this.filter = SynchronizedViewFilter.Null; + this.filter = SynchronizedViewFilter.Null; this.SyncRoot = new object(); lock (source.SyncRoot) { this.ringBuffer = new RingBuffer<(T, TView)>(source.Select(x => (x, selector(x)))); + this.filteredCount = ringBuffer.Count; this.source.CollectionChanged += SourceCollectionChanged; } } public int Count + { + get + { + lock (SyncRoot) + { + return filteredCount; + } + } + } + + public int UnfilteredCount { get { @@ -59,41 +71,44 @@ namespace ObservableCollections } } - public void AttachFilter(ISynchronizedViewFilter filter, bool invokeAddEventForCurrentElements = false) + public void AttachFilter(ISynchronizedViewFilter filter) { + if (filter.IsNullFilter()) + { + ResetFilter(); + return; + } + lock (SyncRoot) { this.filter = filter; + this.filteredCount = 0; for (var i = 0; i < ringBuffer.Count; i++) { var (value, view) = ringBuffer[i]; - if (invokeAddEventForCurrentElements) + if (filter.IsMatch(value)) { - filter.InvokeOnAdd(value, view, i); - } - else - { - filter.InvokeOnAttach(value, view); + filteredCount++; } } } } - public void ResetFilter(Action? resetAction) + public void ResetFilter() { lock (SyncRoot) { - this.filter = SynchronizedViewFilter.Null; - if (resetAction != null) - { - foreach (var (item, view) in ringBuffer) - { - resetAction(item, view); - } - } + this.filter = SynchronizedViewFilter.Null; + this.filteredCount = ringBuffer.Count; + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyViewChangedAction.FilterReset)); } } + public ISynchronizedViewList ToViewList() + { + return new SynchronizedViewList(this); + } + public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged() { lock (SyncRoot) @@ -110,25 +125,31 @@ namespace ObservableCollections } } - public IEnumerator<(T, TView)> GetEnumerator() + public IEnumerator GetEnumerator() { lock (SyncRoot) { - if (!reverse) + foreach (var item in ringBuffer) + { + if (filter.IsMatch(item.Item1)) + { + yield return item.Item2; + } + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IEnumerable<(T Value, TView View)> Filtered + { + get + { + lock (SyncRoot) { foreach (var item in ringBuffer) { - if (filter.IsMatch(item.Item1, item.Item2)) - { - yield return item; - } - } - } - else - { - foreach (var item in ringBuffer.AsEnumerable().Reverse()) - { - if (filter.IsMatch(item.Item1, item.Item2)) + if (filter.IsMatch(item.Item1)) { yield return item; } @@ -137,7 +158,19 @@ namespace ObservableCollections } } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerable<(T Value, TView View)> Unfiltered + { + get + { + lock (SyncRoot) + { + foreach (var item in ringBuffer) + { + yield return item; + } + } + } + } public void Dispose() { @@ -162,7 +195,7 @@ namespace ObservableCollections { var v = (e.NewItem, selector(e.NewItem)); ringBuffer.AddFirst(v); - filter.InvokeOnAdd(v, 0); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); } else { @@ -170,7 +203,7 @@ namespace ObservableCollections { var v = (item, selector(item)); ringBuffer.AddFirst(v); - filter.InvokeOnAdd(v, 0); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); } } } @@ -181,7 +214,7 @@ namespace ObservableCollections { var v = (e.NewItem, selector(e.NewItem)); ringBuffer.AddLast(v); - filter.InvokeOnAdd(v, ringBuffer.Count - 1); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, ringBuffer.Count - 1); } else { @@ -189,7 +222,7 @@ namespace ObservableCollections { var v = (item, selector(item)); ringBuffer.AddLast(v); - filter.InvokeOnAdd(v, ringBuffer.Count - 1); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, ringBuffer.Count - 1); } } } @@ -202,14 +235,14 @@ namespace ObservableCollections if (e.IsSingleItem) { var v = ringBuffer.RemoveFirst(); - filter.InvokeOnRemove(v, 0); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v, 0); } else { for (int i = 0; i < e.OldItems.Length; i++) { var v = ringBuffer.RemoveFirst(); - filter.InvokeOnRemove(v, 0); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v, 0); } } } @@ -220,7 +253,7 @@ namespace ObservableCollections { var index = ringBuffer.Count - 1; var v = ringBuffer.RemoveLast(); - filter.InvokeOnRemove(v, index); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index); } else { @@ -228,14 +261,14 @@ namespace ObservableCollections { var index = ringBuffer.Count - 1; var v = ringBuffer.RemoveLast(); - filter.InvokeOnRemove(v, index); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index); } } } break; case NotifyCollectionChangedAction.Reset: ringBuffer.Clear(); - filter.InvokeOnReset(); + this.InvokeOnReset(ref filteredCount, ViewChanged); break; case NotifyCollectionChangedAction.Replace: // range is not supported @@ -243,7 +276,7 @@ namespace ObservableCollections var ov = ringBuffer[e.OldStartingIndex]; var v = (e.NewItem, selector(e.NewItem)); ringBuffer[e.NewStartingIndex] = v; - filter.InvokeOnReplace(v, ov, e.NewStartingIndex); + this.InvokeOnReplace(ref filteredCount, ViewChanged, v, ov, e.NewStartingIndex); break; } case NotifyCollectionChangedAction.Move: @@ -251,7 +284,6 @@ namespace ObservableCollections break; } - RoutingCollectionChanged?.Invoke(e); CollectionStateChanged?.Invoke(e.Action); } } diff --git a/src/ObservableCollections/ObservableStack.Views.cs b/src/ObservableCollections/ObservableStack.Views.cs index d0ced1a..2bfa68a 100644 --- a/src/ObservableCollections/ObservableStack.Views.cs +++ b/src/ObservableCollections/ObservableStack.Views.cs @@ -9,45 +9,56 @@ namespace ObservableCollections { public sealed partial class ObservableStack : IReadOnlyCollection, IObservableCollection { - public ISynchronizedView CreateView(Func transform, bool reverse = false) + public ISynchronizedView CreateView(Func transform) { - return new View(this, transform, reverse); + return new View(this, transform); } class View : ISynchronizedView { readonly ObservableStack source; readonly Func selector; - readonly bool reverse; protected readonly Stack<(T, TView)> stack; + int filteredCount; - ISynchronizedViewFilter filter; + ISynchronizedViewFilter filter; - public event NotifyCollectionChangedEventHandler? RoutingCollectionChanged; + public event Action>? ViewChanged; public event Action? CollectionStateChanged; public object SyncRoot { get; } - public ISynchronizedViewFilter CurrentFilter + public ISynchronizedViewFilter Filter { get { lock (SyncRoot) return filter; } } - public View(ObservableStack source, Func selector, bool reverse) + public View(ObservableStack source, Func selector) { this.source = source; this.selector = selector; - this.reverse = reverse; - this.filter = SynchronizedViewFilter.Null; + this.filter = SynchronizedViewFilter.Null; this.SyncRoot = new object(); lock (source.SyncRoot) { this.stack = new Stack<(T, TView)>(source.stack.Select(x => (x, selector(x)))); + this.filteredCount = stack.Count; this.source.CollectionChanged += SourceCollectionChanged; } } public int Count + { + get + { + lock (SyncRoot) + { + return filteredCount; + } + } + } + + public int UnfilteredCount { get { @@ -58,40 +69,44 @@ namespace ObservableCollections } } - public void AttachFilter(ISynchronizedViewFilter filter, bool invokeAddEventForCurrentElements = false) + public void AttachFilter(ISynchronizedViewFilter filter) { + if (filter.IsNullFilter()) + { + ResetFilter(); + return; + } + lock (SyncRoot) { this.filter = filter; + this.filteredCount = 0; foreach (var (value, view) in stack) { - if (invokeAddEventForCurrentElements) + if (filter.IsMatch(value)) { - filter.InvokeOnAdd(value, view, 0); - } - else - { - filter.InvokeOnAttach(value, view); + filteredCount++; } } + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyViewChangedAction.FilterReset)); } } - public void ResetFilter(Action? resetAction) + public void ResetFilter() { lock (SyncRoot) { - this.filter = SynchronizedViewFilter.Null; - if (resetAction != null) - { - foreach (var (item, view) in stack) - { - resetAction(item, view); - } - } + this.filter = SynchronizedViewFilter.Null; + this.filteredCount = stack.Count; + ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs(NotifyViewChangedAction.FilterReset)); } } + public ISynchronizedViewList ToViewList() + { + return new SynchronizedViewList(this); + } + public INotifyCollectionChangedSynchronizedView ToNotifyCollectionChanged() { lock (SyncRoot) @@ -108,25 +123,31 @@ namespace ObservableCollections } } - public IEnumerator<(T, TView)> GetEnumerator() + public IEnumerator GetEnumerator() { lock (SyncRoot) { - if (!reverse) + foreach (var item in stack) + { + if (filter.IsMatch(item.Item1)) + { + yield return item.Item2; + } + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IEnumerable<(T Value, TView View)> Filtered + { + get + { + lock (SyncRoot) { foreach (var item in stack) { - if (filter.IsMatch(item.Item1, item.Item2)) - { - yield return item; - } - } - } - else - { - foreach (var item in stack.AsEnumerable().Reverse()) - { - if (filter.IsMatch(item.Item1, item.Item2)) + if (filter.IsMatch(item.Item1)) { yield return item; } @@ -135,7 +156,19 @@ namespace ObservableCollections } } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerable<(T Value, TView View)> Unfiltered + { + get + { + lock (SyncRoot) + { + foreach (var item in stack) + { + yield return item; + } + } + } + } public void Dispose() { @@ -154,7 +187,7 @@ namespace ObservableCollections { var v = (e.NewItem, selector(e.NewItem)); stack.Push(v); - filter.InvokeOnAdd(v, 0); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); } else { @@ -162,7 +195,7 @@ namespace ObservableCollections { var v = (item, selector(item)); stack.Push(v); - filter.InvokeOnAdd(v, 0); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); } } break; @@ -171,7 +204,7 @@ namespace ObservableCollections if (e.IsSingleItem) { var v = stack.Pop(); - filter.InvokeOnRemove(v.Item1, v.Item2, 0); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); } else { @@ -179,13 +212,13 @@ namespace ObservableCollections for (int i = 0; i < len; i++) { var v = stack.Pop(); - filter.InvokeOnRemove(v.Item1, v.Item2, 0); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); } } break; case NotifyCollectionChangedAction.Reset: stack.Clear(); - filter.InvokeOnReset(); + this.InvokeOnReset(ref filteredCount, ViewChanged); break; case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Move: @@ -193,7 +226,6 @@ namespace ObservableCollections break; } - RoutingCollectionChanged?.Invoke(e); CollectionStateChanged?.Invoke(e.Action); } }