Dict, HashSet, Queue, RingBuffer, Stack
This commit is contained in:
parent
ce624265f3
commit
295cef5ae5
@ -9,7 +9,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
public sealed partial class ObservableDictionary<TKey, TValue>
|
public sealed partial class ObservableDictionary<TKey, TValue>
|
||||||
{
|
{
|
||||||
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool _ = false)
|
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform)
|
||||||
{
|
{
|
||||||
// reverse is no used.
|
// reverse is no used.
|
||||||
return new View<TView>(this, transform);
|
return new View<TView>(this, transform);
|
||||||
@ -19,32 +19,45 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
readonly ObservableDictionary<TKey, TValue> source;
|
readonly ObservableDictionary<TKey, TValue> source;
|
||||||
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
|
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
|
||||||
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
|
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> filter;
|
||||||
readonly Dictionary<TKey, (TValue, TView)> dict;
|
readonly Dictionary<TKey, (TValue, TView)> dict;
|
||||||
|
int filteredCount;
|
||||||
|
|
||||||
public View(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector)
|
public View(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector)
|
||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
|
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
|
||||||
this.SyncRoot = new object();
|
this.SyncRoot = new object();
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x)));
|
this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x)));
|
||||||
|
this.filteredCount = dict.Count;
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public object SyncRoot { get; }
|
public object SyncRoot { get; }
|
||||||
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>>? RoutingCollectionChanged;
|
public event Action<SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>>? ViewChanged;
|
||||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||||
|
|
||||||
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> CurrentFilter
|
public ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> Filter
|
||||||
{
|
{
|
||||||
get { lock (SyncRoot) return filter; }
|
get { lock (SyncRoot) return filter; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
return filteredCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnfilteredCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -60,76 +73,104 @@ namespace ObservableCollections
|
|||||||
this.source.CollectionChanged -= SourceCollectionChanged;
|
this.source.CollectionChanged -= SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter, bool invokeAddEventForCurrentElements = false)
|
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>> filter)
|
||||||
{
|
{
|
||||||
|
if (filter.IsNullFilter())
|
||||||
|
{
|
||||||
|
ResetFilter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
this.filteredCount = 0;
|
||||||
foreach (var v in dict)
|
foreach (var v in dict)
|
||||||
{
|
{
|
||||||
var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
|
var value = new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1);
|
||||||
var view = v.Value.Item2;
|
if (filter.IsMatch(value))
|
||||||
if (invokeAddEventForCurrentElements)
|
|
||||||
{
|
{
|
||||||
filter.InvokeOnAdd(value, view, -1);
|
filteredCount++;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filter.InvokeOnAttach(value, view);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetFilter(Action<KeyValuePair<TKey, TValue>, TView>? resetAction)
|
public void ResetFilter()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
|
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>>.Null;
|
||||||
if (resetAction != null)
|
this.filteredCount = dict.Count;
|
||||||
{
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<KeyValuePair<TKey, TValue>, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
foreach (var v in dict)
|
|
||||||
{
|
|
||||||
resetAction(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
|
{
|
||||||
|
return new SynchronizedViewList<KeyValuePair<TKey, TValue>, TView>(this);
|
||||||
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, null);
|
||||||
{
|
|
||||||
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, collectionEventDispatcher);
|
||||||
{
|
|
||||||
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this, collectionEventDispatcher);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<(KeyValuePair<TKey, TValue>, TView)> GetEnumerator()
|
public IEnumerator<TView> GetEnumerator()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
foreach (var item in dict)
|
||||||
{
|
{
|
||||||
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
|
var v = (new KeyValuePair<TKey, TValue>(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<TKey, TValue> Value, TView View)> Filtered
|
||||||
{
|
{
|
||||||
return GetEnumerator();
|
get
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
foreach (var item in dict)
|
||||||
|
{
|
||||||
|
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
|
||||||
|
if (filter.IsMatch(v.Item1))
|
||||||
|
{
|
||||||
|
yield return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(KeyValuePair<TKey, TValue> Value, TView View)> Unfiltered
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
foreach (var item in dict)
|
||||||
|
{
|
||||||
|
var v = (new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2);
|
||||||
|
yield return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>> e)
|
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>> e)
|
||||||
@ -140,41 +181,40 @@ 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(e.NewItem, v, -1);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, e.NewItem, v, -1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Remove:
|
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;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
{
|
{
|
||||||
var v = selector(e.NewItem);
|
var v = selector(e.NewItem);
|
||||||
dict.Remove(e.OldItem.Key, out var ov);
|
dict.Remove(e.OldItem.Key, out var ov);
|
||||||
dict[e.NewItem.Key] = (e.NewItem.Value, v);
|
dict[e.NewItem.Key] = (e.NewItem.Value, v);
|
||||||
|
|
||||||
filter.InvokeOnReplace(e.NewItem, v, e.OldItem, ov.Item2, -1);
|
this.InvokeOnReplace(ref filteredCount, ViewChanged, e.NewItem, v, e.OldItem, ov.Item2, -1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
{
|
{
|
||||||
dict.Clear();
|
dict.Clear();
|
||||||
filter.InvokeOnReset();
|
this.InvokeOnReset(ref filteredCount, ViewChanged);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
|
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoutingCollectionChanged?.Invoke(e);
|
|
||||||
CollectionStateChanged?.Invoke(e.Action);
|
CollectionStateChanged?.Invoke(e.Action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace ObservableCollections
|
namespace ObservableCollections
|
||||||
{
|
{
|
||||||
public sealed partial class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
|
public sealed partial class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyObservableDictionary<TKey, TValue>
|
||||||
IReadOnlyObservableDictionary<TKey, TValue>
|
|
||||||
where TKey : notnull
|
where TKey : notnull
|
||||||
{
|
{
|
||||||
readonly Dictionary<TKey, TValue> dictionary;
|
readonly Dictionary<TKey, TValue> dictionary;
|
||||||
|
@ -4,19 +4,20 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ObservableCollections
|
namespace ObservableCollections
|
||||||
{
|
{
|
||||||
public sealed partial class ObservableHashSet<T> : IReadOnlyCollection<T>, IObservableCollection<T>
|
public sealed partial class ObservableHashSet<T> : IReadOnlyCollection<T>, IObservableCollection<T>
|
||||||
{
|
{
|
||||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool _ = false)
|
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
|
||||||
{
|
{
|
||||||
return new View<TView>(this, transform);
|
return new View<TView>(this, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class View<TView> : ISynchronizedView<T, TView>
|
sealed class View<TView> : ISynchronizedView<T, TView>
|
||||||
{
|
{
|
||||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
public ISynchronizedViewFilter<T> Filter
|
||||||
{
|
{
|
||||||
get { lock (SyncRoot) return filter; }
|
get { lock (SyncRoot) return filter; }
|
||||||
}
|
}
|
||||||
@ -24,10 +25,11 @@ namespace ObservableCollections
|
|||||||
readonly ObservableHashSet<T> source;
|
readonly ObservableHashSet<T> source;
|
||||||
readonly Func<T, TView> selector;
|
readonly Func<T, TView> selector;
|
||||||
readonly Dictionary<T, (T, TView)> dict;
|
readonly Dictionary<T, (T, TView)> dict;
|
||||||
|
int filteredCount;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T> filter;
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||||
|
|
||||||
public object SyncRoot { get; }
|
public object SyncRoot { get; }
|
||||||
@ -36,16 +38,28 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.SyncRoot = new object();
|
this.SyncRoot = new object();
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
this.dict = source.set.ToDictionary(x => x, x => (x, selector(x)));
|
this.dict = source.set.ToDictionary(x => x, x => (x, selector(x)));
|
||||||
|
this.filteredCount = dict.Count;
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
return filteredCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnfilteredCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -56,65 +70,62 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
public void AttachFilter(ISynchronizedViewFilter<T> filter)
|
||||||
{
|
{
|
||||||
|
if (filter.IsNullFilter())
|
||||||
|
{
|
||||||
|
ResetFilter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
this.filteredCount = 0;
|
||||||
foreach (var (_, (value, view)) in dict)
|
foreach (var (_, (value, view)) in dict)
|
||||||
{
|
{
|
||||||
if (invokeAddEventForCurrentElements)
|
if (filter.IsMatch(value))
|
||||||
{
|
{
|
||||||
filter.InvokeOnAdd((value, view), -1);
|
filteredCount++;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filter.InvokeOnAttach(value, view);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetFilter(Action<T, TView>? resetAction)
|
public void ResetFilter()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
if (resetAction != null)
|
this.filteredCount = dict.Count;
|
||||||
{
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
foreach (var (_, (value, view)) in dict)
|
|
||||||
{
|
|
||||||
resetAction(value, view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
|
{
|
||||||
|
return new SynchronizedViewList<T, TView>(this);
|
||||||
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
||||||
{
|
|
||||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
||||||
{
|
|
||||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<(T, TView)> GetEnumerator()
|
public IEnumerator<TView> GetEnumerator()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
foreach (var item in dict)
|
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();
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
this.source.CollectionChanged -= SourceCollectionChanged;
|
this.source.CollectionChanged -= SourceCollectionChanged;
|
||||||
@ -138,7 +180,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);
|
||||||
filter.InvokeOnAdd(v, -1);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, -1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -147,7 +189,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var v = (item, selector(item));
|
var v = (item, selector(item));
|
||||||
dict.Add(item, v);
|
dict.Add(item, v);
|
||||||
filter.InvokeOnAdd(v, i++);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -156,7 +198,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
if (dict.Remove(e.OldItem, out var value))
|
if (dict.Remove(e.OldItem, out var value))
|
||||||
{
|
{
|
||||||
filter.InvokeOnRemove(value, -1);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -165,14 +207,14 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
if (dict.Remove(item, out var value))
|
if (dict.Remove(item, out var value))
|
||||||
{
|
{
|
||||||
filter.InvokeOnRemove(value, -1);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, value, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
dict.Clear();
|
dict.Clear();
|
||||||
filter.InvokeOnReset();
|
this.InvokeOnReset(ref filteredCount, ViewChanged);
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
@ -180,7 +222,6 @@ namespace ObservableCollections
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoutingCollectionChanged?.Invoke(e);
|
|
||||||
CollectionStateChanged?.Invoke(e.Action);
|
CollectionStateChanged?.Invoke(e.Action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,6 @@ namespace ObservableCollections
|
|||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
|
||||||
this.filteredCount = 0;
|
this.filteredCount = 0;
|
||||||
for (var i = 0; i < list.Count; i++)
|
for (var i = 0; i < list.Count; i++)
|
||||||
{
|
{
|
||||||
|
@ -4,14 +4,15 @@ using System.Collections.Specialized;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ObservableCollections
|
namespace ObservableCollections
|
||||||
{
|
{
|
||||||
public sealed partial class ObservableQueue<T> : IReadOnlyCollection<T>, IObservableCollection<T>
|
public sealed partial class ObservableQueue<T> : IReadOnlyCollection<T>, IObservableCollection<T>
|
||||||
{
|
{
|
||||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
|
||||||
{
|
{
|
||||||
return new View<TView>(this, transform, reverse);
|
return new View<TView>(this, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
class View<TView> : ISynchronizedView<T, TView>
|
class View<TView> : ISynchronizedView<T, TView>
|
||||||
@ -20,34 +21,46 @@ namespace ObservableCollections
|
|||||||
readonly Func<T, TView> selector;
|
readonly Func<T, TView> selector;
|
||||||
readonly bool reverse;
|
readonly bool reverse;
|
||||||
protected readonly Queue<(T, TView)> queue;
|
protected readonly Queue<(T, TView)> queue;
|
||||||
|
int filteredCount;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T> filter;
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||||
|
|
||||||
public object SyncRoot { get; }
|
public object SyncRoot { get; }
|
||||||
|
|
||||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
public ISynchronizedViewFilter<T> Filter
|
||||||
{
|
{
|
||||||
get { lock (SyncRoot) return filter; }
|
get { lock (SyncRoot) return filter; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public View(ObservableQueue<T> source, Func<T, TView> selector, bool reverse)
|
public View(ObservableQueue<T> source, Func<T, TView> selector)
|
||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.reverse = reverse;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
|
||||||
this.SyncRoot = new object();
|
this.SyncRoot = new object();
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
this.queue = new Queue<(T, TView)>(source.queue.Select(x => (x, selector(x))));
|
this.queue = new Queue<(T, TView)>(source.queue.Select(x => (x, selector(x))));
|
||||||
|
this.filteredCount = queue.Count;
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
return filteredCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnfilteredCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -58,79 +71,63 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
public void AttachFilter(ISynchronizedViewFilter<T> filter)
|
||||||
{
|
{
|
||||||
|
if (filter.IsNullFilter())
|
||||||
|
{
|
||||||
|
ResetFilter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
var i = 0;
|
this.filteredCount = 0;
|
||||||
foreach (var (value, view) in queue)
|
foreach (var (value, view) in queue)
|
||||||
{
|
{
|
||||||
if (invokeAddEventForCurrentElements)
|
if (filter.IsMatch(value))
|
||||||
{
|
{
|
||||||
filter.InvokeOnAdd(value, view, i++);
|
filteredCount++;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filter.InvokeOnAttach(value, view);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetFilter(Action<T, TView>? resetAction)
|
public void ResetFilter()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
if (resetAction != null)
|
this.filteredCount = queue.Count;
|
||||||
{
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
foreach (var (item, view) in queue)
|
|
||||||
{
|
|
||||||
resetAction(item, view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
|
{
|
||||||
|
return new SynchronizedViewList<T, TView>(this);
|
||||||
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
||||||
{
|
|
||||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
||||||
{
|
|
||||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this, collectionEventDispatcher);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<(T, TView)> GetEnumerator()
|
public IEnumerator<TView> GetEnumerator()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
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.Item2;
|
||||||
{
|
|
||||||
yield return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var item in queue.AsEnumerable().Reverse())
|
|
||||||
{
|
|
||||||
if (filter.IsMatch(item.Item1, item.Item2))
|
|
||||||
{
|
|
||||||
yield return item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,6 +135,37 @@ namespace ObservableCollections
|
|||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
this.source.CollectionChanged -= SourceCollectionChanged;
|
this.source.CollectionChanged -= SourceCollectionChanged;
|
||||||
@ -155,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);
|
||||||
filter.InvokeOnAdd(v, e.NewStartingIndex);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, e.NewStartingIndex);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -164,7 +192,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var v = (item, selector(item));
|
var v = (item, selector(item));
|
||||||
queue.Enqueue(v);
|
queue.Enqueue(v);
|
||||||
filter.InvokeOnAdd(v, i++);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, i++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -173,7 +201,7 @@ namespace ObservableCollections
|
|||||||
if (e.IsSingleItem)
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
var v = queue.Dequeue();
|
var v = queue.Dequeue();
|
||||||
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -181,13 +209,13 @@ namespace ObservableCollections
|
|||||||
for (int i = 0; i < len; i++)
|
for (int i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
var v = queue.Dequeue();
|
var v = queue.Dequeue();
|
||||||
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
queue.Clear();
|
queue.Clear();
|
||||||
filter.InvokeOnReset();
|
this.InvokeOnReset(ref filteredCount, ViewChanged);
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
@ -195,7 +223,6 @@ namespace ObservableCollections
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoutingCollectionChanged?.Invoke(e);
|
|
||||||
CollectionStateChanged?.Invoke(e.Action);
|
CollectionStateChanged?.Invoke(e.Action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,51 +4,63 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ObservableCollections
|
namespace ObservableCollections
|
||||||
{
|
{
|
||||||
public sealed partial class ObservableRingBuffer<T>
|
public sealed partial class ObservableRingBuffer<T>
|
||||||
{
|
{
|
||||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
|
||||||
{
|
{
|
||||||
return new View<TView>(this, transform, reverse);
|
return new View<TView>(this, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
// used with ObservableFixedSizeRingBuffer
|
// used with ObservableFixedSizeRingBuffer
|
||||||
internal sealed class View<TView> : ISynchronizedView<T, TView>
|
internal sealed class View<TView> : ISynchronizedView<T, TView>
|
||||||
{
|
{
|
||||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
public ISynchronizedViewFilter<T> Filter
|
||||||
{
|
{
|
||||||
get { lock (SyncRoot) return filter; }
|
get { lock (SyncRoot) return filter; }
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly IObservableCollection<T> source;
|
readonly IObservableCollection<T> source;
|
||||||
readonly Func<T, TView> selector;
|
readonly Func<T, TView> selector;
|
||||||
readonly bool reverse;
|
|
||||||
readonly RingBuffer<(T, TView)> ringBuffer;
|
readonly RingBuffer<(T, TView)> ringBuffer;
|
||||||
|
int filteredCount;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T> filter;
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||||
|
|
||||||
public object SyncRoot { get; }
|
public object SyncRoot { get; }
|
||||||
|
|
||||||
public View(IObservableCollection<T> source, Func<T, TView> selector, bool reverse)
|
public View(IObservableCollection<T> source, Func<T, TView> selector)
|
||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.reverse = reverse;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
|
||||||
this.SyncRoot = new object();
|
this.SyncRoot = new object();
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
this.ringBuffer = new RingBuffer<(T, TView)>(source.Select(x => (x, selector(x))));
|
this.ringBuffer = new RingBuffer<(T, TView)>(source.Select(x => (x, selector(x))));
|
||||||
|
this.filteredCount = ringBuffer.Count;
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
return filteredCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnfilteredCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -59,41 +71,44 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
public void AttachFilter(ISynchronizedViewFilter<T> filter)
|
||||||
{
|
{
|
||||||
|
if (filter.IsNullFilter())
|
||||||
|
{
|
||||||
|
ResetFilter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
this.filteredCount = 0;
|
||||||
for (var i = 0; i < ringBuffer.Count; i++)
|
for (var i = 0; i < ringBuffer.Count; i++)
|
||||||
{
|
{
|
||||||
var (value, view) = ringBuffer[i];
|
var (value, view) = ringBuffer[i];
|
||||||
if (invokeAddEventForCurrentElements)
|
if (filter.IsMatch(value))
|
||||||
{
|
{
|
||||||
filter.InvokeOnAdd(value, view, i);
|
filteredCount++;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filter.InvokeOnAttach(value, view);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetFilter(Action<T, TView>? resetAction)
|
public void ResetFilter()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
if (resetAction != null)
|
this.filteredCount = ringBuffer.Count;
|
||||||
{
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
foreach (var (item, view) in ringBuffer)
|
|
||||||
{
|
|
||||||
resetAction(item, view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
|
{
|
||||||
|
return new SynchronizedViewList<T, TView>(this);
|
||||||
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
@ -110,25 +125,31 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<(T, TView)> GetEnumerator()
|
public IEnumerator<TView> GetEnumerator()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
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)
|
foreach (var item in ringBuffer)
|
||||||
{
|
{
|
||||||
if (filter.IsMatch(item.Item1, item.Item2))
|
if (filter.IsMatch(item.Item1))
|
||||||
{
|
|
||||||
yield return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var item in ringBuffer.AsEnumerable().Reverse())
|
|
||||||
{
|
|
||||||
if (filter.IsMatch(item.Item1, item.Item2))
|
|
||||||
{
|
{
|
||||||
yield return item;
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@ -162,7 +195,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var v = (e.NewItem, selector(e.NewItem));
|
var v = (e.NewItem, selector(e.NewItem));
|
||||||
ringBuffer.AddFirst(v);
|
ringBuffer.AddFirst(v);
|
||||||
filter.InvokeOnAdd(v, 0);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -170,7 +203,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var v = (item, selector(item));
|
var v = (item, selector(item));
|
||||||
ringBuffer.AddFirst(v);
|
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));
|
var v = (e.NewItem, selector(e.NewItem));
|
||||||
ringBuffer.AddLast(v);
|
ringBuffer.AddLast(v);
|
||||||
filter.InvokeOnAdd(v, ringBuffer.Count - 1);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, ringBuffer.Count - 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -189,7 +222,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var v = (item, selector(item));
|
var v = (item, selector(item));
|
||||||
ringBuffer.AddLast(v);
|
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)
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
var v = ringBuffer.RemoveFirst();
|
var v = ringBuffer.RemoveFirst();
|
||||||
filter.InvokeOnRemove(v, 0);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, 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();
|
||||||
filter.InvokeOnRemove(v, 0);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,7 +253,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var index = ringBuffer.Count - 1;
|
var index = ringBuffer.Count - 1;
|
||||||
var v = ringBuffer.RemoveLast();
|
var v = ringBuffer.RemoveLast();
|
||||||
filter.InvokeOnRemove(v, index);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -228,14 +261,14 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var index = ringBuffer.Count - 1;
|
var index = ringBuffer.Count - 1;
|
||||||
var v = ringBuffer.RemoveLast();
|
var v = ringBuffer.RemoveLast();
|
||||||
filter.InvokeOnRemove(v, index);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, v, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
ringBuffer.Clear();
|
ringBuffer.Clear();
|
||||||
filter.InvokeOnReset();
|
this.InvokeOnReset(ref filteredCount, ViewChanged);
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
// range is not supported
|
// range is not supported
|
||||||
@ -243,7 +276,7 @@ namespace ObservableCollections
|
|||||||
var ov = ringBuffer[e.OldStartingIndex];
|
var ov = ringBuffer[e.OldStartingIndex];
|
||||||
var v = (e.NewItem, selector(e.NewItem));
|
var v = (e.NewItem, selector(e.NewItem));
|
||||||
ringBuffer[e.NewStartingIndex] = v;
|
ringBuffer[e.NewStartingIndex] = v;
|
||||||
filter.InvokeOnReplace(v, ov, e.NewStartingIndex);
|
this.InvokeOnReplace(ref filteredCount, ViewChanged, v, ov, e.NewStartingIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
@ -251,7 +284,6 @@ namespace ObservableCollections
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoutingCollectionChanged?.Invoke(e);
|
|
||||||
CollectionStateChanged?.Invoke(e.Action);
|
CollectionStateChanged?.Invoke(e.Action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,45 +9,56 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
public sealed partial class ObservableStack<T> : IReadOnlyCollection<T>, IObservableCollection<T>
|
public sealed partial class ObservableStack<T> : IReadOnlyCollection<T>, IObservableCollection<T>
|
||||||
{
|
{
|
||||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform)
|
||||||
{
|
{
|
||||||
return new View<TView>(this, transform, reverse);
|
return new View<TView>(this, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
class View<TView> : ISynchronizedView<T, TView>
|
class View<TView> : ISynchronizedView<T, TView>
|
||||||
{
|
{
|
||||||
readonly ObservableStack<T> source;
|
readonly ObservableStack<T> source;
|
||||||
readonly Func<T, TView> selector;
|
readonly Func<T, TView> selector;
|
||||||
readonly bool reverse;
|
|
||||||
protected readonly Stack<(T, TView)> stack;
|
protected readonly Stack<(T, TView)> stack;
|
||||||
|
int filteredCount;
|
||||||
|
|
||||||
ISynchronizedViewFilter<T, TView> filter;
|
ISynchronizedViewFilter<T> filter;
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
public event Action<SynchronizedViewChangedEventArgs<T, TView>>? ViewChanged;
|
||||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||||
|
|
||||||
public object SyncRoot { get; }
|
public object SyncRoot { get; }
|
||||||
|
|
||||||
public ISynchronizedViewFilter<T, TView> CurrentFilter
|
public ISynchronizedViewFilter<T> Filter
|
||||||
{
|
{
|
||||||
get { lock (SyncRoot) return filter; }
|
get { lock (SyncRoot) return filter; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public View(ObservableStack<T> source, Func<T, TView> selector, bool reverse)
|
public View(ObservableStack<T> source, Func<T, TView> selector)
|
||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.reverse = reverse;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
|
||||||
this.SyncRoot = new object();
|
this.SyncRoot = new object();
|
||||||
lock (source.SyncRoot)
|
lock (source.SyncRoot)
|
||||||
{
|
{
|
||||||
this.stack = new Stack<(T, TView)>(source.stack.Select(x => (x, selector(x))));
|
this.stack = new Stack<(T, TView)>(source.stack.Select(x => (x, selector(x))));
|
||||||
|
this.filteredCount = stack.Count;
|
||||||
this.source.CollectionChanged += SourceCollectionChanged;
|
this.source.CollectionChanged += SourceCollectionChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
return filteredCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnfilteredCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -58,40 +69,44 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter, bool invokeAddEventForCurrentElements = false)
|
public void AttachFilter(ISynchronizedViewFilter<T> filter)
|
||||||
{
|
{
|
||||||
|
if (filter.IsNullFilter())
|
||||||
|
{
|
||||||
|
ResetFilter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
this.filteredCount = 0;
|
||||||
foreach (var (value, view) in stack)
|
foreach (var (value, view) in stack)
|
||||||
{
|
{
|
||||||
if (invokeAddEventForCurrentElements)
|
if (filter.IsMatch(value))
|
||||||
{
|
{
|
||||||
filter.InvokeOnAdd(value, view, 0);
|
filteredCount++;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filter.InvokeOnAttach(value, view);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetFilter(Action<T, TView>? resetAction)
|
public void ResetFilter()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
this.filter = SynchronizedViewFilter<T>.Null;
|
||||||
if (resetAction != null)
|
this.filteredCount = stack.Count;
|
||||||
{
|
ViewChanged?.Invoke(new SynchronizedViewChangedEventArgs<T, TView>(NotifyViewChangedAction.FilterReset));
|
||||||
foreach (var (item, view) in stack)
|
|
||||||
{
|
|
||||||
resetAction(item, view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISynchronizedViewList<TView> ToViewList()
|
||||||
|
{
|
||||||
|
return new SynchronizedViewList<T, TView>(this);
|
||||||
|
}
|
||||||
|
|
||||||
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
public INotifyCollectionChangedSynchronizedView<TView> ToNotifyCollectionChanged()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
@ -108,25 +123,31 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<(T, TView)> GetEnumerator()
|
public IEnumerator<TView> GetEnumerator()
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
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)
|
foreach (var item in stack)
|
||||||
{
|
{
|
||||||
if (filter.IsMatch(item.Item1, item.Item2))
|
if (filter.IsMatch(item.Item1))
|
||||||
{
|
|
||||||
yield return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var item in stack.AsEnumerable().Reverse())
|
|
||||||
{
|
|
||||||
if (filter.IsMatch(item.Item1, item.Item2))
|
|
||||||
{
|
{
|
||||||
yield return item;
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@ -154,7 +187,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var v = (e.NewItem, selector(e.NewItem));
|
var v = (e.NewItem, selector(e.NewItem));
|
||||||
stack.Push(v);
|
stack.Push(v);
|
||||||
filter.InvokeOnAdd(v, 0);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -162,7 +195,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
var v = (item, selector(item));
|
var v = (item, selector(item));
|
||||||
stack.Push(v);
|
stack.Push(v);
|
||||||
filter.InvokeOnAdd(v, 0);
|
this.InvokeOnAdd(ref filteredCount, ViewChanged, v, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -171,7 +204,7 @@ namespace ObservableCollections
|
|||||||
if (e.IsSingleItem)
|
if (e.IsSingleItem)
|
||||||
{
|
{
|
||||||
var v = stack.Pop();
|
var v = stack.Pop();
|
||||||
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -179,13 +212,13 @@ namespace ObservableCollections
|
|||||||
for (int i = 0; i < len; i++)
|
for (int i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
var v = stack.Pop();
|
var v = stack.Pop();
|
||||||
filter.InvokeOnRemove(v.Item1, v.Item2, 0);
|
this.InvokeOnRemove(ref filteredCount, ViewChanged, v.Item1, v.Item2, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
stack.Clear();
|
stack.Clear();
|
||||||
filter.InvokeOnReset();
|
this.InvokeOnReset(ref filteredCount, ViewChanged);
|
||||||
break;
|
break;
|
||||||
case NotifyCollectionChangedAction.Replace:
|
case NotifyCollectionChangedAction.Replace:
|
||||||
case NotifyCollectionChangedAction.Move:
|
case NotifyCollectionChangedAction.Move:
|
||||||
@ -193,7 +226,6 @@ namespace ObservableCollections
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoutingCollectionChanged?.Invoke(e);
|
|
||||||
CollectionStateChanged?.Invoke(e.Action);
|
CollectionStateChanged?.Invoke(e.Action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user