add writableview
This commit is contained in:
parent
14893136e5
commit
4cdbe8ce34
56
README.md
56
README.md
@ -359,6 +359,31 @@ public class WpfDispatcherCollection(Dispatcher dispatcher) : ICollectionEventDi
|
|||||||
|
|
||||||
Views and ToNotifyCollectionChanged are internally connected by events, so they need to be `Dispose` to release those connections.
|
Views and ToNotifyCollectionChanged are internally connected by events, so they need to be `Dispose` to release those connections.
|
||||||
|
|
||||||
|
Standard Views are readonly. If you want to reflect the results of binding back to the original collection, use `CreateWritableView` to generate an `IWritableSynchronizedView`, and then use `ToWritableNotifyCollectionChanged` to create an `INotifyCollectionChanged` collection from it.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public delegate T WritableViewChangedEventHandler<T, TView>(TView newView, T originalValue, ref bool setValue);
|
||||||
|
|
||||||
|
public interface IWritableSynchronizedView<T, TView> : ISynchronizedView<T, TView>
|
||||||
|
{
|
||||||
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`ToWritableNotifyCollectionChanged` accepts a delegate called `WritableViewChangedEventHandler`. `newView` receives the newly bound value. If `setValue` is true, it sets a new value to the original collection, triggering notification propagation. The View is also regenerated. If `T originalValue` is a reference type, you can prevent such propagation by setting `setValue` to `false`.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var list = new ObservableList<int>();
|
||||||
|
var view = list.CreateWritableView(x => x.ToString());
|
||||||
|
view.AttachFilter(x => x % 2 == 0);
|
||||||
|
IList<string> notify = view.ToWritableNotifyCollectionChanged((string newView, int originalValue, ref bool setValue) =>
|
||||||
|
{
|
||||||
|
setValue = true; // or false
|
||||||
|
return int.Parse(newView);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
Unity
|
Unity
|
||||||
---
|
---
|
||||||
In Unity projects, you can installing `ObservableCollections` with [NugetForUnity](https://github.com/GlitchEnzo/NuGetForUnity). If R3 integration is required, similarly install `ObservableCollections.R3` via NuGetForUnity.
|
In Unity projects, you can installing `ObservableCollections` with [NugetForUnity](https://github.com/GlitchEnzo/NuGetForUnity). If R3 integration is required, similarly install `ObservableCollections.R3` via NuGetForUnity.
|
||||||
@ -553,6 +578,37 @@ public static class SynchronizedViewExtensions
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`ObservableList<T>` has writable view.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public sealed partial class ObservableList<T>
|
||||||
|
{
|
||||||
|
public IWritableSynchronizedView<T, TView> CreateWritableView<TView>(Func<T, TView> transform);
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged();
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, WritableViewChangedEventHandler<T, TView>? converter);
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, ICollectionEventDispatcher? collectionEventDispatcher, WritableViewChangedEventHandler<T, TView>? converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate T WritableViewChangedEventHandler<T, TView>(TView newView, T originalValue, ref bool setValue);
|
||||||
|
|
||||||
|
public interface IWritableSynchronizedView<T, TView> : ISynchronizedView<T, TView>
|
||||||
|
{
|
||||||
|
(T Value, TView View) GetAt(int index);
|
||||||
|
void SetViewAt(int index, TView view);
|
||||||
|
void SetToSourceCollection(int index, T value);
|
||||||
|
IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IWritableSynchronizedViewList<TView> : ISynchronizedViewList<TView>
|
||||||
|
{
|
||||||
|
new TView this[int index] { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Here are definitions for other collections:
|
Here are definitions for other collections:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
@ -5,23 +5,30 @@ using System.Linq;
|
|||||||
using ObservableCollections;
|
using ObservableCollections;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks.Sources;
|
||||||
|
|
||||||
|
var l = new ObservableList<int>();
|
||||||
|
var view = l.CreateWritableView(x => x.ToString());
|
||||||
|
view.AttachFilter(x => x % 2 == 0);
|
||||||
|
IList<string> notify = view.ToWritableNotifyCollectionChanged((string newView, int originalValue, ref bool setValue) =>
|
||||||
|
{
|
||||||
|
setValue = false;
|
||||||
|
return int.Parse(newView);
|
||||||
|
});
|
||||||
|
|
||||||
var dict = new ObservableDictionary<int, string>();
|
l.Add(0);
|
||||||
var view = dict.CreateView(x => x);
|
l.Add(1);
|
||||||
view.AttachFilter(x => x.Key == 1);
|
l.Add(2);
|
||||||
|
l.Add(3);
|
||||||
|
l.Add(4);
|
||||||
|
l.Add(5);
|
||||||
|
|
||||||
var view2 = view.ToNotifyCollectionChanged();
|
notify[1] = "99999";
|
||||||
dict.Add(key: 1, value: "foo");
|
|
||||||
dict.Add(key: 2, value: "bar");
|
|
||||||
|
|
||||||
foreach (var item in view2)
|
foreach (var item in view)
|
||||||
{
|
{
|
||||||
Console.WriteLine(item);
|
Console.WriteLine(item);
|
||||||
}
|
}
|
||||||
Console.WriteLine("---");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//var buffer = new ObservableFixedSizeRingBuffer<int>(5);
|
//var buffer = new ObservableFixedSizeRingBuffer<int>(5);
|
||||||
|
@ -35,8 +35,11 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
public T this[int index]
|
public T this[int index]
|
||||||
{
|
{
|
||||||
get => list[index].Value;
|
get => list[index].Value;
|
||||||
|
set => CollectionsMarshal.AsSpan(list)[index].Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetAlternateIndex(int index) => list[index].AlternateIndex;
|
||||||
|
|
||||||
public int Count => list.Count;
|
public int Count => list.Count;
|
||||||
|
|
||||||
public int Insert(int alternateIndex, T value)
|
public int Insert(int alternateIndex, T value)
|
||||||
@ -127,6 +130,7 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>NOTE: when replace successfully, list has been sorted.</summary>
|
||||||
public bool TryReplaceAlternateIndex(int getAlternateIndex, int setAlternateIndex)
|
public bool TryReplaceAlternateIndex(int getAlternateIndex, int setAlternateIndex)
|
||||||
{
|
{
|
||||||
var index = list.BinarySearch(getAlternateIndex);
|
var index = list.BinarySearch(getAlternateIndex);
|
||||||
@ -137,6 +141,7 @@ public class AlternateIndexList<T> : IEnumerable<T>
|
|||||||
|
|
||||||
var span = CollectionsMarshal.AsSpan(list);
|
var span = CollectionsMarshal.AsSpan(list);
|
||||||
span[index].AlternateIndex = setAlternateIndex;
|
span[index].AlternateIndex = setAlternateIndex;
|
||||||
|
list.Sort(); // needs sort to keep order
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@ -7,6 +8,7 @@ namespace ObservableCollections
|
|||||||
{
|
{
|
||||||
public delegate void NotifyCollectionChangedEventHandler<T>(in NotifyCollectionChangedEventArgs<T> e);
|
public delegate void NotifyCollectionChangedEventHandler<T>(in NotifyCollectionChangedEventArgs<T> e);
|
||||||
public delegate void NotifyViewChangedEventHandler<T, TView>(in SynchronizedViewChangedEventArgs<T, TView> e);
|
public delegate void NotifyViewChangedEventHandler<T, TView>(in SynchronizedViewChangedEventArgs<T, TView> e);
|
||||||
|
public delegate T WritableViewChangedEventHandler<T, TView>(TView newView, T originalValue, ref bool setValue);
|
||||||
|
|
||||||
public interface IObservableCollection<T> : IReadOnlyCollection<T>
|
public interface IObservableCollection<T> : IReadOnlyCollection<T>
|
||||||
{
|
{
|
||||||
@ -49,11 +51,26 @@ namespace ObservableCollections
|
|||||||
INotifyCollectionChangedSynchronizedViewList<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
INotifyCollectionChangedSynchronizedViewList<TView> ToNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IWritableSynchronizedView<T, TView> : ISynchronizedView<T, TView>
|
||||||
|
{
|
||||||
|
(T Value, TView View) GetAt(int index);
|
||||||
|
void SetViewAt(int index, TView view);
|
||||||
|
void SetToSourceCollection(int index, T value);
|
||||||
|
IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
public interface ISynchronizedViewList<out TView> : IReadOnlyList<TView>, IDisposable
|
public interface ISynchronizedViewList<out TView> : IReadOnlyList<TView>, IDisposable
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface INotifyCollectionChangedSynchronizedViewList<out TView> : ISynchronizedViewList<TView>, INotifyCollectionChanged, INotifyPropertyChanged
|
public interface IWritableSynchronizedViewList<TView> : ISynchronizedViewList<TView>
|
||||||
|
{
|
||||||
|
new TView this[int index] { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface INotifyCollectionChangedSynchronizedViewList<TView> : IList<TView>, IList, ISynchronizedViewList<TView>, INotifyCollectionChanged, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,36 @@ namespace ObservableCollections
|
|||||||
return new View<TView>(this, transform);
|
return new View<TView>(this, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class View<TView> : ISynchronizedView<T, TView>
|
public IWritableSynchronizedView<T, TView> CreateWritableView<TView>(Func<T, TView> transform)
|
||||||
|
{
|
||||||
|
return new View<TView>(this, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged()
|
||||||
|
{
|
||||||
|
return ToWritableNotifyCollectionChanged(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<T> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
|
{
|
||||||
|
return ToWritableNotifyCollectionChanged(static x => x, collectionEventDispatcher, static (T newView, T originalValue, ref bool setValue) =>
|
||||||
|
{
|
||||||
|
setValue = true;
|
||||||
|
return newView;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, WritableViewChangedEventHandler<T, TView>? converter)
|
||||||
|
{
|
||||||
|
return ToWritableNotifyCollectionChanged(transform, null!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged<TView>(Func<T, TView> transform, ICollectionEventDispatcher? collectionEventDispatcher, WritableViewChangedEventHandler<T, TView>? converter)
|
||||||
|
{
|
||||||
|
return new NonFilteredNotifyCollectionChangedSynchronizedViewList<T, TView>(CreateView(transform), collectionEventDispatcher, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class View<TView> : ISynchronizedView<T, TView>, IWritableSynchronizedView<T, TView>
|
||||||
{
|
{
|
||||||
public ISynchronizedViewFilter<T> Filter
|
public ISynchronizedViewFilter<T> Filter
|
||||||
{
|
{
|
||||||
@ -301,6 +330,50 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Writable
|
||||||
|
|
||||||
|
public (T Value, TView View) GetAt(int index)
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
return list[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetViewAt(int index, TView view)
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
var v = list[index];
|
||||||
|
list[index] = (v.Item1, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetToSourceCollection(int index, T value)
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
source[index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter)
|
||||||
|
{
|
||||||
|
return new FiltableWritableSynchronizedViewList<T, TView>(this, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter)
|
||||||
|
{
|
||||||
|
return new NotifyCollectionChangedSynchronizedViewList<T, TView>(this, null, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter, ICollectionEventDispatcher? collectionEventDispatcher)
|
||||||
|
{
|
||||||
|
return new NotifyCollectionChangedSynchronizedViewList<T, TView>(this, null, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
sealed class IgnoreViewComparer : IComparer<(T, TView)>
|
sealed class IgnoreViewComparer : IComparer<(T, TView)>
|
||||||
{
|
{
|
||||||
readonly IComparer<T> comparer;
|
readonly IComparer<T> comparer;
|
||||||
|
@ -13,7 +13,7 @@ namespace ObservableCollections;
|
|||||||
|
|
||||||
internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
||||||
{
|
{
|
||||||
readonly ISynchronizedView<T, TView> parent;
|
protected readonly ISynchronizedView<T, TView> parent;
|
||||||
protected readonly AlternateIndexList<TView> listView;
|
protected readonly AlternateIndexList<TView> listView;
|
||||||
protected readonly object gate = new object();
|
protected readonly object gate = new object();
|
||||||
|
|
||||||
@ -211,7 +211,11 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
|
|||||||
break;
|
break;
|
||||||
case RejectedViewChangedAction.Move:
|
case RejectedViewChangedAction.Move:
|
||||||
if (oldIndex == -1) return;
|
if (oldIndex == -1) return;
|
||||||
listView.TryReplaceAlternateIndex(oldIndex, index);
|
if (listView.TryReplaceAlternateIndex(oldIndex, index))
|
||||||
|
{
|
||||||
|
// replace alternate-index changes order so needs Reset
|
||||||
|
OnCollectionChanged(new SynchronizedViewChangedEventArgs<T, TView>(NotifyCollectionChangedAction.Reset, true));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -270,7 +274,7 @@ internal class FiltableSynchronizedViewList<T, TView> : ISynchronizedViewList<TV
|
|||||||
|
|
||||||
internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList<TView>
|
||||||
{
|
{
|
||||||
readonly ISynchronizedView<T, TView> parent;
|
protected readonly ISynchronizedView<T, TView> parent;
|
||||||
protected readonly List<TView> listView; // no filter can be faster
|
protected readonly List<TView> listView; // no filter can be faster
|
||||||
protected readonly object gate = new object();
|
protected readonly object gate = new object();
|
||||||
|
|
||||||
@ -523,6 +527,43 @@ internal class NonFilteredSynchronizedViewList<T, TView> : ISynchronizedViewList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class FiltableWritableSynchronizedViewList<T, TView> : FiltableSynchronizedViewList<T, TView>, IWritableSynchronizedViewList<TView>
|
||||||
|
{
|
||||||
|
IWritableSynchronizedView<T, TView> writableView;
|
||||||
|
WritableViewChangedEventHandler<T, TView> converter;
|
||||||
|
|
||||||
|
public FiltableWritableSynchronizedViewList(IWritableSynchronizedView<T, TView> parent, WritableViewChangedEventHandler<T, TView> converter) : base(parent)
|
||||||
|
{
|
||||||
|
this.writableView = parent;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new TView this[int index]
|
||||||
|
{
|
||||||
|
get => base[index];
|
||||||
|
set
|
||||||
|
{
|
||||||
|
lock (gate)
|
||||||
|
{
|
||||||
|
var originalIndex = listView.GetAlternateIndex(index);
|
||||||
|
var (originalValue, _) = writableView.GetAt(originalIndex);
|
||||||
|
|
||||||
|
// update view
|
||||||
|
writableView.SetViewAt(originalIndex, value);
|
||||||
|
listView[index] = value;
|
||||||
|
|
||||||
|
var setValue = true;
|
||||||
|
var newOriginal = converter(value, originalValue, ref setValue);
|
||||||
|
|
||||||
|
if (setValue)
|
||||||
|
{
|
||||||
|
writableView.SetToSourceCollection(index, newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
||||||
FiltableSynchronizedViewList<T, TView>,
|
FiltableSynchronizedViewList<T, TView>,
|
||||||
INotifyCollectionChangedSynchronizedViewList<TView>,
|
INotifyCollectionChangedSynchronizedViewList<TView>,
|
||||||
@ -532,6 +573,7 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent;
|
static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent;
|
||||||
|
|
||||||
readonly ICollectionEventDispatcher eventDispatcher;
|
readonly ICollectionEventDispatcher eventDispatcher;
|
||||||
|
WritableViewChangedEventHandler<T, TView>? converter; // null = readonly
|
||||||
|
|
||||||
protected override bool IsSupportRangeFeature => false; // WPF, Avalonia etc does not support range notification
|
protected override bool IsSupportRangeFeature => false; // WPF, Avalonia etc does not support range notification
|
||||||
|
|
||||||
@ -544,6 +586,13 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
|
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NotifyCollectionChangedSynchronizedViewList(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher, WritableViewChangedEventHandler<T, TView>? converter)
|
||||||
|
: base(parent)
|
||||||
|
{
|
||||||
|
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
|
protected override void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
|
||||||
{
|
{
|
||||||
if (CollectionChanged == null && PropertyChanged == null) return;
|
if (CollectionChanged == null && PropertyChanged == null) return;
|
||||||
@ -644,7 +693,30 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
TView IList<TView>.this[int index]
|
TView IList<TView>.this[int index]
|
||||||
{
|
{
|
||||||
get => ((IReadOnlyList<TView>)this)[index];
|
get => ((IReadOnlyList<TView>)this)[index];
|
||||||
set => throw new NotSupportedException();
|
set
|
||||||
|
{
|
||||||
|
if (converter == null || parent is not IWritableSynchronizedView<T,TView> writableView)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var originalIndex = listView.GetAlternateIndex(index);
|
||||||
|
var (originalValue, _) = writableView.GetAt(originalIndex);
|
||||||
|
|
||||||
|
// update view
|
||||||
|
writableView.SetViewAt(originalIndex, value);
|
||||||
|
listView[index] = value;
|
||||||
|
|
||||||
|
var setValue = true;
|
||||||
|
var newOriginal = converter(value, originalValue, ref setValue);
|
||||||
|
|
||||||
|
if (setValue)
|
||||||
|
{
|
||||||
|
writableView.SetToSourceCollection(index, newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object? IList.this[int index]
|
object? IList.this[int index]
|
||||||
@ -653,7 +725,7 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
{
|
{
|
||||||
return this[index];
|
return this[index];
|
||||||
}
|
}
|
||||||
set => throw new NotSupportedException();
|
set => ((IList<TView>)this)[index] = (TView)value!;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsCompatibleObject(object? value)
|
static bool IsCompatibleObject(object? value)
|
||||||
@ -779,6 +851,7 @@ internal class NonFilteredNotifyCollectionChangedSynchronizedViewList<T, TView>
|
|||||||
static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent;
|
static readonly Action<NotifyCollectionChangedEventArgs> raiseChangedEventInvoke = RaiseChangedEvent;
|
||||||
|
|
||||||
readonly ICollectionEventDispatcher eventDispatcher;
|
readonly ICollectionEventDispatcher eventDispatcher;
|
||||||
|
readonly WritableViewChangedEventHandler<T, TView>? converter; // null = readonly
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
@ -791,6 +864,13 @@ internal class NonFilteredNotifyCollectionChangedSynchronizedViewList<T, TView>
|
|||||||
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
|
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NonFilteredNotifyCollectionChangedSynchronizedViewList(ISynchronizedView<T, TView> parent, ICollectionEventDispatcher? eventDispatcher, WritableViewChangedEventHandler<T, TView>? converter)
|
||||||
|
: base(parent)
|
||||||
|
{
|
||||||
|
this.eventDispatcher = eventDispatcher ?? InlineCollectionEventDispatcher.Instance;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
|
protected override void OnCollectionChanged(in SynchronizedViewChangedEventArgs<T, TView> args)
|
||||||
{
|
{
|
||||||
if (CollectionChanged == null && PropertyChanged == null) return;
|
if (CollectionChanged == null && PropertyChanged == null) return;
|
||||||
@ -891,7 +971,29 @@ internal class NonFilteredNotifyCollectionChangedSynchronizedViewList<T, TView>
|
|||||||
TView IList<TView>.this[int index]
|
TView IList<TView>.this[int index]
|
||||||
{
|
{
|
||||||
get => ((IReadOnlyList<TView>)this)[index];
|
get => ((IReadOnlyList<TView>)this)[index];
|
||||||
set => throw new NotSupportedException();
|
set
|
||||||
|
{
|
||||||
|
if (converter == null || parent is not IWritableSynchronizedView<T, TView> writableView)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var (originalValue, _) = writableView.GetAt(index);
|
||||||
|
|
||||||
|
// update view
|
||||||
|
writableView.SetViewAt(index, value);
|
||||||
|
listView[index] = value;
|
||||||
|
|
||||||
|
var setValue = true;
|
||||||
|
var newOriginal = converter(value, originalValue, ref setValue);
|
||||||
|
|
||||||
|
if (setValue)
|
||||||
|
{
|
||||||
|
writableView.SetToSourceCollection(index, newOriginal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object? IList.this[int index]
|
object? IList.this[int index]
|
||||||
@ -900,7 +1002,7 @@ internal class NonFilteredNotifyCollectionChangedSynchronizedViewList<T, TView>
|
|||||||
{
|
{
|
||||||
return this[index];
|
return this[index];
|
||||||
}
|
}
|
||||||
set => throw new NotSupportedException();
|
set => ((IList<TView>)this)[index] = (TView)value!;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsCompatibleObject(object? value)
|
static bool IsCompatibleObject(object? value)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user