From 62b959c2c2a55aa32e2b0aae93bd951d80df8eb0 Mon Sep 17 00:00:00 2001 From: zerodev1200 <42404360+zerodev1200@users.noreply.github.com> Date: Thu, 17 Oct 2024 23:26:52 +0900 Subject: [PATCH] add Insert, Remove, RemoveAt, Clear, other --- .../IObservableCollection.cs | 57 +++++- .../ObservableList.OptimizeView.cs | 21 +- .../ObservableList.Views.cs | 51 ++++- .../SynchronizedViewList.cs | 188 ++++++++++++++++-- 4 files changed, 278 insertions(+), 39 deletions(-) diff --git a/src/ObservableCollections/IObservableCollection.cs b/src/ObservableCollections/IObservableCollection.cs index 9394f88..8dd232a 100644 --- a/src/ObservableCollections/IObservableCollection.cs +++ b/src/ObservableCollections/IObservableCollection.cs @@ -57,6 +57,10 @@ namespace ObservableCollections void SetViewAt(int index, TView view); void SetToSourceCollection(int index, T value); void AddToSourceCollection(T value); + void InsertIntoSourceCollection(int index, T value); + bool RemoveFromSourceCollection(T value); + void RemoveAtSourceCollection(int index); + void ClearSourceCollection(); IWritableSynchronizedViewList ToWritableViewList(WritableViewChangedEventHandler converter); NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(); NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler converter); @@ -100,8 +104,8 @@ namespace ObservableCollections } public abstract int Count { get; } - public bool IsReadOnly => false; - public bool IsFixedSize => false; + public virtual bool IsReadOnly { get; } = true; + public bool IsFixedSize => IsReadOnly; public bool IsSynchronized => true; public object SyncRoot => gate; @@ -113,9 +117,14 @@ namespace ObservableCollections int IList.Add(object? value) { Add((TView)value!); - return -1; // itself does not add in this collection + return Count - 1; } + public abstract void Insert(int index, TView item); + public abstract bool Remove(TView item); + public abstract void RemoveAt(int index); + public abstract void Clear(); + public abstract bool Contains(TView item); bool IList.Contains(object? value) @@ -150,17 +159,43 @@ namespace ObservableCollections return value is TView || value == null && default(TView) == null; } - void ICollection.Clear() => throw new NotSupportedException(); - void IList.Clear() => throw new NotSupportedException(); + void ICollection.Clear() + { + Clear(); + } + void IList.Clear() + { + Clear(); + } void ICollection.CopyTo(TView[] array, int arrayIndex) => throw new NotSupportedException(); void ICollection.CopyTo(Array array, int index) => throw new NotSupportedException(); - void IList.Insert(int index, TView item) => throw new NotSupportedException(); - void IList.Insert(int index, object? value) => throw new NotSupportedException(); - bool ICollection.Remove(TView item) => throw new NotSupportedException(); - void IList.Remove(object? value) => throw new NotSupportedException(); - void IList.RemoveAt(int index) => throw new NotSupportedException(); + void IList.Insert(int index, TView item) + { + Insert(index, item); + } + + void IList.Insert(int index, object? value) + { + Insert(index, (TView)value!); + } + + bool ICollection.Remove(TView item) + { + return Remove(item!); + } + + void IList.Remove(object? value) + { + Remove((TView)value!); + } + + void IList.RemoveAt(int index) + { + RemoveAt(index); + } + void IList.RemoveAt(int index) => throw new NotSupportedException(); } @@ -198,4 +233,4 @@ namespace ObservableCollections return new NonFilteredSynchronizedViewList(collection.CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, null); } } -} \ No newline at end of file +} diff --git a/src/ObservableCollections/ObservableList.OptimizeView.cs b/src/ObservableCollections/ObservableList.OptimizeView.cs index b2a6df6..af48818 100644 --- a/src/ObservableCollections/ObservableList.OptimizeView.cs +++ b/src/ObservableCollections/ObservableList.OptimizeView.cs @@ -189,6 +189,25 @@ internal sealed class ObservableListSynchronizedViewList : NotifyCollectionCh { parent.Add(item); } + public override void Insert(int index, T item) + { + parent.Insert(index, item); + } + + public override bool Remove(T item) + { + return parent.Remove(item); + } + + public override void RemoveAt(int index) + { + parent.RemoveAt(index); + } + + public override void Clear() + { + parent.Clear(); + } public override bool Contains(T item) { @@ -199,4 +218,4 @@ internal sealed class ObservableListSynchronizedViewList : NotifyCollectionCh { return parent.IndexOf(item); } -} \ No newline at end of file +} diff --git a/src/ObservableCollections/ObservableList.Views.cs b/src/ObservableCollections/ObservableList.Views.cs index 08fa810..e3b9498 100644 --- a/src/ObservableCollections/ObservableList.Views.cs +++ b/src/ObservableCollections/ObservableList.Views.cs @@ -367,6 +367,37 @@ namespace ObservableCollections source.Add(value); } } + public void InsertIntoSourceCollection(int index, T value) + { + lock (SyncRoot) + { + source.Insert(index, value); + } + } + + public bool RemoveFromSourceCollection(T value) + { + lock (SyncRoot) + { + return source.Remove(value); + } + } + + public void RemoveAtSourceCollection(int index) + { + lock (SyncRoot) + { + source.RemoveAt(index); + } + } + + public void ClearSourceCollection() + { + lock (SyncRoot) + { + source.Clear(); + } + } public IWritableSynchronizedViewList ToWritableViewList(WritableViewChangedEventHandler converter) { @@ -378,10 +409,10 @@ namespace ObservableCollections return new FiltableSynchronizedViewList(this, isSupportRangeFeature: false, converter: static (TView newView, T originalValue, ref bool setValue) => - { - setValue = true; - return originalValue; - }); + { + setValue = true; + return originalValue; + }); } public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler converter) @@ -393,12 +424,12 @@ namespace ObservableCollections { return new FiltableSynchronizedViewList(this, isSupportRangeFeature: false, - collectionEventDispatcher, - static (TView newView, T originalValue, ref bool setValue) => - { - setValue = true; - return originalValue; - }); + eventDispatcher: collectionEventDispatcher, + converter: static (TView newView, T originalValue, ref bool setValue) => + { + setValue = true; + return originalValue; + }); } public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler converter, ICollectionEventDispatcher? collectionEventDispatcher) diff --git a/src/ObservableCollections/SynchronizedViewList.cs b/src/ObservableCollections/SynchronizedViewList.cs index e6c6c2d..c936b2c 100644 --- a/src/ObservableCollections/SynchronizedViewList.cs +++ b/src/ObservableCollections/SynchronizedViewList.cs @@ -337,21 +337,22 @@ internal sealed class FiltableSynchronizedViewList : NotifyCollectionC } set { - if (converter == null || parent is not IWritableSynchronizedView writableView) + if (IsReadOnly) { - throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged."); + throw new NotSupportedException("This CollectionView does not support Set. If base type is ObservableList, you can use CreateWritableView and ToWritableNotifyCollectionChanged."); } else { + var writableView = parent as IWritableSynchronizedView; var originalIndex = listView.GetAlternateIndex(index); - var (originalValue, _) = writableView.GetAt(originalIndex); + var (originalValue, _) = writableView!.GetAt(originalIndex); // update view writableView.SetViewAt(originalIndex, value); listView[index] = value; var setValue = true; - var newOriginal = converter(value, originalValue, ref setValue); + var newOriginal = converter!(value, originalValue, ref setValue); if (setValue) { @@ -372,6 +373,8 @@ internal sealed class FiltableSynchronizedViewList : NotifyCollectionC } } + public override bool IsReadOnly => converter == null || parent is not IWritableSynchronizedView; + public override IEnumerator GetEnumerator() { lock (gate) @@ -385,17 +388,91 @@ internal sealed class FiltableSynchronizedViewList : NotifyCollectionC public override void Add(TView item) { - if (converter == null || parent is not IWritableSynchronizedView writableView) + if (IsReadOnly) { - throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged."); + throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList, you can use CreateWritableView and ToWritableNotifyCollectionChanged."); } else { + var writableView = parent as IWritableSynchronizedView; + if (typeof(T) == typeof(TView) && item is T tItem) + { + writableView!.AddToSourceCollection(tItem); + return; + } var setValue = false; - var newOriginal = converter(item, default!, ref setValue); + var newOriginal = converter!(item, default!, ref setValue); // always add - writableView.AddToSourceCollection(newOriginal); + writableView!.AddToSourceCollection(newOriginal); + } + } + + public override void Insert(int index, TView item) + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support Insert. If base type is ObservableList, you can use CreateWritableView and ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + if (typeof(T) == typeof(TView) && item is T tItem) + { + writableView!.InsertIntoSourceCollection(index, tItem); + return; + } + var setValue = false; + var newOriginal = converter!(item, default!, ref setValue); + + writableView!.InsertIntoSourceCollection(index, newOriginal); + } + } + + public override bool Remove(TView item) + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support Remove. If base type is ObservableList, you can use CreateWritableView and ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + if (typeof(T) == typeof(TView) && item is T tItem) + { + return writableView!.RemoveFromSourceCollection(tItem); + } + var setValue = false; + var newOriginal = converter!(item, default!, ref setValue); + + // always add + return writableView!.RemoveFromSourceCollection(newOriginal); + } + } + + public override void RemoveAt(int index) + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support RemoveAt. If base type is ObservableList, you can use CreateWritableView and ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + writableView!.RemoveAtSourceCollection(index); + } + } + + public override void Clear() + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support Clear. If base type is ObservableList, you can use CreateWritableView and ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + writableView!.ClearSourceCollection(); } } @@ -762,20 +839,21 @@ internal sealed class NonFilteredSynchronizedViewList : NotifyCollecti } set { - if (converter == null || parent is not IWritableSynchronizedView writableView) + if (IsReadOnly) { - throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged."); + throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList, you can use ToWritableNotifyCollectionChanged."); } else { - var (originalValue, _) = writableView.GetAt(index); + var writableView = parent as IWritableSynchronizedView; + var (originalValue, _) = writableView!.GetAt(index); // update view writableView.SetViewAt(index, value); listView[index] = value; var setValue = true; - var newOriginal = converter(value, originalValue, ref setValue); + var newOriginal = converter!(value, originalValue, ref setValue); if (setValue) { @@ -796,6 +874,8 @@ internal sealed class NonFilteredSynchronizedViewList : NotifyCollecti } } + public override bool IsReadOnly => converter == null || parent is not IWritableSynchronizedView; + public override IEnumerator GetEnumerator() { lock (gate) @@ -809,17 +889,91 @@ internal sealed class NonFilteredSynchronizedViewList : NotifyCollecti public override void Add(TView item) { - if (converter == null || parent is not IWritableSynchronizedView writableView) + if (IsReadOnly) { - throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged."); + throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList, you can use ToWritableNotifyCollectionChanged."); } else { + var writableView = parent as IWritableSynchronizedView; + if (typeof(T) == typeof(TView) && item is T tItem) + { + writableView!.AddToSourceCollection(tItem); + return; + } var setValue = false; - var newOriginal = converter(item, default!, ref setValue); + var newOriginal = converter!(item, default!, ref setValue); // always add - writableView.AddToSourceCollection(newOriginal); + writableView!.AddToSourceCollection(newOriginal); + } + } + + public override void Insert(int index, TView item) + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support Insert. If base type is ObservableList, you can use ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + if (typeof(T) == typeof(TView) && item is T tItem) + { + writableView!.InsertIntoSourceCollection(index, tItem); + return; + } + var setValue = false; + var newOriginal = converter!(item, default!, ref setValue); + + writableView!.InsertIntoSourceCollection(index, newOriginal); + } + } + + public override bool Remove(TView item) + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support Remove. If base type is ObservableList, you can use ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + if (typeof(T) == typeof(TView) && item is T tItem) + { + return writableView!.RemoveFromSourceCollection(tItem); + } + var setValue = false; + var newOriginal = converter!(item, default!, ref setValue); + + // always add + return writableView!.RemoveFromSourceCollection(newOriginal); + } + } + + public override void RemoveAt(int index) + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support RemoveAt. If base type is ObservableList, you can use ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + writableView!.RemoveAtSourceCollection(index); + } + } + + public override void Clear() + { + if (IsReadOnly) + { + throw new NotSupportedException("This CollectionView does not support Clear. If base type is ObservableList, you can use ToWritableNotifyCollectionChanged."); + } + else + { + var writableView = parent as IWritableSynchronizedView; + writableView!.ClearSourceCollection(); } } @@ -860,4 +1014,4 @@ internal sealed class NonFilteredSynchronizedViewList : NotifyCollecti parent.ViewChanged -= Parent_ViewChanged; parent.Dispose(); // Dispose parent } -} \ No newline at end of file +}