Fix (NotifyCollecitonChanged)SynchronizedViewList does not sync correcty when view is attached filter

This commit is contained in:
neuecc 2024-10-02 18:28:05 +09:00
parent b73a30c366
commit 0ce9b3a10e
12 changed files with 212 additions and 153 deletions

View File

@ -466,6 +466,11 @@ This is the interface for View:
```csharp ```csharp
public delegate void NotifyViewChangedEventHandler<T, TView>(in SynchronizedViewChangedEventArgs<T, TView> e); public delegate void NotifyViewChangedEventHandler<T, TView>(in SynchronizedViewChangedEventArgs<T, TView> e);
public enum RejectedViewChangedAction
{
Add, Remove, Move
}
public interface ISynchronizedView<T, TView> : IReadOnlyCollection<TView>, IDisposable public interface ISynchronizedView<T, TView> : IReadOnlyCollection<TView>, IDisposable
{ {
object SyncRoot { get; } object SyncRoot { get; }
@ -475,6 +480,7 @@ public interface ISynchronizedView<T, TView> : IReadOnlyCollection<TView>, IDisp
int UnfilteredCount { get; } int UnfilteredCount { get; }
event NotifyViewChangedEventHandler<T, TView>? ViewChanged; event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged; // int index, int oldIndex(when RejectedViewChangedAction is Move)
event Action<NotifyCollectionChangedAction>? CollectionStateChanged; event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T> filter); void AttachFilter(ISynchronizedViewFilter<T> filter);

View File

@ -8,68 +8,80 @@ using System.Collections.Generic;
var buffer = new ObservableList<int>(5);
// Queue <-> List Synchronization var view = buffer.CreateView(value => value);
var queue = new ObservableQueue<int>(); view.AttachFilter(value => value % 2 == 1); // when filtered, mismatch...!
queue.Enqueue(1); //{
queue.Enqueue(10); // INotifyCollectionChangedSynchronizedViewList created from ISynchronizedView with a filter.
queue.Enqueue(100); var collection = view.ToNotifyCollectionChanged();
queue.Enqueue(1000);
queue.Enqueue(10000);
using var view = queue.CreateView(x => x.ToString() + "$"); // Not disposed here.
//}
using var viewList = view.ToViewList(); buffer.Insert(0, 1);
buffer.Insert(0, 1);
buffer.Insert(0, 2);
buffer.Insert(0, 3);
buffer.Insert(0, 5);
buffer.RemoveAt(buffer.Count - 1);
Console.WriteLine(viewList[2]); // 100$ buffer.Insert(0, 8);
buffer.Insert(0, 13);
view.ViewChanged += View_ViewChanged; buffer.Move(1, 5);
void View_ViewChanged(in SynchronizedViewChangedEventArgs<int, string> eventArgs) foreach (var item in view)
{ {
if (eventArgs.Action == NotifyCollectionChangedAction.Add) Console.WriteLine(item);
{ }
// eventArgs.OldItem.View. Console.WriteLine("---");
}
throw new NotImplementedException(); foreach (var item in collection)
{
Console.WriteLine(item);
} }
class ViewModel
{
public int Id { get; set; }
public string Value { get; set; } = default!;
}
class HogeFilter : ISynchronizedViewFilter<int>
{
public bool IsMatch(int value)
{
return value % 2 == 0;
}
public void OnCollectionChanged(in SynchronizedViewChangedEventArgs<int, ViewModel> eventArgs) //var buffer = new ObservableFixedSizeRingBuffer<int>(5);
{
switch (eventArgs.Action) //var view = buffer.CreateView(value => value);
{ //view.AttachFilter(value => value % 2 == 1); // when filtered, mismatch...!
case NotifyCollectionChangedAction.Add:
eventArgs.NewItem.View.Value += " Add"; ////{
break; //// INotifyCollectionChangedSynchronizedViewList created from ISynchronizedView with a filter.
case NotifyCollectionChangedAction.Remove: //var collection = view.ToNotifyCollectionChanged();
eventArgs.OldItem.View.Value += " Remove";
break; //// Not disposed here.
case NotifyCollectionChangedAction.Move: ////}
eventArgs.NewItem.View.Value += $" Move {eventArgs.OldStartingIndex} {eventArgs.NewStartingIndex}";
break; //buffer.AddFirst(1);
case NotifyCollectionChangedAction.Replace: //buffer.AddFirst(1);
eventArgs.NewItem.View.Value += $" Replace {eventArgs.NewStartingIndex}"; //buffer.AddFirst(2);
break; //buffer.AddFirst(3);
case NotifyCollectionChangedAction.Reset: //buffer.AddFirst(5);
break; //buffer.AddFirst(8); // Argument out of range
default: //buffer.AddFirst(13);
throw new ArgumentOutOfRangeException(nameof(eventArgs.Action), eventArgs.Action, null);
} //foreach (var item in collection)
} //{
} // Console.WriteLine(item);
//}
//Console.WriteLine("---");
//foreach (var item in view)
//{
// Console.WriteLine(item);
//}
//Console.WriteLine("---");
//foreach (var item in buffer)
//{
// Console.WriteLine(item);
//}

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -24,7 +23,7 @@ public class AlternateIndexList<T> : IEnumerable<T>
this.list = values.Select(x => new IndexedValue(x.OrderedAlternateIndex, x.Value)).ToList(); this.list = values.Select(x => new IndexedValue(x.OrderedAlternateIndex, x.Value)).ToList();
} }
void UpdateAlternateIndex(int startIndex, int incr) public void UpdateAlternateIndex(int startIndex, int incr)
{ {
var span = CollectionsMarshal.AsSpan(list); var span = CollectionsMarshal.AsSpan(list);
for (int i = startIndex; i < span.Length; i++) for (int i = startIndex; i < span.Length; i++)
@ -80,11 +79,15 @@ public class AlternateIndexList<T> : IEnumerable<T>
public int RemoveAt(int alternateIndex) public int RemoveAt(int alternateIndex)
{ {
var index = list.BinarySearch(alternateIndex); var index = list.BinarySearch(alternateIndex);
if (index != -1) if (index >= 0)
{ {
list.RemoveAt(index); list.RemoveAt(index);
UpdateAlternateIndex(index, -1); UpdateAlternateIndex(index, -1);
} }
else
{
throw new InvalidOperationException("Index was not found. AlternateIndex:" + alternateIndex);
}
return index; return index;
} }
@ -124,6 +127,19 @@ public class AlternateIndexList<T> : IEnumerable<T>
return true; return true;
} }
public bool TryReplaceAlternateIndex(int getAlternateIndex, int setAlternateIndex)
{
var index = list.BinarySearch(getAlternateIndex);
if (index < 0)
{
return false;
}
var span = CollectionsMarshal.AsSpan(list);
span[index].AlternateIndex = setAlternateIndex;
return true;
}
public bool TryReplaceByValue(T searchValue, T replaceValue, out int replacedIndex) public bool TryReplaceByValue(T searchValue, T replaceValue, out int replacedIndex)
{ {
replacedIndex = list.FindIndex(x => EqualityComparer<T>.Default.Equals(x.Value, searchValue)); replacedIndex = list.FindIndex(x => EqualityComparer<T>.Default.Equals(x.Value, searchValue));

View File

@ -25,6 +25,11 @@ namespace ObservableCollections
{ {
} }
public enum RejectedViewChangedAction
{
Add, Remove, Move
}
public interface ISynchronizedView<T, TView> : IReadOnlyCollection<TView>, IDisposable public interface ISynchronizedView<T, TView> : IReadOnlyCollection<TView>, IDisposable
{ {
object SyncRoot { get; } object SyncRoot { get; }
@ -34,6 +39,7 @@ namespace ObservableCollections
int UnfilteredCount { get; } int UnfilteredCount { get; }
event NotifyViewChangedEventHandler<T, TView>? ViewChanged; event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
event Action<NotifyCollectionChangedAction>? CollectionStateChanged; event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T> filter); void AttachFilter(ISynchronizedViewFilter<T> filter);

View File

@ -39,6 +39,7 @@ namespace ObservableCollections
public object SyncRoot { get; } public object SyncRoot { get; }
public event NotifyViewChangedEventHandler<KeyValuePair<TKey, TValue>, TView>? ViewChanged; public event NotifyViewChangedEventHandler<KeyValuePair<TKey, TValue>, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> Filter public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> Filter
@ -184,14 +185,14 @@ namespace ObservableCollections
{ {
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));
this.InvokeOnAdd(ref filteredCount, ViewChanged, e.NewItem, v, -1); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, e.NewItem, v, -1);
} }
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))
{ {
this.InvokeOnRemove(ref filteredCount, ViewChanged, e.OldItem, v.Item2, -1); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, e.OldItem, v.Item2, -1);
} }
} }
break; break;

