Improve removal of items
This commit is contained in:
@@ -11,7 +11,10 @@ public partial class MediaInformationViewModel :
|
||||
[ObservableProperty]
|
||||
private string description = "this is a test description";
|
||||
|
||||
public MediaInformationViewModel(IServiceFactory serviceFactory, IMediator mediator, IDisposer disposer, ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer, templateFactory)
|
||||
public MediaInformationViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer, templateFactory)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,35 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class ConfigurationChangedHandler :
|
||||
public class ConfigurationChangedHandler(IMediator mediator,
|
||||
PrimaryWidgetConfiguration configuration,
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory,
|
||||
IViewModelCache<Guid, IWidgetComponentViewModel> cache) :
|
||||
INotificationHandler<ConfigurationChanged<PrimaryWidgetConfiguration>>
|
||||
{
|
||||
public ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
|
||||
public async ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
foreach (KeyValuePair<Guid, IWidgetComponentViewModel> cached in cache)
|
||||
{
|
||||
if (configuration.FirstOrDefault(x => x.Id == cached.Key) == null)
|
||||
{
|
||||
await mediator.PublishAsync(new Removed<IWidgetComponentViewModel>(cached.Value),
|
||||
cancellationToken);
|
||||
|
||||
cache.Remove(cached.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PrimaryCommandConfiguration item in configuration)
|
||||
{
|
||||
|
||||
//if (!cache.ContainsKey(item.Id))
|
||||
//{
|
||||
// factory.CreateAsync(item);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
//}
|
||||
} }
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public class PrimaryWidgetProvider :
|
||||
{
|
||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||
services.AddConfiguration<PrimaryWidgetConfiguration>()
|
||||
.AddTransient<IViewModelCache<Guid, IWidgetComponentViewModel>, ViewModelCache<Guid, IWidgetComponentViewModel>>()
|
||||
.AddSingleton<IViewModelCache<Guid, IWidgetComponentViewModel>, ViewModelCache<Guid, IWidgetComponentViewModel>>()
|
||||
.AddTransient<IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
|
||||
.AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>()
|
||||
.AddWidgetTemplate<PrimaryWidgetViewModel>()
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class PrimaryWidgetViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
|
||||
@@ -4,11 +4,11 @@ public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration confi
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory) :
|
||||
IViewModelEnumerator<IWidgetComponentViewModel>
|
||||
{
|
||||
public async IAsyncEnumerable<IWidgetComponentViewModel?> Next()
|
||||
public IEnumerable<IWidgetComponentViewModel?> Next()
|
||||
{
|
||||
foreach (PrimaryCommandConfiguration item in configuration)
|
||||
{
|
||||
yield return await factory.CreateAsync(item);
|
||||
yield return factory.Create(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ public class WidgetComponentViewModelFactory(IServiceFactory service,
|
||||
IViewModelCache<Guid, IWidgetComponentViewModel> cache) :
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>
|
||||
{
|
||||
public async ValueTask<IWidgetComponentViewModel?> CreateAsync(PrimaryCommandConfiguration value)
|
||||
public IWidgetComponentViewModel? Create(PrimaryCommandConfiguration value)
|
||||
{
|
||||
IWidgetComponentViewModel? viewModel = null;
|
||||
IWidgetComponentViewModel? viewModel = default;
|
||||
|
||||
if (value is KeyAcceleratorCommandConfiguration keyAcceleratorCommand)
|
||||
{
|
||||
@@ -31,6 +31,6 @@ public class WidgetComponentViewModelFactory(IServiceFactory service,
|
||||
cache.Add(value.Id, viewModel);
|
||||
}
|
||||
|
||||
return viewModel ?? default;
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,6 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="System.Reactive" Version="6.0.0" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IViewModelCache<TKey, TViewModel> :
|
||||
IDictionary<TKey, TViewModel>
|
||||
where TKey : notnull
|
||||
{
|
||||
|
||||
}
|
||||
@@ -2,29 +2,5 @@
|
||||
|
||||
public interface IViewModelFactory<TParameter, TViewModel>
|
||||
{
|
||||
ValueTask<TViewModel> CreateAsync(TParameter value);
|
||||
}
|
||||
|
||||
public interface IViewModelCache<TKey, TViewModel>
|
||||
{
|
||||
void Add(TKey key, TViewModel value);
|
||||
|
||||
void Remove(TKey key);
|
||||
}
|
||||
|
||||
public class ViewModelCache<TKey, TViewModel> :
|
||||
IViewModelCache<TKey, TViewModel>
|
||||
{
|
||||
private readonly Dictionary<TKey, TViewModel> cache = [];
|
||||
|
||||
public void Add(TKey key,
|
||||
TViewModel value)
|
||||
{
|
||||
cache.TryAdd(key, value);
|
||||
}
|
||||
|
||||
public void Remove(TKey key)
|
||||
{
|
||||
cache.Remove(key);
|
||||
}
|
||||
TViewModel? Create(TParameter value);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public class ViewModelCache<TKey, TViewModel>(IDisposer disposer) :
|
||||
IViewModelCache<TKey, TViewModel>
|
||||
where TKey : notnull
|
||||
{
|
||||
private readonly IDictionary<TKey, TViewModel> cache = new Dictionary<TKey, TViewModel>();
|
||||
|
||||
public TViewModel this[TKey key]
|
||||
{
|
||||
get => cache[key];
|
||||
set => cache[key] = value;
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => cache.Keys;
|
||||
|
||||
public ICollection<TViewModel> Values => cache.Values;
|
||||
|
||||
public int Count => cache.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(TKey key, TViewModel value)
|
||||
{
|
||||
disposer.Add(value!, Disposable.Create(() =>
|
||||
{
|
||||
Remove(key);
|
||||
}));
|
||||
|
||||
cache.Add(key, value);
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TViewModel> item)
|
||||
{
|
||||
cache.Add(item);
|
||||
}
|
||||
|
||||
public void Clear() => cache.Clear();
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TViewModel> item)
|
||||
{
|
||||
return cache.Contains(item);
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return cache.ContainsKey(key);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TViewModel>[] array, int arrayIndex)
|
||||
{
|
||||
cache.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TViewModel>> GetEnumerator()
|
||||
{
|
||||
return cache.GetEnumerator();
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
return cache.Remove(key);
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TViewModel> item)
|
||||
{
|
||||
return cache.Remove(item);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TViewModel value)
|
||||
{
|
||||
return cache.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return cache.GetEnumerator();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ public class Mediator(IServiceProvider provider) :
|
||||
{
|
||||
private readonly ConditionalWeakTable<dynamic, Type> subjects = [];
|
||||
|
||||
public ValueTask PublishAsync<TNotification>(TNotification notification,
|
||||
public async ValueTask PublishAsync<TNotification>(TNotification notification,
|
||||
CancellationToken cancellationToken = default)
|
||||
where TNotification :
|
||||
INotification
|
||||
@@ -26,10 +26,8 @@ public class Mediator(IServiceProvider provider) :
|
||||
|
||||
foreach (INotificationHandler<TNotification> handler in handlers)
|
||||
{
|
||||
return handler.Handle(notification, cancellationToken);
|
||||
await handler.Handle(notification, cancellationToken);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> request,
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
public interface IViewModelEnumerator<TItem>
|
||||
{
|
||||
IAsyncEnumerable<TItem?> Next();
|
||||
IEnumerable<TItem?> Next();
|
||||
}
|
||||
|
||||
@@ -14,10 +14,9 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
{
|
||||
public ObservableCollection<TItem> collection = [];
|
||||
private readonly SynchronizationContext? context;
|
||||
private readonly IDisposer disposer;
|
||||
private readonly IViewModelEnumerator<TItem>? enumerator;
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
private readonly IDisposer disposer;
|
||||
|
||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer)
|
||||
@@ -49,16 +48,13 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
|
||||
if (enumerator is not null)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await foreach (TItem? item in enumerator.Next())
|
||||
foreach (TItem? item in enumerator.Next())
|
||||
{
|
||||
if (item is not null)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,15 +146,27 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
TItem
|
||||
{
|
||||
T? item = serviceFactory.Create<T>();
|
||||
context?.Post(state => Add(item), null);
|
||||
Add(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public void Add(TItem item)
|
||||
{
|
||||
context?.Post(state =>
|
||||
{
|
||||
disposer.Add(this, item);
|
||||
disposer.Add(item, Disposable.Create(item, args =>
|
||||
{
|
||||
if (Contains(args))
|
||||
{
|
||||
Remove(args);
|
||||
}
|
||||
}));
|
||||
|
||||
int index = collection.Count;
|
||||
InsertItem(index, item);
|
||||
}, null);
|
||||
}
|
||||
|
||||
int IList.Add(object? value)
|
||||
@@ -182,27 +190,35 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
{
|
||||
foreach (TItem? item in items)
|
||||
{
|
||||
context?.Post(state => Add(item), null);
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() => ClearItems();
|
||||
|
||||
public bool Contains(TItem item) => collection.Contains(item);
|
||||
public bool Contains(TItem item) =>
|
||||
collection.Contains(item);
|
||||
|
||||
bool IList.Contains(object? value) => IsCompatibleObject(value) && Contains((TItem)value!);
|
||||
bool IList.Contains(object? value) =>
|
||||
IsCompatibleObject(value) && Contains((TItem)value!);
|
||||
|
||||
public void CopyTo(TItem[] array, int index) => collection.CopyTo(array, index);
|
||||
public void CopyTo(TItem[] array, int index) =>
|
||||
collection.CopyTo(array, index);
|
||||
|
||||
void ICollection.CopyTo(Array array, int index) => collection.CopyTo((TItem[])array, index);
|
||||
void ICollection.CopyTo(Array array, int index) =>
|
||||
collection.CopyTo((TItem[])array, index);
|
||||
|
||||
public IEnumerator<TItem> GetEnumerator() => collection.GetEnumerator();
|
||||
public IEnumerator<TItem> GetEnumerator() =>
|
||||
collection.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() =>
|
||||
((IEnumerable)collection).GetEnumerator();
|
||||
|
||||
public int IndexOf(TItem item) => collection.IndexOf(item);
|
||||
|
||||
int IList.IndexOf(object? value) => IsCompatibleObject(value) ? IndexOf((TItem)value!) : -1;
|
||||
int IList.IndexOf(object? value) =>
|
||||
IsCompatibleObject(value) ?
|
||||
IndexOf((TItem)value!) : -1;
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
@@ -215,9 +231,11 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Insert(int index, TItem item) => InsertItem(index, item);
|
||||
public void Insert(int index, TItem item) =>
|
||||
InsertItem(index, item);
|
||||
|
||||
void IList.Insert(int index, object? value)
|
||||
void IList.Insert(int index,
|
||||
object? value)
|
||||
{
|
||||
if (value is TItem item)
|
||||
{
|
||||
@@ -229,7 +247,13 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
{
|
||||
int index = collection.IndexOf(item);
|
||||
if (index < 0) return false;
|
||||
RemoveItem(index);
|
||||
|
||||
context?.Post(state =>
|
||||
{
|
||||
context?.Post(state => RemoveItem(index), null);
|
||||
|
||||
}, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -241,32 +265,23 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index) => RemoveItem(index);
|
||||
public void RemoveAt(int index) =>
|
||||
RemoveItem(index);
|
||||
|
||||
protected virtual void ClearItems() => collection.Clear();
|
||||
protected virtual void ClearItems() =>
|
||||
collection.Clear();
|
||||
|
||||
protected virtual void InsertItem(int index, TItem value)
|
||||
{
|
||||
if (value is TItem item)
|
||||
{
|
||||
disposer.Add(item, Disposable.Create(() =>
|
||||
{
|
||||
if (Contains(item))
|
||||
{
|
||||
Remove(item);
|
||||
}
|
||||
}));
|
||||
protected virtual void InsertItem(int index,
|
||||
TItem value) => collection.Insert(index, value);
|
||||
|
||||
context?.Post(state => collection.Insert(index, item), null);
|
||||
}
|
||||
}
|
||||
protected virtual void RemoveItem(int index) =>
|
||||
collection.RemoveAt(index);
|
||||
|
||||
protected virtual void RemoveItem(int index) => collection.RemoveAt(index);
|
||||
protected virtual void SetItem(int index, TItem item) =>
|
||||
collection[index] = item;
|
||||
|
||||
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 static bool IsCompatibleObject(object? value) =>
|
||||
(value is TItem) || (value == null && default(TItem) == null);
|
||||
|
||||
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) =>
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public class ObservableViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer) :
|
||||
ObservableObject,
|
||||
IDisposable
|
||||
{
|
||||
public void Dispose() => disposer.Dispose(this);
|
||||
}
|
||||
@@ -7,13 +7,10 @@ public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid id = default,
|
||||
Guid guid = default,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory)
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private Guid id = id;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? icon = icon;
|
||||
|
||||
|
||||
@@ -2,21 +2,43 @@
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public class ObservableViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer) :
|
||||
ObservableObject,
|
||||
IDisposable
|
||||
public partial class WidgetComponentViewModel :
|
||||
ObservableViewModel,
|
||||
IWidgetComponentViewModel,
|
||||
ITemplatedViewModel,
|
||||
INotificationHandler<Removed<IWidgetComponentViewModel>>
|
||||
{
|
||||
public void Dispose() => disposer.Dispose(this);
|
||||
}
|
||||
private readonly IMediator mediator;
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
|
||||
public partial class WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||
[ObservableProperty]
|
||||
private Guid id;
|
||||
|
||||
public WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory) : ObservableViewModel(serviceFactory, mediator, disposer),
|
||||
IWidgetComponentViewModel,
|
||||
ITemplatedViewModel
|
||||
{
|
||||
public ITemplateFactory TemplateFactory => templateFactory;
|
||||
ITemplateFactory templateFactory,
|
||||
Guid id = default) : base(serviceFactory, mediator, disposer)
|
||||
{
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.mediator = mediator;
|
||||
this.id = id;
|
||||
|
||||
TemplateFactory = templateFactory;
|
||||
|
||||
mediator.Subscribe(this);
|
||||
}
|
||||
|
||||
public ITemplateFactory TemplateFactory { get; private set; }
|
||||
|
||||
public ValueTask Handle(Removed<IWidgetComponentViewModel> notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (notification.Value.Equals(this))
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user