Improve removal of items

This commit is contained in:
TheXamlGuy
2024-01-13 11:04:33 +00:00
parent b47a563876
commit ff1d400531
16 changed files with 245 additions and 109 deletions
@@ -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;
}
}
+1 -1
View File
@@ -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>
+8
View File
@@ -0,0 +1,8 @@
namespace Hyperbar;
public interface IViewModelCache<TKey, TViewModel> :
IDictionary<TKey, TViewModel>
where TKey : notnull
{
}
+1 -25
View File
@@ -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);
}
+83
View File
@@ -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();
}
}
+2 -4
View File
@@ -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,
+1 -1
View File
@@ -2,5 +2,5 @@
public interface IViewModelEnumerator<TItem>
{
IAsyncEnumerable<TItem?> Next();
IEnumerable<TItem?> Next();
}
+54 -39
View File
@@ -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);
+12
View File
@@ -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);
}
+2 -5
View File
@@ -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;
+34 -12
View File
@@ -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
ITemplateFactory templateFactory,
Guid id = default) : base(serviceFactory, mediator, disposer)
{
public ITemplateFactory TemplateFactory => templateFactory;
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;
}
}