View File

@ -30,6 +30,7 @@ namespace ObservableCollections
ISynchronizedViewFilter<T> filter; ISynchronizedViewFilter<T> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged; public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; } public object SyncRoot { get; }
@ -181,7 +182,7 @@ namespace ObservableCollections
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
dict.Add(e.NewItem, v); dict.Add(e.NewItem, v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, -1); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, -1);
} }
else else
{ {
@ -190,7 +191,7 @@ namespace ObservableCollections
{ {
var v = (item, selector(item)); var v = (item, selector(item));
dict.Add(item, v); dict.Add(item, v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, i++);
} }
} }
break; break;
@ -199,7 +200,7 @@ namespace ObservableCollections
{ {
if (dict.Remove(e.OldItem, out var value)) if (dict.Remove(e.OldItem, out var value))
{ {
this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, value, -1);
} }
} }
else else
@ -208,7 +209,7 @@ namespace ObservableCollections
{ {
if (dict.Remove(item, out var value)) if (dict.Remove(item, out var value))
{ {
this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, value, -1);
} }
} }
} }

View File

@ -32,6 +32,7 @@ namespace ObservableCollections
ISynchronizedViewFilter<T> filter; ISynchronizedViewFilter<T> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged; public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; } public object SyncRoot { get; }
@ -185,7 +186,7 @@ namespace ObservableCollections
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
list.Insert(e.NewStartingIndex, v); list.Insert(e.NewStartingIndex, v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, e.NewStartingIndex);
} }
else else
{ {
@ -214,7 +215,7 @@ namespace ObservableCollections
} }
list.InsertRange(e.NewStartingIndex, valueViews.Span); list.InsertRange(e.NewStartingIndex, valueViews.Span);
this.InvokeOnAddRange(ViewChanged, e.NewItems, views.Span, isMatchAll, matches.Span, e.NewStartingIndex); this.InvokeOnAddRange(ViewChanged, RejectedViewChanged, e.NewItems, views.Span, isMatchAll, matches.Span, e.NewStartingIndex);
} }
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
@ -222,7 +223,7 @@ namespace ObservableCollections
{ {
var v = list[e.OldStartingIndex]; var v = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex); list.RemoveAt(e.OldStartingIndex);
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, e.OldStartingIndex); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v, e.OldStartingIndex);
} }
else else
{ {
@ -251,7 +252,7 @@ namespace ObservableCollections
} }
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length); list.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
this.InvokeOnRemoveRange(ViewChanged, values.Span, views.Span, isMatchAll, matches.Span, e.OldStartingIndex); this.InvokeOnRemoveRange(ViewChanged, RejectedViewChanged, values.Span, views.Span, isMatchAll, matches.Span, e.OldStartingIndex);
} }
break; break;
case NotifyCollectionChangedAction.Replace: case NotifyCollectionChangedAction.Replace:
@ -269,7 +270,7 @@ namespace ObservableCollections
list.RemoveAt(e.OldStartingIndex); list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem); list.Insert(e.NewStartingIndex, removeItem);
this.InvokeOnMove(ref filteredCount, ViewChanged, removeItem, e.NewStartingIndex, e.OldStartingIndex); this.InvokeOnMove(ref filteredCount, ViewChanged, RejectedViewChanged, removeItem, e.NewStartingIndex, e.OldStartingIndex);
} }
break; break;
case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Reset:

