Support for firing add event for initial elements

This commit is contained in:
hadashiA 2024-02-01 19:06:45 +09:00
parent bcf250c631
commit 7b75bf2940
30 changed files with 504 additions and 156 deletions

View File

@ -1,24 +1,66 @@
using ObservableCollections; using System;
using System; using System.Linq;
using System.Collections.Specialized; using ObservableCollections;
var models = new ObservableList<int>(Enumerable.Range(0, 10));
var viewModels = models.CreateView(x => new ViewModel
// Basic sample, use like ObservableCollection<T>.
// CollectionChanged observes all collection modification
var list = new ObservableList<int>();
var view = list.CreateView(x => x.ToString() + "$");
list.Add(10);
list.Add(20);
list.AddRange(new[] { 30, 40, 50 });
list[1] = 60;
list.RemoveAt(3);
foreach (var (_, v) in view)
{ {
// 10$, 60$, 30$, 50$ Id = x,
Console.WriteLine(v); Value = "@" + x
});
viewModels.AttachFilter(new HogeFilter(), true);
models.Add(100);
foreach (var (x, xs) in viewModels)
{
System.Console.WriteLine(xs.Value);
} }
// Dispose view is unsubscribe collection changed event. class ViewModel
view.Dispose(); {
public int Id { get; set; }
public string Value { get; set; }
}
class HogeFilter : ISynchronizedViewFilter<int, ViewModel>
{
public bool IsMatch(int value, ViewModel view)
{
return value % 2 == 0;
}
public void WhenTrue(int value, ViewModel view)
{
view.Value = $"@{value} (even)";
}
public void WhenFalse(int value, ViewModel view)
{
view.Value = $"@{value} (odd)";
}
public void OnCollectionChanged(
ChangedKind changedKind,
int value,
ViewModel view,
in NotifyCollectionChangedEventArgs<int> eventArgs)
{
switch (changedKind)
{
case ChangedKind.Add:
view.Value += " Add";
break;
case ChangedKind.Remove:
view.Value += " Remove";
break;
case ChangedKind.Move:
view.Value += $" Move {eventArgs.OldStartingIndex} {eventArgs.NewStartingIndex}";
break;
default:
throw new ArgumentOutOfRangeException(nameof(changedKind), changedKind, null);
}
}
}

View File

@ -28,7 +28,7 @@ namespace ObservableCollections
event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged; event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
event Action<NotifyCollectionChangedAction> CollectionStateChanged; event Action<NotifyCollectionChangedAction> CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T, TView> filter); void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForInitialElements = false);
void ResetFilter(Action<T, TView> resetAction); void ResetFilter(Action<T, TView> resetAction);
INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged(); INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged();
} }

View File

@ -49,7 +49,7 @@ namespace ObservableCollections.Internal
} }
else else
{ {
var array = ArrayPool<T>.Shared.Rent(count); var array = ArrayPool<T>.Shared.Rent(16);
var i = 0; var i = 0;
foreach (var item in source) foreach (var item in source)
@ -75,8 +75,8 @@ namespace ObservableCollections.Internal
if (array.Length == index) if (array.Length == index)
{ {
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>()); ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
array = ArrayPool<T>.Shared.Rent(index * 2);
} }
array = ArrayPool<T>.Shared.Rent(index * 2);
} }
public void Dispose() public void Dispose()

View File

@ -38,14 +38,22 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in list) for (var i = 0; i < list.Count; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -133,14 +141,22 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in array) for (var i = 0; i < array.Length; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = array[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -178,7 +194,6 @@ namespace ObservableCollections.Internal
public void Dispose() public void Dispose()
{ {
} }
public void Sort(IComparer<T> comparer) public void Sort(IComparer<T> comparer)

View File

@ -55,7 +55,7 @@ namespace ObservableCollections.Internal
remove { parent.RoutingCollectionChanged -= value; } remove { parent.RoutingCollectionChanged -= value; }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) => parent.AttachFilter(filter); public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false) => parent.AttachFilter(filter, invokeAddEventForCurrentElements);
public void ResetFilter(Action<T, TView> resetAction) => parent.ResetFilter(resetAction); public void ResetFilter(Action<T, TView> resetAction) => parent.ResetFilter(resetAction);
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged() => this; public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged() => this;
public void Dispose() public void Dispose()

