diff --git a/src/ObservableCollections/IObservableCollection.cs b/src/ObservableCollections/IObservableCollection.cs index 7ce908e..ac650df 100644 --- a/src/ObservableCollections/IObservableCollection.cs +++ b/src/ObservableCollections/IObservableCollection.cs @@ -13,7 +13,7 @@ namespace ObservableCollections { event NotifyCollectionChangedEventHandler? CollectionChanged; object SyncRoot { get; } - ISynchronizedView CreateView(Func transform, bool reverse = false); + ISynchronizedView CreateView(Func transform); } public interface IReadOnlyObservableList : @@ -36,7 +36,9 @@ namespace ObservableCollections { object SyncRoot { get; } ISynchronizedViewFilter Filter { get; } + IEnumerable<(T Value, TView View)> Filtered { get; } IEnumerable<(T Value, TView View)> Unfiltered { get; } + int UnfilteredCount { get; } event Action>? ViewChanged; event Action? CollectionStateChanged; diff --git a/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs b/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs index 0a3ed01..3114d96 100644 --- a/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs +++ b/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs @@ -15,9 +15,11 @@ namespace ObservableCollections.Internal public SynchronizedViewList(ISynchronizedView parent) { this.parent = parent; - this.listView = parent.Select(x => x.View).ToList(); // need lock - // TODO:add - parent.ViewChanged += Parent_ViewChanged; + lock (parent.SyncRoot) + { + this.listView = parent.ToList(); + parent.ViewChanged += Parent_ViewChanged; + } } private void Parent_ViewChanged(SynchronizedViewChangedEventArgs e) diff --git a/src/ObservableCollections/ObservableList.Views.cs b/src/ObservableCollections/ObservableList.Views.cs index 03a4ccd..df02e15 100644 --- a/src/ObservableCollections/ObservableList.Views.cs +++ b/src/ObservableCollections/ObservableList.Views.cs @@ -9,9 +9,9 @@ namespace ObservableCollections { public sealed partial class ObservableList : IList, IReadOnlyObservableList { - public ISynchronizedView CreateView(Func transform, bool reverse = false) + public ISynchronizedView CreateView(Func transform) { - return new View(this, transform, reverse); + return new View(this, transform); } internal sealed class View : ISynchronizedView @@ -26,7 +26,6 @@ namespace ObservableCollections readonly ObservableList source; readonly Func selector; - readonly bool reverse; readonly List<(T, TView)> list; int filteredCount; @@ -37,11 +36,10 @@ namespace ObservableCollections public object SyncRoot { get; } - public View(ObservableList source, Func selector, bool reverse) + public View(ObservableList source, Func selector) { this.source = source; this.selector = selector; - this.reverse = reverse; this.filter = SynchronizedViewFilter.Null; this.SyncRoot = new object(); lock (source.SyncRoot) @@ -63,6 +61,17 @@ namespace ObservableCollections } } + public int UnfilteredCount + { + get + { + lock (SyncRoot) + { + return list.Count; + } + } + } + public void AttachFilter(ISynchronizedViewFilter filter) { if (filter.IsNullFilter()) @@ -114,25 +123,31 @@ namespace ObservableCollections } } - public IEnumerator<(T, TView)> GetEnumerator() + public IEnumerator GetEnumerator() { lock (SyncRoot) { - if (!reverse) + foreach (var item in list) + { + 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 list) { - if (filter.IsMatch(item.Item1, item.Item2)) - { - yield return item; - } - } - } - else - { - foreach (var item in list.AsEnumerable().Reverse()) - { - if (filter.IsMatch(item.Item1, item.Item2)) + if (filter.IsMatch(item.Item1)) { yield return item; } @@ -141,7 +156,19 @@ namespace ObservableCollections } } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerable<(T Value, TView View)> Unfiltered + { + get + { + lock (SyncRoot) + { + foreach (var item in list) + { + yield return item; + } + } + } + } public void Dispose() { @@ -171,11 +198,7 @@ namespace ObservableCollections { var v = (item, selector(item)); list.Add(v); - if (filter.IsMatch(v)) - { - filteredCount++; - } - filter.InvokeOnAdd(v, i++); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++); } } } @@ -186,20 +209,17 @@ namespace ObservableCollections { var v = (e.NewItem, selector(e.NewItem)); list.Insert(e.NewStartingIndex, v); - filter.InvokeOnAdd(v, e.NewStartingIndex); + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex); } else { - // inefficient copy, need refactoring - var newArray = new (T, TView)[e.NewItems.Length]; var span = e.NewItems; for (var i = 0; i < span.Length; i++) { var v = (span[i], selector(span[i])); - newArray[i] = v; - filter.InvokeOnAdd(v, e.NewStartingIndex + i); + list.Insert(e.NewStartingIndex + i, v); // should we use InsertRange? + this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex + i); } - list.InsertRange(e.NewStartingIndex, newArray); } } break; @@ -208,7 +228,7 @@ namespace ObservableCollections { var v = list[e.OldStartingIndex]; list.RemoveAt(e.OldStartingIndex); - filter.InvokeOnRemove(v, e.OldStartingIndex); + this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex); } else { @@ -216,10 +236,9 @@ namespace ObservableCollections for (var i = e.OldStartingIndex; i < len; i++) { var v = list[i]; - filter.InvokeOnRemove(v, e.OldStartingIndex + i); + list.RemoveAt(e.OldStartingIndex + i); // should we use RemoveRange? + this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex + i); } - - list.RemoveRange(e.OldStartingIndex, e.OldItems.Length); } break; case NotifyCollectionChangedAction.Replace: @@ -228,7 +247,7 @@ namespace ObservableCollections var v = (e.NewItem, selector(e.NewItem)); var ov = (e.OldItem, list[e.OldStartingIndex].Item2); list[e.NewStartingIndex] = v; - filter.InvokeOnReplace(v, ov, e.NewStartingIndex); + this.InvokeOnReplace(ref filteredCount, ViewChanged, v, ov, e.NewStartingIndex); break; } case NotifyCollectionChangedAction.Move: @@ -237,18 +256,17 @@ namespace ObservableCollections list.RemoveAt(e.OldStartingIndex); list.Insert(e.NewStartingIndex, removeItem); - filter.InvokeOnMove(removeItem, e.NewStartingIndex, e.OldStartingIndex); + this.InvokeOnMove(ref filteredCount, ViewChanged, removeItem, e.NewStartingIndex, e.OldStartingIndex); } break; case NotifyCollectionChangedAction.Reset: list.Clear(); - filter.InvokeOnReset(); + this.InvokeOnReset(ref filteredCount, ViewChanged); break; default: break; } - RoutingCollectionChanged?.Invoke(e); CollectionStateChanged?.Invoke(e.Action); } }