View File

@ -25,6 +25,7 @@ namespace ObservableCollections
ISynchronizedViewFilter<T> filter; ISynchronizedViewFilter<T> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged; public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; } public object SyncRoot { get; }
@ -182,7 +183,7 @@ namespace ObservableCollections
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
queue.Enqueue(v); queue.Enqueue(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, e.NewStartingIndex);
} }
else else
{ {
@ -191,7 +192,7 @@ namespace ObservableCollections
{ {
var v = (item, selector(item)); var v = (item, selector(item));
queue.Enqueue(v); queue.Enqueue(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, i++);
} }
} }
break; break;
@ -200,7 +201,7 @@ namespace ObservableCollections
if (e.IsSingleItem) if (e.IsSingleItem)
{ {
var v = queue.Dequeue(); var v = queue.Dequeue();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v.Item1, v.Item2, 0);
} }
else else
{ {
@ -208,7 +209,7 @@ namespace ObservableCollections
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
var v = queue.Dequeue(); var v = queue.Dequeue();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v.Item1, v.Item2, 0);
} }
} }
break; break;

View File

@ -31,6 +31,7 @@ namespace ObservableCollections
ISynchronizedViewFilter<T> filter; ISynchronizedViewFilter<T> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged; public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; } public object SyncRoot { get; }
@ -196,7 +197,7 @@ namespace ObservableCollections
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
ringBuffer.AddFirst(v); ringBuffer.AddFirst(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, 0);
} }
else else
{ {
@ -204,7 +205,7 @@ namespace ObservableCollections
{ {
var v = (item, selector(item)); var v = (item, selector(item));
ringBuffer.AddFirst(v); ringBuffer.AddFirst(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, 0);
} }
} }
} }
@ -215,7 +216,7 @@ namespace ObservableCollections
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
ringBuffer.AddLast(v); ringBuffer.AddLast(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, ringBuffer.Count - 1); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, ringBuffer.Count - 1);
} }
else else
{ {
@ -223,7 +224,7 @@ namespace ObservableCollections
{ {
var v = (item, selector(item)); var v = (item, selector(item));
ringBuffer.AddLast(v); ringBuffer.AddLast(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, ringBuffer.Count - 1); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, ringBuffer.Count - 1);
} }
} }
} }
@ -236,14 +237,14 @@ namespace ObservableCollections
if (e.IsSingleItem) if (e.IsSingleItem)
{ {
var v = ringBuffer.RemoveFirst(); var v = ringBuffer.RemoveFirst();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, 0); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v, 0);
} }
else else
{ {
for (int i = 0; i < e.OldItems.Length; i++) for (int i = 0; i < e.OldItems.Length; i++)
{ {
var v = ringBuffer.RemoveFirst(); var v = ringBuffer.RemoveFirst();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, 0); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v, 0);
} }
} }
} }
@ -254,7 +255,7 @@ namespace ObservableCollections
{ {
var index = ringBuffer.Count - 1; var index = ringBuffer.Count - 1;
var v = ringBuffer.RemoveLast(); var v = ringBuffer.RemoveLast();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v, index);
} }
else else
{ {
@ -262,7 +263,7 @@ namespace ObservableCollections
{ {
var index = ringBuffer.Count - 1; var index = ringBuffer.Count - 1;
var v = ringBuffer.RemoveLast(); var v = ringBuffer.RemoveLast();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v, index);
} }
} }
} }

