Observable Dict
This commit is contained in:
parent
4946d82444
commit
327850a0db
@ -1,249 +1,257 @@
|
||||
//using ObservableCollections.Internal;
|
||||
//using ObservableCollections;
|
||||
//using System;
|
||||
//using System.Collections;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Collections.Specialized;
|
||||
//using System.Linq;
|
||||
//using System.Runtime.InteropServices;
|
||||
//using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
using ObservableCollections.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
//namespace ObservableCollections
|
||||
//{
|
||||
// public sealed class FreezedList<T> : IReadOnlyList<T>, IFreezedCollection<T>
|
||||
// {
|
||||
// readonly IReadOnlyList<T> list;
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed class FreezedList<T> : IReadOnlyList<T>, IFreezedCollection<T>
|
||||
{
|
||||
readonly IReadOnlyList<T> list;
|
||||
|
||||
// public T this[int index]
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// return list[index];
|
||||
// }
|
||||
// }
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return list[index];
|
||||
}
|
||||
}
|
||||
|
||||
// public int Count
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// return list.Count;
|
||||
// }
|
||||
// }
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
|
||||
// public bool IsReadOnly => true;
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
// public FreezedList(IReadOnlyList<T> list)
|
||||
// {
|
||||
// this.list = list;
|
||||
// }
|
||||
public FreezedList(IReadOnlyList<T> list)
|
||||
{
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
// public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
||||
// {
|
||||
// return new View<TView>(this, transform, reverse);
|
||||
// }
|
||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
||||
{
|
||||
return new View<TView>(this, transform, reverse);
|
||||
}
|
||||
|
||||
// public ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform)
|
||||
// {
|
||||
// return new SortableView<TView>(this, transform);
|
||||
// }
|
||||
public ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform)
|
||||
{
|
||||
return new SortableView<TView>(this, transform);
|
||||
}
|
||||
|
||||
// public bool Contains(T item)
|
||||
// {
|
||||
// return list.Contains(item);
|
||||
// }
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return list.Contains(item);
|
||||
}
|
||||
|
||||
// public IEnumerator<T> GetEnumerator()
|
||||
// {
|
||||
// return list.GetEnumerator();
|
||||
// }
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
// IEnumerator IEnumerable.GetEnumerator()
|
||||
// {
|
||||
// return GetEnumerator();
|
||||
// }
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
// class View<TView> : ISynchronizedView<T, TView>
|
||||
// {
|
||||
// readonly bool reverse;
|
||||
// readonly List<(T, TView)> list;
|
||||
class View<TView> : ISynchronizedView<T, TView>
|
||||
{
|
||||
readonly bool reverse;
|
||||
readonly List<(T, TView)> list;
|
||||
|
||||
// ISynchronizedViewFilter<T, TView> filter;
|
||||
ISynchronizedViewFilter<T, TView> filter;
|
||||
|
||||
// public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
|
||||
// public object SyncRoot { get; } = new object();
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
// public View(FreezedList<T> source, Func<T, TView> selector, bool reverse)
|
||||
// {
|
||||
// this.reverse = reverse;
|
||||
// this.filter = TrueViewFilter<T, TView>.Instance;
|
||||
// this.list = source.Select(x => (x, selector(x))).ToList();
|
||||
// }
|
||||
public View(FreezedList<T> source, Func<T, TView> selector, bool reverse)
|
||||
{
|
||||
this.reverse = reverse;
|
||||
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
|
||||
this.list = source.Select(x => (x, selector(x))).ToList();
|
||||
}
|
||||
|
||||
// public int Count
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// lock (SyncRoot)
|
||||
// {
|
||||
// return list.Count;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
|
||||
// {
|
||||
// lock (SyncRoot)
|
||||
// {
|
||||
// this.filter = filter;
|
||||
// foreach (var (value, view) in list)
|
||||
// {
|
||||
// filter.Invoke(value, view);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
foreach (var (value, view) in list)
|
||||
{
|
||||
filter.Invoke(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public void ResetFilter(Action<T, TView>? resetAction)
|
||||
// {
|
||||
// lock (SyncRoot)
|
||||
// {
|
||||
// this.filter = TrueViewFilter<T, TView>.Instance;
|
||||
// if (resetAction != null)
|
||||
// {
|
||||
// foreach (var (item, view) in list)
|
||||
// {
|
||||
// resetAction(item, view);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public void ResetFilter(Action<T, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var (item, view) in list)
|
||||
{
|
||||
resetAction(item, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public IEnumerator<(T, TView)> GetEnumerator()
|
||||
// {
|
||||
// if (!reverse)
|
||||
// {
|
||||
// return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter);
|
||||
// }
|
||||
// }
|
||||
public IEnumerator<(T, TView)> GetEnumerator()
|
||||
{
|
||||
if (!reverse)
|
||||
{
|
||||
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter);
|
||||
}
|
||||
}
|
||||
|
||||
// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// class SortableView<TView> : ISortableSynchronizedView<T, TView>
|
||||
// {
|
||||
// readonly (T, TView)[] array;
|
||||
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
|
||||
}
|
||||
}
|
||||
|
||||
// ISynchronizedViewFilter<T, TView> filter;
|
||||
class SortableView<TView> : ISortableSynchronizedView<T, TView>
|
||||
{
|
||||
readonly (T, TView)[] array;
|
||||
|
||||
// public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
ISynchronizedViewFilter<T, TView> filter;
|
||||
|
||||
// public object SyncRoot { get; } = new object();
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
|
||||
// public SortableView(FreezedList<T> source, Func<T, TView> selector)
|
||||
// {
|
||||
// this.filter = TrueViewFilter<T, TView>.Instance;
|
||||
// this.array = source.Select(x => (x, selector(x))).ToArray();
|
||||
// }
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
// public int Count
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// lock (SyncRoot)
|
||||
// {
|
||||
// return array.Length;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public SortableView(FreezedList<T> source, Func<T, TView> selector)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
|
||||
this.array = source.Select(x => (x, selector(x))).ToArray();
|
||||
}
|
||||
|
||||
// public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
|
||||
// {
|
||||
// lock (SyncRoot)
|
||||
// {
|
||||
// this.filter = filter;
|
||||
// foreach (var (value, view) in array)
|
||||
// {
|
||||
// filter.Invoke(value, view);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return array.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public void ResetFilter(Action<T, TView>? resetAction)
|
||||
// {
|
||||
// lock (SyncRoot)
|
||||
// {
|
||||
// this.filter = TrueViewFilter<T, TView>.Instance;
|
||||
// if (resetAction != null)
|
||||
// {
|
||||
// foreach (var (item, view) in array)
|
||||
// {
|
||||
// resetAction(item, view);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
foreach (var (value, view) in array)
|
||||
{
|
||||
filter.Invoke(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public IEnumerator<(T, TView)> GetEnumerator()
|
||||
// {
|
||||
// return new SynchronizedViewEnumerator<T, TView>(SyncRoot, array.AsEnumerable().GetEnumerator(), filter);
|
||||
// }
|
||||
public void ResetFilter(Action<T, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.AlwaysTrue;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var (item, view) in array)
|
||||
{
|
||||
resetAction(item, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public IEnumerator<(T, TView)> GetEnumerator()
|
||||
{
|
||||
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, array.AsEnumerable().GetEnumerator(), filter);
|
||||
}
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
// }
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
// public void Sort(IComparer<T> comparer)
|
||||
// {
|
||||
// Array.Sort(array, new TComparer(comparer));
|
||||
// }
|
||||
}
|
||||
|
||||
// public void Sort(IComparer<TView> viewComparer)
|
||||
// {
|
||||
// Array.Sort(array, new TViewComparer(viewComparer));
|
||||
// }
|
||||
public void Sort(IComparer<T> comparer)
|
||||
{
|
||||
Array.Sort(array, new TComparer(comparer));
|
||||
}
|
||||
|
||||
// class TComparer : IComparer<(T, TView)>
|
||||
// {
|
||||
// readonly IComparer<T> comparer;
|
||||
public void Sort(IComparer<TView> viewComparer)
|
||||
{
|
||||
Array.Sort(array, new TViewComparer(viewComparer));
|
||||
}
|
||||
|
||||
// public TComparer(IComparer<T> comparer)
|
||||
// {
|
||||
// this.comparer = comparer;
|
||||
// }
|
||||
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
|
||||
}
|
||||
|
||||
// public int Compare((T, TView) x, (T, TView) y)
|
||||
// {
|
||||
// return comparer.Compare(x.Item1, y.Item1);
|
||||
// }
|
||||
// }
|
||||
class TComparer : IComparer<(T, TView)>
|
||||
{
|
||||
readonly IComparer<T> comparer;
|
||||
|
||||
// class TViewComparer : IComparer<(T, TView)>
|
||||
// {
|
||||
// readonly IComparer<TView> comparer;
|
||||
public TComparer(IComparer<T> comparer)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
// public TViewComparer(IComparer<TView> comparer)
|
||||
// {
|
||||
// this.comparer = comparer;
|
||||
// }
|
||||
public int Compare((T, TView) x, (T, TView) y)
|
||||
{
|
||||
return comparer.Compare(x.Item1, y.Item1);
|
||||
}
|
||||
}
|
||||
|
||||
// public int Compare((T, TView) x, (T, TView) y)
|
||||
// {
|
||||
// return comparer.Compare(x.Item2, y.Item2);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
class TViewComparer : IComparer<(T, TView)>
|
||||
{
|
||||
readonly IComparer<TView> comparer;
|
||||
|
||||
public TViewComparer(IComparer<TView> comparer)
|
||||
{
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
public int Compare((T, TView) x, (T, TView) y)
|
||||
{
|
||||
return comparer.Compare(x.Item2, y.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
@ -13,9 +14,6 @@ namespace ObservableCollections
|
||||
ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false);
|
||||
ISynchronizedView<T, TView> CreateSortedView<TView>(Func<T, TView> transform, IComparer<T> comparer);
|
||||
ISynchronizedView<T, TView> CreateSortedView<TView>(Func<T, TView> transform, IComparer<TView> viewComparer);
|
||||
|
||||
// TODO:Grouping
|
||||
// IGroupedSynchronizedView<T, TKey, TView> CreateGroupedView<TKey, TView>(Func<T, TKey> keySelector, Func<T, TView> transform);
|
||||
}
|
||||
|
||||
public interface IFreezedCollection<T>
|
||||
@ -42,7 +40,7 @@ namespace ObservableCollections
|
||||
void Sort(IComparer<TView> viewComparer);
|
||||
}
|
||||
|
||||
public interface INotifyCollectionChangedSynchronizedView<T, TView> : ISynchronizedView<T, TView>, INotifyCollectionChanged
|
||||
public interface INotifyCollectionChangedSynchronizedView<T, TView> : ISynchronizedView<T, TView>, INotifyCollectionChanged, INotifyPropertyChanged
|
||||
{
|
||||
}
|
||||
}
|
@ -2,12 +2,14 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
{
|
||||
internal class NotifyCollectionChangedSynchronizedView<T, TView> : INotifyCollectionChangedSynchronizedView<T, TView>
|
||||
{
|
||||
readonly ISynchronizedView<T, TView> parent;
|
||||
static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new PropertyChangedEventArgs("Count");
|
||||
|
||||
public NotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent)
|
||||
{
|
||||
@ -18,6 +20,20 @@ namespace ObservableCollections.Internal
|
||||
private void Parent_RoutingCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
|
||||
{
|
||||
CollectionChanged?.Invoke(this, e.ToStandardEventArgs());
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
// add, remove, reset will change the count.
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
PropertyChanged?.Invoke(this, CountPropertyChangedEventArgs);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public object SyncRoot => parent.SyncRoot;
|
||||
@ -25,6 +41,7 @@ namespace ObservableCollections.Internal
|
||||
public int Count => parent.Count;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged
|
||||
{
|
||||
|
46
src/ObservableCollections/Internal/SynchronizedEnumerator.cs
Normal file
46
src/ObservableCollections/Internal/SynchronizedEnumerator.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace ObservableCollections.Internal
|
||||
{
|
||||
internal class SynchronizedEnumerator<T> : IEnumerator<T>
|
||||
{
|
||||
bool isDisposed;
|
||||
readonly object gate;
|
||||
readonly bool lockTaken;
|
||||
readonly IEnumerator<T> enumerator;
|
||||
|
||||
public SynchronizedEnumerator(object gate, IEnumerator<T> enumerator)
|
||||
{
|
||||
this.gate = gate;
|
||||
this.enumerator = enumerator;
|
||||
Monitor.Enter(gate, ref lockTaken);
|
||||
}
|
||||
|
||||
public T Current => enumerator.Current;
|
||||
|
||||
object IEnumerator.Current => Current!;
|
||||
public bool MoveNext() => enumerator.MoveNext();
|
||||
public void Reset() => enumerator.Reset();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
try
|
||||
{
|
||||
enumerator.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
Monitor.Exit(gate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,8 +8,7 @@ namespace ObservableCollections.Internal
|
||||
internal class SynchronizedViewEnumerator<T, TView> : IEnumerator<(T, TView)>, IDisposable
|
||||
{
|
||||
bool isDisposed;
|
||||
bool startEnumerate;
|
||||
bool lockTaken;
|
||||
readonly bool lockTaken;
|
||||
readonly object gate;
|
||||
readonly IEnumerator<(T, TView)> enumerator;
|
||||
readonly ISynchronizedViewFilter<T, TView> filter;
|
||||
@ -22,8 +21,7 @@ namespace ObservableCollections.Internal
|
||||
this.filter = filter;
|
||||
this.current = default;
|
||||
this.isDisposed = false;
|
||||
this.startEnumerate = false;
|
||||
this.lockTaken = false;
|
||||
Monitor.Enter(gate, ref lockTaken);
|
||||
}
|
||||
|
||||
public (T, TView) Current => current;
|
||||
@ -31,12 +29,6 @@ namespace ObservableCollections.Internal
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (!startEnumerate) // TODO: check is this work correctly?
|
||||
{
|
||||
startEnumerate = true;
|
||||
Monitor.Enter(gate, ref lockTaken);
|
||||
}
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
current = enumerator.Current;
|
||||
@ -51,7 +43,7 @@ namespace ObservableCollections.Internal
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (isDisposed)
|
||||
if (!isDisposed)
|
||||
{
|
||||
isDisposed = true;
|
||||
try
|
||||
|
297
src/ObservableCollections/ObservableDictionary.Views.cs
Normal file
297
src/ObservableCollections/ObservableDictionary.Views.cs
Normal file
@ -0,0 +1,297 @@
|
||||
using ObservableCollections.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed partial class ObservableDictionary<TKey, TValue>
|
||||
{
|
||||
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool reverse = false)
|
||||
{
|
||||
// reverse is no used.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<KeyValuePair<TKey, TValue>> comparer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<TView> viewComparer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
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>.AlwaysTrue;
|
||||
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)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
foreach (var v in dict)
|
||||
{
|
||||
filter.Invoke(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFilter(Action<KeyValuePair<TKey, TValue>, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.AlwaysTrue;
|
||||
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()
|
||||
{
|
||||
return new SynchronizedViewEnumerator<KeyValuePair<TKey, TValue>, TView>(SyncRoot,
|
||||
dict.Select(x => (new KeyValuePair<TKey, TValue>(x.Key, x.Value.Item1), x.Value.Item2)).GetEnumerator(),
|
||||
filter);
|
||||
}
|
||||
|
||||
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.Invoke(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
{
|
||||
dict.Remove(e.OldItem.Key);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
{
|
||||
dict.Remove(e.OldItem.Key);
|
||||
var v = selector(e.NewItem);
|
||||
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
|
||||
filter.Invoke(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
{
|
||||
dict.Clear();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
RoutingCollectionChanged?.Invoke(e);
|
||||
CollectionStateChanged?.Invoke(e.Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SortedView<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 SortedDictionary<KeyValuePair<TKey, TValue>, TView> dict;
|
||||
|
||||
public SortedView(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector, IComparer<KeyValuePair<TKey, TValue>> comparer)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.AlwaysTrue;
|
||||
this.SyncRoot = new object();
|
||||
lock (source.SyncRoot)
|
||||
{
|
||||
this.dict = new SortedDictionary<KeyValuePair<TKey, TValue>, TView>(comparer);
|
||||
foreach (var item in source.dictionary)
|
||||
{
|
||||
dict.Add(item, selector(item));
|
||||
}
|
||||
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)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
foreach (var v in dict)
|
||||
{
|
||||
filter.Invoke(new KeyValuePair<TKey, TValue>(v.Key.Key, v.Key.Value), v.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFilter(Action<KeyValuePair<TKey, TValue>, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.AlwaysTrue;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var v in dict)
|
||||
{
|
||||
resetAction(new KeyValuePair<TKey, TValue>(v.Key.Key, v.Key.Value), v.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView> WithINotifyCollectionChanged()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(KeyValuePair<TKey, TValue>, TView)> GetEnumerator()
|
||||
{
|
||||
return new SynchronizedViewEnumerator<KeyValuePair<TKey, TValue>, TView>(SyncRoot,
|
||||
dict.Select(x => (new KeyValuePair<TKey, TValue>(x.Key.Key, x.Key.Value), x.Value)).GetEnumerator(),
|
||||
filter);
|
||||
}
|
||||
|
||||
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);
|
||||
var k = new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value);
|
||||
dict.Add(k, v);
|
||||
filter.Invoke(k, v);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
{
|
||||
dict.Remove(e.OldItem);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
{
|
||||
var k = new KeyValuePair<TKey, TValue>(e.OldItem.Key, e.OldItem.Value);
|
||||
dict.Remove(k);
|
||||
var v = selector(e.NewItem);
|
||||
var nk = new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value);
|
||||
dict.Add(nk, v);
|
||||
filter.Invoke(nk, v);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
{
|
||||
dict.Clear();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
RoutingCollectionChanged?.Invoke(e);
|
||||
CollectionStateChanged?.Invoke(e.Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
206
src/ObservableCollections/ObservableDictionary.cs
Normal file
206
src/ObservableCollections/ObservableDictionary.cs
Normal file
@ -0,0 +1,206 @@
|
||||
using ObservableCollections.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed partial class ObservableDictionary<TKey, TValue>
|
||||
: IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, IObservableCollection<KeyValuePair<TKey, TValue>>
|
||||
where TKey : notnull
|
||||
{
|
||||
readonly Dictionary<TKey, TValue> dictionary;
|
||||
public readonly object SyncRoot = new object();
|
||||
|
||||
public ObservableDictionary()
|
||||
{
|
||||
this.dictionary = new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
public ObservableDictionary(Dictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
this.dictionary = dictionary;
|
||||
}
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>>? CollectionChanged;
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return dictionary[key];
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var oldValue = dictionary[key];
|
||||
dictionary[key] = value;
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Replace(
|
||||
new KeyValuePair<TKey, TValue>(key, value),
|
||||
new KeyValuePair<TKey, TValue>(key, oldValue),
|
||||
-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for lock synchronization, hide keys and values.
|
||||
ICollection<TKey> IDictionary<TKey, TValue>.Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return dictionary.Keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ICollection<TValue> IDictionary<TKey, TValue>.Values
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return dictionary.Values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return dictionary.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return dictionary.Keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return dictionary.Values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
dictionary.Add(key, value);
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Add(new KeyValuePair<TKey, TValue>(key, value), -1));
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
dictionary.Clear();
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Reset());
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return ((ICollection<KeyValuePair<TKey, TValue>>)dictionary).Contains(item);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return ((IDictionary<TKey, TValue>)dictionary).ContainsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
((ICollection<KeyValuePair<TKey, TValue>>)dictionary).CopyTo(array, arrayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (dictionary.Remove(key, out var value))
|
||||
{
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Remove(new KeyValuePair<TKey, TValue>(key, value), -1));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (dictionary.TryGetValue(item.Key, out var value))
|
||||
{
|
||||
if (EqualityComparer<TValue>.Default.Equals(value, item.Value))
|
||||
{
|
||||
if (dictionary.Remove(item.Key, out var value2))
|
||||
{
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Remove(new KeyValuePair<TKey, TValue>(item.Key, value2), -1));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return new SynchronizedEnumerator<KeyValuePair<TKey, TValue>>(SyncRoot, dictionary.GetEnumerator());
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ namespace ObservableCollections
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
public object SyncRoot { get; }
|
||||
|
||||
public View(ObservableList<T> source, Func<T, TView> selector, bool reverse)
|
||||
{
|
||||
@ -47,7 +47,7 @@ namespace ObservableCollections
|
||||
this.SyncRoot = new object();
|
||||
lock (source.SyncRoot)
|
||||
{
|
||||
this.list = source.Select(x => (x, selector(x))).ToList();
|
||||
this.list = source.list.Select(x => (x, selector(x))).ToList();
|
||||
this.source.CollectionChanged += SourceCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -13,6 +12,16 @@ namespace ObservableCollections
|
||||
readonly List<T> list;
|
||||
public readonly object SyncRoot = new object();
|
||||
|
||||
public ObservableList()
|
||||
{
|
||||
list = new List<T>();
|
||||
}
|
||||
|
||||
public ObservableList(IEnumerable<T> source)
|
||||
{
|
||||
list = source.ToList();
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
@ -49,16 +58,6 @@ namespace ObservableCollections
|
||||
public event NotifyCollectionChangedEventHandler<T>? CollectionChanged;
|
||||
|
||||
|
||||
public ObservableList()
|
||||
{
|
||||
list = new List<T>();
|
||||
}
|
||||
|
||||
public ObservableList(IEnumerable<T> source)
|
||||
{
|
||||
list = source.ToList();
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
@ -136,7 +135,7 @@ namespace ObservableCollections
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return list.GetEnumerator();
|
||||
return new SynchronizedEnumerator<T>(SyncRoot, list.GetEnumerator());
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
|
@ -10,5 +10,6 @@ namespace ObservableCollections.Tests
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user