View File

@ -51,14 +51,21 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (_, (value, view)) in dict) foreach (var (_, (value, view)) in dict)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -55,14 +55,21 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (_, (value, view)) in dict) foreach (var (_, (value, view)) in dict)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -55,14 +55,23 @@ namespace ObservableCollections
this.source.CollectionChanged -= SourceCollectionChanged; this.source.CollectionChanged -= SourceCollectionChanged;
} }
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter) public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var v in dict) foreach (var v in dict)
{ {
filter.InvokeOnAttach(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2); var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
var view = v.Value.Item2;
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -118,44 +127,44 @@ namespace ObservableCollections
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
{ {
var v = selector(e.NewItem); var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v)); dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e); filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
} }
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
{
if (dict.Remove(e.OldItem.Key, out var v))
{ {
if (dict.Remove(e.OldItem.Key, out var v)) filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
}
} }
}
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
{
if (dict.Remove(e.OldItem.Key, out var oldView))
{ {
if (dict.Remove(e.OldItem.Key, out var oldView)) filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
}
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
} }
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
{
if (!filter.IsNullFilter())
{ {
if (!filter.IsNullFilter()) foreach (var item in dict)
{ {
foreach (var item in dict) filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
}
} }
dict.Clear();
} }
dict.Clear();
}
break; break;
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation. case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
default: default:
@ -168,4 +177,4 @@ namespace ObservableCollections
} }
} }
} }
} }

View File

@ -51,14 +51,21 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (_, (value, view)) in dict) foreach (var (_, (value, view)) in dict)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd((value, view), NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in list) for (var i = 0; i < list.Count; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
var eventArgs = NotifyCollectionChangedEventArgs<T>.Add(value, i);
filter.InvokeOnAdd(value, view, eventArgs);
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -193,24 +202,24 @@ namespace ObservableCollections
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
// ObservableList does not support replace range // ObservableList does not support replace range
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
var oldItem = list[e.NewStartingIndex]; var oldItem = list[e.NewStartingIndex];
list[e.NewStartingIndex] = v; list[e.NewStartingIndex] = v;
filter.InvokeOnRemove(oldItem, e); filter.InvokeOnRemove(oldItem, e);
filter.InvokeOnAdd(v, e); filter.InvokeOnAdd(v, e);
break; break;
} }
case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Move:
{ {
var removeItem = list[e.OldStartingIndex]; var removeItem = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex); list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem); list.Insert(e.NewStartingIndex, removeItem);
filter.InvokeOnMove(removeItem, e); filter.InvokeOnMove(removeItem, e);
} }
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter()) if (!filter.IsNullFilter())

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
var i = 0;
foreach (var (value, view) in queue) foreach (var (value, view) in queue)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
} }
} }
} }

View File

@ -54,14 +54,22 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in ringBuffer) for (var i = 0; i < ringBuffer.Count; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = ringBuffer[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
var i = 0;
foreach (var (value, view) in stack) foreach (var (value, view) in stack)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
} }
} }
} }

View File

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
#if (NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6) && !UNITY_2021_1_OR_NEWER #if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6
namespace System.Diagnostics.CodeAnalysis namespace System.Diagnostics.CodeAnalysis
{ {

View File

@ -28,7 +28,7 @@ namespace ObservableCollections
event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged; event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
event Action<NotifyCollectionChangedAction>? CollectionStateChanged; event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T, TView> filter); void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForInitialElements = false);
void ResetFilter(Action<T, TView>? resetAction); void ResetFilter(Action<T, TView>? resetAction);
INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged(); INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged();
} }

View File