View File

@ -24,6 +24,7 @@ namespace ObservableCollections
ISynchronizedViewFilter<T> filter; ISynchronizedViewFilter<T> filter;
public event NotifyViewChangedEventHandler<T, TView>? ViewChanged; public event NotifyViewChangedEventHandler<T, TView>? ViewChanged;
public event Action<RejectedViewChangedAction, int, int>? RejectedViewChanged;
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged; public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
public object SyncRoot { get; } public object SyncRoot { get; }
@ -187,7 +188,7 @@ namespace ObservableCollections
{ {
var v = (e.NewItem, selector(e.NewItem)); var v = (e.NewItem, selector(e.NewItem));
stack.Push(v); stack.Push(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, 0);
} }
else else
{ {
@ -195,7 +196,7 @@ namespace ObservableCollections
{ {
var v = (item, selector(item)); var v = (item, selector(item));
stack.Push(v); stack.Push(v);
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0); this.InvokeOnAdd(ref filteredCount, ViewChanged, RejectedViewChanged, v, 0);
} }
} }
break; break;
@ -204,7 +205,7 @@ namespace ObservableCollections
if (e.IsSingleItem) if (e.IsSingleItem)
{ {
var v = stack.Pop(); var v = stack.Pop();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v.Item1, v.Item2, 0);
} }
else else
{ {
@ -212,7 +213,7 @@ namespace ObservableCollections
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
var v = stack.Pop(); var v = stack.Pop();
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0); this.InvokeOnRemove(ref filteredCount, ViewChanged, RejectedViewChanged, v.Item1, v.Item2, 0);
} }
} }
break; break;

