315 lines
7.2 KiB
C#
315 lines
7.2 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using System.Collections;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.Reactive.Disposables;
|
|
|
|
namespace Hyperbar;
|
|
|
|
public partial class ObservableCollectionViewModel<TItem> :
|
|
ObservableObject,
|
|
IObservableCollectionViewModel<TItem>,
|
|
INotificationHandler<Removed<TItem>>,
|
|
INotificationHandler<Created<TItem>>,
|
|
IDisposable
|
|
where TItem :
|
|
IDisposable
|
|
{
|
|
public ObservableCollection<TItem> collection = [];
|
|
private readonly IViewModelEnumerator<TItem>? enumerator;
|
|
|
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
|
IMediator mediator,
|
|
IDisposer disposer)
|
|
{
|
|
ServiceFactory = serviceFactory;
|
|
Mediator = mediator;
|
|
Disposer = disposer;
|
|
|
|
mediator.Subscribe(this);
|
|
|
|
collection.CollectionChanged += OnCollectionChanged;
|
|
}
|
|
|
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
|
IMediator mediator,
|
|
IDisposer disposer,
|
|
IViewModelEnumerator<TItem> enumerator)
|
|
{
|
|
ServiceFactory = serviceFactory;
|
|
Mediator = mediator;
|
|
Disposer = disposer;
|
|
|
|
this.enumerator = enumerator;
|
|
|
|
mediator.Subscribe(this);
|
|
|
|
collection.CollectionChanged += OnCollectionChanged;
|
|
|
|
if (enumerator is not null)
|
|
{
|
|
foreach (TItem? item in enumerator.Next())
|
|
{
|
|
if (item is not null)
|
|
{
|
|
Add(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
|
IMediator mediator,
|
|
IDisposer disposer,
|
|
IEnumerable<TItem> items)
|
|
{
|
|
ServiceFactory = serviceFactory;
|
|
Mediator = mediator;
|
|
Disposer = disposer;
|
|
|
|
mediator.Subscribe(this);
|
|
|
|
collection.CollectionChanged += OnCollectionChanged;
|
|
|
|
AddRange(items);
|
|
}
|
|
|
|
public event NotifyCollectionChangedEventHandler? CollectionChanged;
|
|
|
|
public int Count => collection.Count;
|
|
|
|
bool IList.IsFixedSize => false;
|
|
|
|
bool ICollection<TItem>.IsReadOnly => false;
|
|
|
|
bool IList.IsReadOnly => false;
|
|
|
|
bool ICollection.IsSynchronized => false;
|
|
|
|
object ICollection.SyncRoot => this;
|
|
|
|
protected IDisposer Disposer { get; private set; }
|
|
|
|
protected IList<TItem> Items => collection;
|
|
|
|
protected IMediator Mediator { get; private set; }
|
|
|
|
protected IServiceFactory ServiceFactory { get; private set; }
|
|
|
|
public TItem this[int index]
|
|
{
|
|
get => collection[index];
|
|
set
|
|
{
|
|
SetItem(index, value);
|
|
}
|
|
}
|
|
|
|
object? IList.this[int index]
|
|
{
|
|
get => collection[index];
|
|
set
|
|
{
|
|
TItem? item = default;
|
|
|
|
try
|
|
{
|
|
item = (TItem)value!;
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
|
|
}
|
|
|
|
this[index] = item!;
|
|
}
|
|
}
|
|
|
|
public TItem Add()
|
|
{
|
|
TItem? item = ServiceFactory.Create<TItem>();
|
|
|
|
Add(item);
|
|
return item;
|
|
}
|
|
|
|
public TItem Add<T>(params object?[] parameters)
|
|
where T : TItem
|
|
{
|
|
T? item = ServiceFactory.Create<T>(parameters);
|
|
Add(item);
|
|
|
|
return item;
|
|
}
|
|
|
|
public TItem Add<T>()
|
|
where T :
|
|
TItem
|
|
{
|
|
T? item = ServiceFactory.Create<T>();
|
|
Add(item);
|
|
|
|
return item;
|
|
}
|
|
|
|
public void Add(TItem item)
|
|
{
|
|
int index = collection.Count;
|
|
InsertItem(index, item);
|
|
}
|
|
|
|
int IList.Add(object? value)
|
|
{
|
|
TItem? item = default;
|
|
|
|
try
|
|
{
|
|
item = (TItem)value!;
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
|
|
}
|
|
|
|
Add(item!);
|
|
return Count - 1;
|
|
}
|
|
|
|
public void AddRange(IEnumerable<TItem> items)
|
|
{
|
|
foreach (TItem? item in items)
|
|
{
|
|
Add(item);
|
|
}
|
|
}
|
|
|
|
public void Clear() => ClearItems();
|
|
|
|
public bool Contains(TItem item) =>
|
|
collection.Contains(item);
|
|
|
|
bool IList.Contains(object? value) =>
|
|
IsCompatibleObject(value) && Contains((TItem)value!);
|
|
|
|
public void CopyTo(TItem[] array, int index) =>
|
|
collection.CopyTo(array, index);
|
|
|
|
void ICollection.CopyTo(Array array, int index) =>
|
|
collection.CopyTo((TItem[])array, index);
|
|
|
|
public void Dispose() => Disposer.Dispose(this);
|
|
|
|
public IEnumerator<TItem> GetEnumerator() =>
|
|
collection.GetEnumerator();
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() =>
|
|
((IEnumerable)collection).GetEnumerator();
|
|
|
|
public ValueTask Handle(Removed<TItem> notification,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
foreach (TItem item in this.ToList())
|
|
{
|
|
if (notification.Value is not null)
|
|
{
|
|
if (notification.Value.Equals(item))
|
|
{
|
|
if (item is IDisposable disposable)
|
|
{
|
|
disposable.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
public ValueTask Handle(Created<TItem> notification,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (notification.Value is not null)
|
|
{
|
|
Add(notification.Value);
|
|
}
|
|
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
public int IndexOf(TItem item) =>
|
|
collection.IndexOf(item);
|
|
|
|
int IList.IndexOf(object? value) =>
|
|
IsCompatibleObject(value) ?
|
|
IndexOf((TItem)value!) : -1;
|
|
|
|
public void Insert(int index, TItem item) =>
|
|
InsertItem(index, item);
|
|
|
|
void IList.Insert(int index,
|
|
object? value)
|
|
{
|
|
if (value is TItem item)
|
|
{
|
|
Insert(index, item);
|
|
}
|
|
}
|
|
|
|
public bool Remove(TItem item)
|
|
{
|
|
int index = collection.IndexOf(item);
|
|
if (index < 0) return false;
|
|
|
|
Disposer.Remove(this, item);
|
|
RemoveItem(index);
|
|
|
|
return true;
|
|
}
|
|
|
|
void IList.Remove(object? value)
|
|
{
|
|
if (IsCompatibleObject(value))
|
|
{
|
|
Remove((TItem)value!);
|
|
}
|
|
}
|
|
|
|
public void RemoveAt(int index) =>
|
|
RemoveItem(index);
|
|
|
|
protected virtual void ClearItems() =>
|
|
collection.Clear();
|
|
|
|
protected virtual void InsertItem(int index,
|
|
TItem value)
|
|
{
|
|
Disposer.Add(this, Disposable.Create(() =>
|
|
{
|
|
Remove(value);
|
|
}));
|
|
|
|
Disposer.Add(value, Disposable.Create(() =>
|
|
{
|
|
Remove(value);
|
|
}));
|
|
|
|
collection.Insert(index, value);
|
|
}
|
|
|
|
protected virtual void RemoveItem(int index) =>
|
|
collection.RemoveAt(index);
|
|
|
|
protected virtual void SetItem(int index, TItem item) =>
|
|
collection[index] = item;
|
|
|
|
private static bool IsCompatibleObject(object? value) =>
|
|
(value is TItem) || (value == null && default(TItem) == null);
|
|
|
|
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) =>
|
|
CollectionChanged?.Invoke(this, args);
|
|
}
|
|
|
|
public class ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
|
IMediator mediator,
|
|
IDisposer disposer) :
|
|
ObservableCollectionViewModel<IDisposable>(serviceFactory, mediator, disposer); |