@ -38,14 +38,22 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in list) for (var i = 0; i < list.Count; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -133,14 +141,22 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in array) for (var i = 0; i < array.Length; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = array[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -178,7 +194,6 @@ namespace ObservableCollections.Internal
public void Dispose() public void Dispose()
{ {
} }
public void Sort(IComparer<T> comparer) public void Sort(IComparer<T> comparer)

View File

@ -55,7 +55,7 @@ namespace ObservableCollections.Internal
remove { parent.RoutingCollectionChanged -= value; } remove { parent.RoutingCollectionChanged -= value; }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) => parent.AttachFilter(filter); public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false) => parent.AttachFilter(filter, invokeAddEventForCurrentElements);
public void ResetFilter(Action<T, TView>? resetAction) => parent.ResetFilter(resetAction); public void ResetFilter(Action<T, TView>? resetAction) => parent.ResetFilter(resetAction);
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged() => this; public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged() => this;
public void Dispose() public void Dispose()

View File

@ -51,14 +51,21 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (_, (value, view)) in dict) foreach (var (_, (value, view)) in dict)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -55,14 +55,21 @@ namespace ObservableCollections.Internal
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (_, (value, view)) in dict) foreach (var (_, (value, view)) in dict)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -55,14 +55,23 @@ namespace ObservableCollections
this.source.CollectionChanged -= SourceCollectionChanged; this.source.CollectionChanged -= SourceCollectionChanged;
} }
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter) public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var v in dict) foreach (var v in dict)
{ {
filter.InvokeOnAttach(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2); var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
var view = v.Value.Item2;
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -118,44 +127,44 @@ namespace ObservableCollections
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
{ {
var v = selector(e.NewItem); var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v)); dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e); filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
} }
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
{
if (dict.Remove(e.OldItem.Key, out var v))
{ {
if (dict.Remove(e.OldItem.Key, out var v)) filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
}
} }
}
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
{
if (dict.Remove(e.OldItem.Key, out var oldView))
{ {
if (dict.Remove(e.OldItem.Key, out var oldView)) filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2), e);
}
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
} }
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
}
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
{
if (!filter.IsNullFilter())
{ {
if (!filter.IsNullFilter()) foreach (var item in dict)
{ {
foreach (var item in dict) filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
}
} }
dict.Clear();
} }
dict.Clear();
}
break; break;
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation. case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
default: default:
@ -168,4 +177,4 @@ namespace ObservableCollections
} }
} }
} }
} }

View File

@ -51,14 +51,21 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (_, (value, view)) in dict) foreach (var (_, (value, view)) in dict)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd((value, view), NotifyCollectionChangedEventArgs<T>.Add(value, -1));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in list) for (var i = 0; i < list.Count; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = list[i];
if (invokeAddEventForCurrentElements)
{
var eventArgs = NotifyCollectionChangedEventArgs<T>.Add(value, i);
filter.InvokeOnAdd(value, view, eventArgs);
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }
@ -193,24 +202,24 @@ namespace ObservableCollections
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
// ObservableList does not support replace range // ObservableList does not support replace range
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
var oldItem = list[e.NewStartingIndex]; var oldItem = list[e.NewStartingIndex];
list[e.NewStartingIndex] = v; list[e.NewStartingIndex] = v;
filter.InvokeOnRemove(oldItem, e); filter.InvokeOnRemove(oldItem, e);
filter.InvokeOnAdd(v, e); filter.InvokeOnAdd(v, e);
break; break;
} }
case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Move:
{ {
var removeItem = list[e.OldStartingIndex]; var removeItem = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex); list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem); list.Insert(e.NewStartingIndex, removeItem);
filter.InvokeOnMove(removeItem, e); filter.InvokeOnMove(removeItem, e);
} }
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter()) if (!filter.IsNullFilter())

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
var i = 0;
foreach (var (value, view) in queue) foreach (var (value, view) in queue)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
} }
} }
} }

View File

@ -54,14 +54,22 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
foreach (var (value, view) in ringBuffer) for (var i = 0; i < ringBuffer.Count; i++)
{ {
filter.InvokeOnAttach(value, view); var (value, view) = ringBuffer[i];
if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
} }
} }
} }

View File

