180 lines
7.3 KiB
C#
180 lines
7.3 KiB
C#
using ObservableCollections.Internal;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Linq;
|
|
|
|
namespace ObservableCollections
|
|
{
|
|
public sealed partial class ObservableDictionary<TKey, TValue>
|
|
{
|
|
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool _ = false)
|
|
{
|
|
// reverse is no used.
|
|
return new View<TView>(this, transform);
|
|
}
|
|
|
|
class View<TView> : ISynchronizedView<KeyValuePair<TKey, TValue>, TView>
|
|
{
|
|
readonly ObservableDictionary<TKey, TValue> source;
|
|
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
|
|
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
|
|
readonly Dictionary<TKey, (TValue, TView)> dict;
|
|
|
|
public View(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector)
|
|
{
|
|
this.source = source;
|
|
this.selector = selector;
|
|
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
|
|
this.SyncRoot = new object();
|
|
lock (source.SyncRoot)
|
|
{
|
|
this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x)));
|
|
this.source.CollectionChanged += SourceCollectionChanged;
|
|
}
|
|
}
|
|
|
|
public object SyncRoot { get; }
|
|
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>> RoutingCollectionChanged;
|
|
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
|
|
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
lock (SyncRoot)
|
|
{
|
|
return dict.Count;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
this.source.CollectionChanged -= SourceCollectionChanged;
|
|
}
|
|
|
|
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter, bool invokeAddEventForCurrentElements = false)
|
|
{
|
|
lock (SyncRoot)
|
|
{
|
|
this.filter = filter;
|
|
foreach (var v in dict)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ResetFilter(Action<KeyValuePair<TKey, TValue>, TView> resetAction)
|
|
{
|
|
lock (SyncRoot)
|
|
{
|
|
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
|
|
if (resetAction != null)
|
|
{
|
|
foreach (var v in dict)
|
|
{
|
|
resetAction(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public INotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView> WithINotifyCollectionChanged()
|
|
{
|
|
lock (SyncRoot)
|
|
{
|
|
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this);
|
|
}
|
|
}
|
|
|
|
public IEnumerator<(KeyValuePair<TKey, TValue>, TView)> GetEnumerator()
|
|
{
|
|
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, v.Item2))
|
|
{
|
|
yield return v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>> e)
|
|
{
|
|
// ObservableDictionary only provides single item operation and does not use int index.
|
|
lock (SyncRoot)
|
|
{
|
|
switch (e.Action)
|
|
{
|
|
case NotifyCollectionChangedAction.Add:
|
|
{
|
|
var v = selector(e.NewItem);
|
|
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
|
|
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v, e);
|
|
}
|
|
break;
|
|
case NotifyCollectionChangedAction.Remove:
|
|
{
|
|
if (dict.Remove(e.OldItem.Key, out var v))
|
|
{
|
|
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2), e);
|
|
}
|
|
}
|
|
break;
|
|
case NotifyCollectionChangedAction.Replace:
|
|
{
|
|
if (dict.Remove(e.OldItem.Key, out var oldView))
|
|
{
|
|
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);
|
|
}
|
|
break;
|
|
case NotifyCollectionChangedAction.Reset:
|
|
{
|
|
if (!filter.IsNullFilter())
|
|
{
|
|
foreach (var item in dict)
|
|
{
|
|
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2), e);
|
|
}
|
|
}
|
|
|
|
dict.Clear();
|
|
}
|
|
break;
|
|
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
|
|
default:
|
|
break;
|
|
}
|
|
|
|
RoutingCollectionChanged?.Invoke(e);
|
|
CollectionStateChanged?.Invoke(e.Action);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |