From 290b455c470326f367d57322db3b9dc70eec0f61 Mon Sep 17 00:00:00 2001 From: zerodev1200 <42404360+zerodev1200@users.noreply.github.com> Date: Thu, 17 Oct 2024 23:07:51 +0900 Subject: [PATCH 1/5] INotifyCollectionChangedSynchronizedViewList To NotifyCollectionChangedSynchronizedViewList --- src/ObservableCollections/IObservableCollection.cs | 8 ++++---- src/ObservableCollections/ObservableList.Views.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ObservableCollections/IObservableCollection.cs b/src/ObservableCollections/IObservableCollection.cs index 9134cbf..2c8691b 100644 --- a/src/ObservableCollections/IObservableCollection.cs +++ b/src/ObservableCollections/IObservableCollection.cs @@ -176,22 +176,22 @@ public static ISynchronizedViewList ToViewList(this IObservable return new NonFilteredSynchronizedViewList(collection.CreateView(transform), isSupportRangeFeature: true, null, null); } - public static INotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection) + public static NotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection) { return ToNotifyCollectionChanged(collection, null); } - public static INotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection, ICollectionEventDispatcher? collectionEventDispatcher) + public static NotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection, ICollectionEventDispatcher? collectionEventDispatcher) { return ToNotifyCollectionChanged(collection, static x => x, collectionEventDispatcher); } - public static INotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection, Func transform) + public static NotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection, Func transform) { return ToNotifyCollectionChanged(collection, transform, null!); } - public static INotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection, Func transform, ICollectionEventDispatcher? collectionEventDispatcher) + public static NotifyCollectionChangedSynchronizedViewList ToNotifyCollectionChanged(this IObservableCollection collection, Func transform, ICollectionEventDispatcher? collectionEventDispatcher) { // Optimized for non filtered return new NonFilteredSynchronizedViewList(collection.CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, null); diff --git a/src/ObservableCollections/ObservableList.Views.cs b/src/ObservableCollections/ObservableList.Views.cs index f4e35f2..7581db2 100644 --- a/src/ObservableCollections/ObservableList.Views.cs +++ b/src/ObservableCollections/ObservableList.Views.cs @@ -19,12 +19,12 @@ public IWritableSynchronizedView CreateWritableView(Func(this, transform); } - public INotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged() + public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged() { return ToWritableNotifyCollectionChanged(null); } - public INotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher) + public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher) { return ToWritableNotifyCollectionChanged( static x => x, @@ -36,12 +36,12 @@ public INotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectio collectionEventDispatcher); } - public INotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(Func transform, WritableViewChangedEventHandler? converter) + public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(Func transform, WritableViewChangedEventHandler? converter) { return ToWritableNotifyCollectionChanged(transform, converter, null!); } - public INotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(Func transform, WritableViewChangedEventHandler? converter, ICollectionEventDispatcher? collectionEventDispatcher) + public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(Func transform, WritableViewChangedEventHandler? converter, ICollectionEventDispatcher? collectionEventDispatcher) { return new NonFilteredSynchronizedViewList(CreateView(transform), isSupportRangeFeature: false, collectionEventDispatcher, converter); } From 367be8717cae540f5d3bdcdc0b416863eff7874a Mon Sep 17 00:00:00 2001 From: zerodev1200 <42404360+zerodev1200@users.noreply.github.com> Date: Thu, 17 Oct 2024 23:20:29 +0900 Subject: [PATCH 2/5] add overload WritableFiltable ToWritableNotifyCollectionChanged() --- src/ObservableCollections/IObservableCollection.cs | 1 + src/ObservableCollections/ObservableList.Views.cs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/ObservableCollections/IObservableCollection.cs b/src/ObservableCollections/IObservableCollection.cs index 2c8691b..9394f88 100644 --- a/src/ObservableCollections/IObservableCollection.cs +++ b/src/ObservableCollections/IObservableCollection.cs @@ -58,6 +58,7 @@ public interface IWritableSynchronizedView : ISynchronizedView ToWritableViewList(WritableViewChangedEventHandler converter); + NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(); NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler converter); NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher); NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler converter, ICollectionEventDispatcher? collectionEventDispatcher); diff --git a/src/ObservableCollections/ObservableList.Views.cs b/src/ObservableCollections/ObservableList.Views.cs index 7581db2..08fa810 100644 --- a/src/ObservableCollections/ObservableList.Views.cs +++ b/src/ObservableCollections/ObservableList.Views.cs @@ -373,6 +373,17 @@ public IWritableSynchronizedViewList ToWritableViewList(WritableViewChang return new FiltableSynchronizedViewList(this, isSupportRangeFeature: true, converter: converter); } + public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged() + { + return new FiltableSynchronizedViewList(this, + isSupportRangeFeature: false, + converter: static (TView newView, T originalValue, ref bool setValue) => + { + setValue = true; + return originalValue; + }); + } + public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler converter) { return new FiltableSynchronizedViewList(this, isSupportRangeFeature: false, converter: converter); 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 3/5] 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 @@ public interface IWritableSynchronizedView : ISynchronizedView ToWritableViewList(WritableViewChangedEventHandler converter); NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(); NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler converter); @@ -100,8 +104,8 @@ public abstract class NotifyCollectionChangedSynchronizedViewList : } 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 @@ public abstract class NotifyCollectionChangedSynchronizedViewList : 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 @@ static bool IsCompatibleObject(object? value) 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 @@ public static NotifyCollectionChangedSynchronizedViewList ToNotifyCollect 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 @@ public override void Add(T item) { 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 @@ public override int IndexOf(T item) { 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 @@ public void AddToSourceCollection(T value) 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 @@ public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollec 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 @@ public NotifyCollectionChangedSynchronizedViewList ToWritableNotifyCollec { 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 @@ public override TView this[int index] } 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 @@ public override int Count } } + public override bool IsReadOnly => converter == null || parent is not IWritableSynchronizedView; + public override IEnumerator GetEnumerator() { lock (gate) @@ -385,17 +388,91 @@ public override IEnumerator GetEnumerator() 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 @@ public override TView this[int index] } 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 @@ public override int Count } } + public override bool IsReadOnly => converter == null || parent is not IWritableSynchronizedView; + public override IEnumerator GetEnumerator() { lock (gate) @@ -809,17 +889,91 @@ public override IEnumerator GetEnumerator() 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); + + // always add + 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); + var newOriginal = converter!(item, default!, ref setValue); // always add - writableView.AddToSourceCollection(newOriginal); + 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 @@ public override void Dispose() parent.ViewChanged -= Parent_ViewChanged; parent.Dispose(); // Dispose parent } -} \ No newline at end of file +} From e7e914501155b7a53524cf715b7fdbba83b8752e Mon Sep 17 00:00:00 2001 From: zerodev1200 <42404360+zerodev1200@users.noreply.github.com> Date: Thu, 17 Oct 2024 23:28:25 +0900 Subject: [PATCH 4/5] add four DataGrid in sandbox-WpfApp --- sandbox/WpfApp/MainWindow.xaml | 77 ++++++++++++++++------- sandbox/WpfApp/MainWindow.xaml.cs | 101 ++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 22 deletions(-) diff --git a/sandbox/WpfApp/MainWindow.xaml b/sandbox/WpfApp/MainWindow.xaml index e9d7411..79ac6a2 100644 --- a/sandbox/WpfApp/MainWindow.xaml +++ b/sandbox/WpfApp/MainWindow.xaml @@ -5,29 +5,62 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp" mc:Ignorable="d" - Title="MainWindow" Height="450" Width="800"> - + + +