View File

@ -94,108 +94,108 @@ namespace ObservableCollections
return filter == SynchronizedViewFilter<T>.Null; return filter == SynchronizedViewFilter<T>.Null;
} }
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int index) internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, (T value, TView view) value, int index)
{ {
InvokeOnAdd(collection, ref filteredCount, ev, value.value, value.view, index); InvokeOnAdd(collection, ref filteredCount, ev, ev2, value.value, value.view, index);
} }
internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int index) internal static void InvokeOnAdd<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, T value, TView view, int index)
{ {
var isMatch = collection.Filter.IsMatch(value); var isMatch = collection.Filter.IsMatch(value);
if (isMatch) if (isMatch)
{ {
filteredCount++; filteredCount++;
if (ev != null) ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, true, newItem: (value, view), newStartingIndex: index));
{ }
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, true, newItem: (value, view), newStartingIndex: index)); else
} {
} ev2?.Invoke(RejectedViewChangedAction.Add, index, -1);
} }
}
internal static void InvokeOnAddRange<T, TView>(this ISynchronizedView<T, TView> collection, NotifyViewChangedEventHandler<T, TView>? ev, ReadOnlySpan<T> values, ReadOnlySpan<TView> views, bool isMatchAll, ReadOnlySpan<bool> matches, int index)
{ internal static void InvokeOnAddRange<T, TView>(this ISynchronizedView<T, TView> collection, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, ReadOnlySpan<T> values, ReadOnlySpan<TView> views, bool isMatchAll, ReadOnlySpan<bool> matches, int index)
if (ev != null) {
{ if (isMatchAll)
if (isMatchAll) {
{ ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, isSingleItem: false, newValues: values, newViews: views, newStartingIndex: index));
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, isSingleItem: false, newValues: values, newViews: views, newStartingIndex: index));
} }
else else
{ {
var startingIndex = index;
for (var i = 0; i < matches.Length; i++) for (var i = 0; i < matches.Length; i++)
{ {
if (matches[i]) if (matches[i])
{ {
var item = (values[i], views[i]); var item = (values[i], views[i]);
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, isSingleItem: true, newItem: item, newStartingIndex: startingIndex++)); ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, isSingleItem: true, newItem: item, newStartingIndex: index));
} }
} else
}
}
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int oldIndex)
{ {
InvokeOnRemove(collection, ref filteredCount, ev, value.value, value.view, oldIndex); ev2?.Invoke(RejectedViewChangedAction.Add, index, -1);
}
index++;
}
}
} }
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int oldIndex) internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, (T value, TView view) value, int oldIndex)
{
InvokeOnRemove(collection, ref filteredCount, ev, ev2, value.value, value.view, oldIndex);
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, T value, TView view, int oldIndex)
{ {
var isMatch = collection.Filter.IsMatch(value); var isMatch = collection.Filter.IsMatch(value);
if (isMatch) if (isMatch)
{ {
filteredCount--; filteredCount--;
if (ev != null) ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, true, oldItem: (value, view), oldStartingIndex: oldIndex));
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, true, oldItem: (value, view), oldStartingIndex: oldIndex));
} }
else
{
ev2?.Invoke(RejectedViewChangedAction.Remove, oldIndex, -1);
} }
} }
// only use for ObservableList // only use for ObservableList
internal static void InvokeOnRemoveRange<T, TView>(this ISynchronizedView<T, TView> collection, NotifyViewChangedEventHandler<T, TView>? ev, ReadOnlySpan<T> values, ReadOnlySpan<TView> views, bool isMatchAll, ReadOnlySpan<bool> matches, int index) internal static void InvokeOnRemoveRange<T, TView>(this ISynchronizedView<T, TView> collection, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, ReadOnlySpan<T> values, ReadOnlySpan<TView> views, bool isMatchAll, ReadOnlySpan<bool> matches, int index)
{
if (ev != null)
{ {
if (isMatchAll) if (isMatchAll)
{ {
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, isSingleItem: false, oldValues: values, oldViews: views, oldStartingIndex: index)); ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, isSingleItem: false, oldValues: values, oldViews: views, oldStartingIndex: index));
} }
else else
{ {
var startingIndex = index;
for (var i = 0; i < matches.Length; i++) for (var i = 0; i < matches.Length; i++)
{ {
if (matches[i]) if (matches[i])
{ {
var item = (values[i], views[i]); var item = (values[i], views[i]);
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, isSingleItem: true, oldItem: item, oldStartingIndex: index)); //remove for list, always same index ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, isSingleItem: true, oldItem: item, oldStartingIndex: index)); //remove for list, always same index
} }
else else
{ {
index++; // not matched, skip index ev2?.Invoke(RejectedViewChangedAction.Remove, index, -1);
}
} }
} }
} }
} }
internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, (T value, TView view) value, int index, int oldIndex) internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, (T value, TView view) value, int index, int oldIndex)
{ {
InvokeOnMove(collection, ref filteredCount, ev, value.value, value.view, index, oldIndex); InvokeOnMove(collection, ref filteredCount, ev, ev2, value.value, value.view, index, oldIndex);
} }
internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, T value, TView view, int index, int oldIndex) internal static void InvokeOnMove<T, TView>(this ISynchronizedView<T, TView> collection, ref int filteredCount, NotifyViewChangedEventHandler<T, TView>? ev, Action<RejectedViewChangedAction, int, int>? ev2, T value, TView view, int index, int oldIndex)
{
if (ev != null)
{ {
// move does not changes filtered-count // move does not changes filtered-count
var isMatch = collection.Filter.IsMatch(value); var isMatch = collection.Filter.IsMatch(value);
if (isMatch) if (isMatch)
{ {
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, true, newItem: (value, view), newStartingIndex: index, oldStartingIndex: oldIndex)); ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Move, true, newItem: (value, view), newStartingIndex: index, oldStartingIndex: oldIndex));
} }
else
{
ev2?.Invoke(RejectedViewChangedAction.Move, index, oldIndex);
} }
} }
@ -212,29 +212,19 @@ namespace ObservableCollections
if (bothMatched) if (bothMatched)
{ {
if (ev != null) ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Replace, true, newItem: (value, view), oldItem: (oldValue, oldView), newStartingIndex: index, oldStartingIndex: oldIndex >= 0 ? oldIndex : index));
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Replace, true, newItem: (value, view), oldItem: (oldValue, oldView), newStartingIndex: index, oldStartingIndex: oldIndex >= 0 ? oldIndex : index));
}
} }
else if (oldMatched) else if (oldMatched)
{ {
// only-old is remove // only-old is remove
filteredCount--; filteredCount--;
if (ev != null) ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, true, oldItem: (value, view), oldStartingIndex: oldIndex));
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Remove, true, oldItem: (value, view), oldStartingIndex: oldIndex));
}
} }
else if (newMatched) else if (newMatched)
{ {
// only-new is add // only-new is add
filteredCount++; filteredCount++;
if (ev != null) ev?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, true, newItem: (value, view), newStartingIndex: index));
{
ev.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Add, true, newItem: (value, view), newStartingIndex: index));
}
} }
} }