@ -53,14 +53,23 @@ namespace ObservableCollections
} }
} }
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
this.filter = filter; this.filter = filter;
var i = 0;
foreach (var (value, view) in stack) foreach (var (value, view) in stack)
{ {
filter.InvokeOnAttach(value, view); if (invokeAddEventForCurrentElements)
{
filter.InvokeOnAdd(value, view, NotifyCollectionChangedEventArgs<T>.Add(value, i));
}
else
{
filter.InvokeOnAttach(value, view);
}
i++;
} }
} }
} }

View File

@ -154,5 +154,36 @@ namespace ObservableCollections.Tests
.OrderBy(x => x.Value) .OrderBy(x => x.Value)
.Should().Equal((ChangedKind.Remove, -1090), (ChangedKind.Remove, -100), (ChangedKind.Remove, -53), (ChangedKind.Remove, -40), (ChangedKind.Remove, -34)); .Should().Equal((ChangedKind.Remove, -1090), (ChangedKind.Remove, -100), (ChangedKind.Remove, -53), (ChangedKind.Remove, -40), (ChangedKind.Remove, -34));
} }
[Fact]
public void FilterAndInvokeAddEvent()
{
var dict = new ObservableDictionary<int, int>();
var view1 = dict.CreateView(x => new ViewContainer<int>(x.Value));
var filter1 = new TestFilter2<int>((x, v) => x.Value % 2 == 0);
dict.Add(10, -12); // 0
dict.Add(50, -53); // 1
dict.Add(30, -34); // 2
dict.Add(20, -25); // 3
dict.Add(40, -40); // 4
view1.AttachFilter(filter1, true);
filter1.CalledOnCollectionChanged.Count.Should().Be(5);
filter1.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[0].value.Key.Should().Be(10);
filter1.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[1].value.Key.Should().Be(50);
filter1.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[2].value.Key.Should().Be(30);
filter1.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[3].value.Key.Should().Be(20);
filter1.CalledOnCollectionChanged[4].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[4].value.Key.Should().Be(40);
filter1.CalledWhenTrue.Count.Should().Be(3);
filter1.CalledWhenFalse.Count.Should().Be(2);
}
} }
} }

View File

@ -76,5 +76,35 @@ namespace ObservableCollections.Tests
set.RemoveRange(new[] { 50, 30 }); set.RemoveRange(new[] { 50, 30 });
filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 10), (ChangedKind.Remove, 50), (ChangedKind.Remove, 30)); filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 10), (ChangedKind.Remove, 50), (ChangedKind.Remove, 30));
} }
[Fact]
public void FilterAndInvokeAddEvent()
{
var set = new ObservableHashSet<int>();
var view = set.CreateView(x => new ViewContainer<int>(x));
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
set.Add(10);
set.Add(50);
set.Add(30);
set.Add(20);
set.Add(40);
view.AttachFilter(filter, true);
filter.CalledOnCollectionChanged.Count.Should().Be(5);
filter.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[0].value.Should().Be(10);
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[1].value.Should().Be(50);
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[2].value.Should().Be(30);
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[3].value.Should().Be(20);
filter.CalledOnCollectionChanged[4].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[4].value.Should().Be(40);
filter.CalledWhenTrue.Count.Should().Be(1);
filter.CalledWhenFalse.Count.Should().Be(4);
}
} }
} }

View File

