WritableNotifyCollectionChanged supports Add
This commit is contained in:
parent
dcfb3edd5a
commit
8afb3fb100
57
README.md
57
README.md
@ -374,14 +374,59 @@ public interface IWritableSynchronizedView<T, TView> : ISynchronizedView<T, TVie
|
|||||||
`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`.
|
`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
|
```csharp
|
||||||
var list = new ObservableList<int>();
|
var list = new ObservableList<Person>()
|
||||||
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
|
new (){ Age = 10, Name = "John" },
|
||||||
return int.Parse(newView);
|
new (){ Age = 22, Name = "Jeyne" },
|
||||||
|
new (){ Age = 30, Name = "Mike" },
|
||||||
|
};
|
||||||
|
var view = list.CreateWritableView(x => x.Name);
|
||||||
|
view.AttachFilter(x => x.Age >= 20);
|
||||||
|
|
||||||
|
IList<string?> bindable = view.ToWritableNotifyCollectionChanged((string? newView, Person original, ref bool setValue) =>
|
||||||
|
{
|
||||||
|
if (setValue)
|
||||||
|
{
|
||||||
|
// default setValue == true is Set operation
|
||||||
|
original.Name = newView;
|
||||||
|
|
||||||
|
// You can modify setValue to false, it does not set original collection to new value.
|
||||||
|
// For mutable reference types, when there is only a single,
|
||||||
|
// bound View and to avoid recreating the View, setting false is effective.
|
||||||
|
// Otherwise, keeping it true will set the value in the original collection as well,
|
||||||
|
// and change notifications will be sent to lower-level Views(the delegate for View generation will also be called anew).
|
||||||
|
setValue = false;
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// default setValue == false is Add operation
|
||||||
|
return new Person { Age = null, Name = newView };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bindable[1] = "Bob"; // change Mike(filtered view's [1]) to Bob.
|
||||||
|
bindable.Add("Ken");
|
||||||
|
|
||||||
|
// Show Views
|
||||||
|
foreach (var item in view)
|
||||||
|
{
|
||||||
|
Console.WriteLine(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("---");
|
||||||
|
|
||||||
|
// Show Originals
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
Console.WriteLine((item.Age, item.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Person
|
||||||
|
{
|
||||||
|
public int? Age { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Unity
|
Unity
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>10.0</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\ObservableCollections.R3\ObservableCollections.R3.csproj" />
|
<ProjectReference Include="..\..\src\ObservableCollections.R3\ObservableCollections.R3.csproj" />
|
||||||
<ProjectReference Include="..\..\src\ObservableCollections\ObservableCollections.csproj" />
|
<ProjectReference Include="..\..\src\ObservableCollections\ObservableCollections.csproj" />
|
||||||
<PackageReference Include="R3" Version="1.0.0" />
|
<PackageReference Include="R3" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -6,30 +6,59 @@ using ObservableCollections;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks.Sources;
|
using System.Threading.Tasks.Sources;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
var l = new ObservableList<int>();
|
var list = new ObservableList<Person>()
|
||||||
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;
|
new (){ Age = 10, Name = "John" },
|
||||||
return int.Parse(newView);
|
new (){ Age = 22, Name = "Jeyne" },
|
||||||
|
new (){ Age = 30, Name = "Mike" },
|
||||||
|
};
|
||||||
|
var view = list.CreateWritableView(x => x.Name);
|
||||||
|
view.AttachFilter(x => x.Age >= 20);
|
||||||
|
|
||||||
|
IList<string?> bindable = view.ToWritableNotifyCollectionChanged((string? newView, Person original, ref bool setValue) =>
|
||||||
|
{
|
||||||
|
if (setValue)
|
||||||
|
{
|
||||||
|
// default setValue == true is Set operation
|
||||||
|
original.Name = newView;
|
||||||
|
|
||||||
|
// You can modify setValue to false, it does not set original collection to new value.
|
||||||
|
// For mutable reference types, when there is only a single,
|
||||||
|
// bound View and to avoid recreating the View, setting false is effective.
|
||||||
|
// Otherwise, keeping it true will set the value in the original collection as well,
|
||||||
|
// and change notifications will be sent to lower-level Views(the delegate for View generation will also be called anew).
|
||||||
|
setValue = false;
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// default setValue == false is Add operation
|
||||||
|
return new Person { Age = null, Name = newView };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
l.Add(0);
|
// bindable[0] = "takoyaki";
|
||||||
l.Add(1);
|
|
||||||
l.Add(2);
|
|
||||||
l.Add(3);
|
|
||||||
l.Add(4);
|
|
||||||
l.Add(5);
|
|
||||||
|
|
||||||
notify[1] = "99999";
|
|
||||||
|
|
||||||
foreach (var item in view)
|
foreach (var item in view)
|
||||||
{
|
{
|
||||||
Console.WriteLine(item);
|
Console.WriteLine(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("---");
|
||||||
|
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
Console.WriteLine((item.Age, item.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Person
|
||||||
|
{
|
||||||
|
public int? Age { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//var buffer = new ObservableFixedSizeRingBuffer<int>(5);
|
//var buffer = new ObservableFixedSizeRingBuffer<int>(5);
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ namespace ObservableCollections
|
|||||||
(T Value, TView View) GetAt(int index);
|
(T Value, TView View) GetAt(int index);
|
||||||
void SetViewAt(int index, TView view);
|
void SetViewAt(int index, TView view);
|
||||||
void SetToSourceCollection(int index, T value);
|
void SetToSourceCollection(int index, T value);
|
||||||
|
void AddToSourceCollection(T value);
|
||||||
IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter);
|
IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(WritableViewChangedEventHandler<T, TView> converter);
|
||||||
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
INotifyCollectionChangedSynchronizedViewList<TView> ToWritableNotifyCollectionChanged(ICollectionEventDispatcher? collectionEventDispatcher);
|
||||||
|
@ -360,6 +360,14 @@ namespace ObservableCollections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddToSourceCollection(T value)
|
||||||
|
{
|
||||||
|
lock (SyncRoot)
|
||||||
|
{
|
||||||
|
source.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter)
|
public IWritableSynchronizedViewList<TView> ToWritableViewList(WritableViewChangedEventHandler<T, TView> converter)
|
||||||
{
|
{
|
||||||
return new FiltableWritableSynchronizedViewList<T, TView>(this, converter);
|
return new FiltableWritableSynchronizedViewList<T, TView>(this, converter);
|
||||||
|
@ -557,7 +557,7 @@ internal class FiltableWritableSynchronizedViewList<T, TView> : FiltableSynchron
|
|||||||
|
|
||||||
if (setValue)
|
if (setValue)
|
||||||
{
|
{
|
||||||
writableView.SetToSourceCollection(index, newOriginal);
|
writableView.SetToSourceCollection(originalIndex, newOriginal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,7 +695,7 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
get => ((IReadOnlyList<TView>)this)[index];
|
get => ((IReadOnlyList<TView>)this)[index];
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (converter == null || parent is not IWritableSynchronizedView<T,TView> writableView)
|
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.");
|
throw new NotSupportedException("This CollectionView does not support set. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
||||||
}
|
}
|
||||||
@ -713,7 +713,7 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
|
|
||||||
if (setValue)
|
if (setValue)
|
||||||
{
|
{
|
||||||
writableView.SetToSourceCollection(index, newOriginal);
|
writableView.SetToSourceCollection(originalIndex, newOriginal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -743,12 +743,24 @@ internal class NotifyCollectionChangedSynchronizedViewList<T, TView> :
|
|||||||
|
|
||||||
public void Add(TView item)
|
public void Add(TView item)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
if (converter == null || parent is not IWritableSynchronizedView<T, TView> writableView)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var setValue = false;
|
||||||
|
var newOriginal = converter(item, default!, ref setValue);
|
||||||
|
|
||||||
|
// always add
|
||||||
|
writableView.AddToSourceCollection(newOriginal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Add(object? value)
|
public int Add(object? value)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
Add((TView)value!);
|
||||||
|
return -1; // itself does not add in this collection
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
@ -1020,12 +1032,24 @@ internal class NonFilteredNotifyCollectionChangedSynchronizedViewList<T, TView>
|
|||||||
|
|
||||||
public void Add(TView item)
|
public void Add(TView item)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
if (converter == null || parent is not IWritableSynchronizedView<T, TView> writableView)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This CollectionView does not support Add. If base type is ObservableList<T>, you can use ToWritableSynchronizedView and ToWritableNotifyCollectionChanged.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var setValue = false;
|
||||||
|
var newOriginal = converter(item, default!, ref setValue);
|
||||||
|
|
||||||
|
// always add
|
||||||
|
writableView.AddToSourceCollection(newOriginal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Add(object? value)
|
public int Add(object? value)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
Add((TView)value!);
|
||||||
|
return -1; // itself does not add in this collection
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user