View File

@ -23,6 +23,7 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
{ {
listView = new AlternateIndexList<TView>(IterateFilteredIndexedViewsOfParent()); listView = new AlternateIndexList<TView>(IterateFilteredIndexedViewsOfParent());
parent.ViewChanged += Parent_ViewChanged; parent.ViewChanged += Parent_ViewChanged;
parent.RejectedViewChanged += Parent_RejectedViewChanged;
} }
} }
@ -153,6 +154,27 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
} }
} }
private void Parent_RejectedViewChanged(RejectedViewChangedAction arg1, int index, int oldIndex)
{
lock (gate)
{
switch (arg1)
{
case RejectedViewChangedAction.Add:
listView.UpdateAlternateIndex(index, 1);
break;
case RejectedViewChangedAction.Remove:
listView.UpdateAlternateIndex(index, -1);
break;
case RejectedViewChangedAction.Move:
listView.TryReplaceAlternateIndex(oldIndex, index);
break;
default:
break;
}
}
}
protected virtual void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args) protected virtual void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
{ {
} }
@ -198,6 +220,7 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
public void Dispose() public void Dispose()
{ {
parent.ViewChanged -= Parent_ViewChanged; parent.ViewChanged -= Parent_ViewChanged;
parent.RejectedViewChanged -= Parent_RejectedViewChanged;
} }
} }