From fa6a843a49b6816565724c67ed61251d12ea5606 Mon Sep 17 00:00:00 2001 From: neuecc Date: Tue, 13 Aug 2024 20:30:44 +0900 Subject: [PATCH] ObservableList.View.ToNotifyCollectionChanged collection implements IList(allow binding for Avalonia) --- .../IObservableCollection.cs | 5 +- ...NotifyCollectionChangedSynchronizedView.cs | 148 ++++++++++++++++++ .../ObservableList.Views.cs | 8 +- 3 files changed, 155 insertions(+), 6 deletions(-) diff --git a/src/ObservableCollections/IObservableCollection.cs b/src/ObservableCollections/IObservableCollection.cs index 1615f2c..3dc4156 100644 --- a/src/ObservableCollections/IObservableCollection.cs +++ b/src/ObservableCollections/IObservableCollection.cs @@ -1,5 +1,6 @@ using ObservableCollections.Internal; using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -15,11 +16,11 @@ namespace ObservableCollections ISynchronizedView CreateView(Func transform, bool reverse = false); } - public interface IReadOnlyObservableDictionary : + public interface IReadOnlyObservableDictionary : IReadOnlyDictionary, IObservableCollection> { } - + public interface IFreezedCollection { ISynchronizedView CreateView(Func transform, bool reverse = false); diff --git a/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs b/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs index 12459f3..777f2d1 100644 --- a/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs +++ b/src/ObservableCollections/Internal/NotifyCollectionChangedSynchronizedView.cs @@ -132,4 +132,152 @@ namespace ObservableCollections.Internal } } } + + internal class ListNotifyCollectionChangedSynchronizedView + : NotifyCollectionChangedSynchronizedView + , IList + , IList + { + readonly ObservableList.View view; + + public ListNotifyCollectionChangedSynchronizedView(ObservableList.View parent, ICollectionEventDispatcher? eventDispatcher) + : base(parent, eventDispatcher) + { + this.view = parent; + } + + public TView this[int index] + { + get + { + lock (view.SyncRoot) + { + return view.list[index].Item2; + } + } + set => throw new NotSupportedException(); + } + + object? IList.this[int index] + { + get + { + return this[index]; + } + set => throw new NotSupportedException(); + } + + static bool IsCompatibleObject(object? value) + { + return (value is T) || (value == null && default(T) == null); + } + + public bool IsReadOnly => true; + + public bool IsFixedSize => false; + + public bool IsSynchronized => true; + + public object SyncRoot => view.SyncRoot; + + public void Add(TView item) + { + throw new NotSupportedException(); + } + + public int Add(object? value) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(TView item) + { + lock (view.SyncRoot) + { + foreach (var listItem in view.list) + { + if (EqualityComparer.Default.Equals(listItem.Item2, item)) + { + return true; + } + } + } + return false; + } + + public bool Contains(object? value) + { + if (IsCompatibleObject(value)) + { + return Contains((TView)value!); + } + return false; + } + + public void CopyTo(TView[] array, int arrayIndex) + { + throw new NotSupportedException(); + } + + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + public int IndexOf(TView item) + { + lock (view.SyncRoot) + { + var index = 0; + foreach (var listItem in view.list) + { + if (EqualityComparer.Default.Equals(listItem.Item2, item)) + { + return index; + } + index++; + } + } + return -1; + } + + public int IndexOf(object? item) + { + if (IsCompatibleObject(item)) + { + return IndexOf((TView)item!); + } + return -1; + } + + public void Insert(int index, TView item) + { + throw new NotSupportedException(); + } + + public void Insert(int index, object? value) + { + throw new NotImplementedException(); + } + + public bool Remove(TView item) + { + throw new NotSupportedException(); + } + + public void Remove(object? value) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + } } \ No newline at end of file diff --git a/src/ObservableCollections/ObservableList.Views.cs b/src/ObservableCollections/ObservableList.Views.cs index ec89f9c..41efaab 100644 --- a/src/ObservableCollections/ObservableList.Views.cs +++ b/src/ObservableCollections/ObservableList.Views.cs @@ -14,7 +14,7 @@ namespace ObservableCollections return new View(this, transform, reverse); } - sealed class View : ISynchronizedView + internal sealed class View : ISynchronizedView { public ISynchronizedViewFilter CurrentFilter { @@ -27,7 +27,7 @@ namespace ObservableCollections readonly ObservableList source; readonly Func selector; readonly bool reverse; - readonly List<(T, TView)> list; + internal readonly List<(T, TView)> list; // be careful to use ISynchronizedViewFilter filter; @@ -100,7 +100,7 @@ namespace ObservableCollections { lock (SyncRoot) { - return new NotifyCollectionChangedSynchronizedView(this, null); + return new ListNotifyCollectionChangedSynchronizedView(this, null); } } @@ -108,7 +108,7 @@ namespace ObservableCollections { lock (SyncRoot) { - return new NotifyCollectionChangedSynchronizedView(this, collectionEventDispatcher); + return new ListNotifyCollectionChangedSynchronizedView(this, collectionEventDispatcher); } }