@ -218,5 +218,28 @@ namespace ObservableCollections.Tests
filter2.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 21), (ChangedKind.Remove, 30), (ChangedKind.Remove, 44), (ChangedKind.Remove, 45), (ChangedKind.Remove, 66), (ChangedKind.Remove, 90), (ChangedKind.Remove, 100), (ChangedKind.Remove, 101), (ChangedKind.Remove, 9999)); filter2.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 21), (ChangedKind.Remove, 30), (ChangedKind.Remove, 44), (ChangedKind.Remove, 45), (ChangedKind.Remove, 66), (ChangedKind.Remove, 90), (ChangedKind.Remove, 100), (ChangedKind.Remove, 101), (ChangedKind.Remove, 9999));
filter3.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 21), (ChangedKind.Remove, 30), (ChangedKind.Remove, 44), (ChangedKind.Remove, 45), (ChangedKind.Remove, 66), (ChangedKind.Remove, 90), (ChangedKind.Remove, 100), (ChangedKind.Remove, 101), (ChangedKind.Remove, 9999)); filter3.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 21), (ChangedKind.Remove, 30), (ChangedKind.Remove, 44), (ChangedKind.Remove, 45), (ChangedKind.Remove, 66), (ChangedKind.Remove, 90), (ChangedKind.Remove, 100), (ChangedKind.Remove, 101), (ChangedKind.Remove, 9999));
} }
[Fact]
public void FilterAndInvokeAddEvent()
{
var list = new ObservableList<int>();
var view1 = list.CreateView(x => new ViewContainer<int>(x));
list.AddRange(new[] { 10, 21, 30, 44 });
var filter1 = new TestFilter<int>((x, v) => x % 2 == 0);
view1.AttachFilter(filter1, true);
filter1.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[0].value.Should().Be(10);
filter1.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[1].value.Should().Be(21);
filter1.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[2].value.Should().Be(30);
filter1.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter1.CalledOnCollectionChanged[3].value.Should().Be(44);
filter1.CalledWhenTrue.Count.Should().Be(3);
filter1.CalledWhenFalse.Count.Should().Be(1);
}
} }
} }

View File

@ -80,5 +80,36 @@ namespace ObservableCollections.Tests
queue.DequeueRange(2); queue.DequeueRange(2);
filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 10), (ChangedKind.Remove, 50), (ChangedKind.Remove, 30)); filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 10), (ChangedKind.Remove, 50), (ChangedKind.Remove, 30));
} }
[Fact]
public void FilterAndInvokeAddEvent()
{
var queue = new ObservableQueue<int>();
var view = queue.CreateView(x => new ViewContainer<int>(x));
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
queue.Enqueue(10);
queue.Enqueue(50);
queue.Enqueue(30);
queue.Enqueue(20);
queue.Enqueue(40);
view.AttachFilter(filter, true);
filter.CalledOnCollectionChanged.Count.Should().Be(5);
filter.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[0].value.Should().Be(10);
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[1].value.Should().Be(50);
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[2].value.Should().Be(30);
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[3].value.Should().Be(20);
filter.CalledOnCollectionChanged[4].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[4].value.Should().Be(40);
filter.CalledWhenTrue.Count.Should().Be(1);
filter.CalledWhenFalse.Count.Should().Be(4);
}
} }
} }

View File

@ -80,5 +80,36 @@ namespace ObservableCollections.Tests
stack.PopRange(2); stack.PopRange(2);
filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 98), (ChangedKind.Remove, 33), (ChangedKind.Remove, 40)); filter.CalledOnCollectionChanged.Select(x => (x.changedKind, x.value)).Should().Equal((ChangedKind.Remove, 98), (ChangedKind.Remove, 33), (ChangedKind.Remove, 40));
} }
[Fact]
public void FilterAndInvokeAddEvent()
{
var stack = new ObservableStack<int>();
var view = stack.CreateView(x => new ViewContainer<int>(x));
var filter = new TestFilter<int>((x, v) => x % 3 == 0);
stack.Push(10);
stack.Push(50);
stack.Push(30);
stack.Push(20);
stack.Push(40);
view.AttachFilter(filter, true);
filter.CalledOnCollectionChanged.Count.Should().Be(5);
filter.CalledOnCollectionChanged[4].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[4].value.Should().Be(10);
filter.CalledOnCollectionChanged[3].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[3].value.Should().Be(50);
filter.CalledOnCollectionChanged[2].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[2].value.Should().Be(30);
filter.CalledOnCollectionChanged[1].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[1].value.Should().Be(20);
filter.CalledOnCollectionChanged[0].changedKind.Should().Be(ChangedKind.Add);
filter.CalledOnCollectionChanged[0].value.Should().Be(40);
filter.CalledWhenTrue.Count.Should().Be(1);
filter.CalledWhenFalse.Count.Should().Be(4);
}
} }
} }