Wire up the disposer for cleaning up unused objects, i.e disposing a VM will remove it from the view
This commit is contained in:
@@ -1,18 +1,13 @@
|
||||
|
||||
namespace Hyperbar.Widget.Contextual;
|
||||
namespace Hyperbar.Widget.Contextual;
|
||||
|
||||
public class ContextualWidgetViewModel :
|
||||
ObservableCollectionViewModel<IWidgetComponentViewModel>,
|
||||
public class ContextualWidgetViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
IEnumerable<IWidgetComponentViewModel> items) :
|
||||
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, disposer, items),
|
||||
IWidgetViewModel,
|
||||
ITemplatedViewModel
|
||||
{
|
||||
public ContextualWidgetViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, mediator, items)
|
||||
{
|
||||
TemplateFactory = templateFactory;
|
||||
}
|
||||
|
||||
public ITemplateFactory TemplateFactory { get; }
|
||||
public ITemplateFactory TemplateFactory => templateFactory;
|
||||
}
|
||||
@@ -3,22 +3,17 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class MediaController :
|
||||
INotificationHandler<PlayRequest>,
|
||||
IDisposable
|
||||
INotificationHandler<PlayRequest>
|
||||
{
|
||||
public MediaController(GlobalSystemMediaTransportControlsSession session)
|
||||
{
|
||||
private readonly GlobalSystemMediaTransportControlsSession session;
|
||||
|
||||
public MediaController(GlobalSystemMediaTransportControlsSession session,
|
||||
IMediator mediator)
|
||||
{
|
||||
this.session = session;
|
||||
mediator.Subscribe(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask Handle(PlayRequest notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public async ValueTask Handle(PlayRequest notification, CancellationToken cancellationToken) =>
|
||||
await session.TryPlayAsync();
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using Windows.Media.Control;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class MediaControllerInitializer :
|
||||
IInitializer
|
||||
{
|
||||
private readonly List<GlobalSystemMediaTransportControlsSession> sessions = [];
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager =
|
||||
await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
|
||||
|
||||
mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged;
|
||||
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
||||
mediaTransportControlsSessionManager.GetSessions();
|
||||
|
||||
foreach (var session in sessions)
|
||||
{
|
||||
this.sessions.Add(session);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSessionsChanged(GlobalSystemMediaTransportControlsSessionManager sender,
|
||||
SessionsChangedEventArgs args)
|
||||
{
|
||||
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
||||
sender.GetSessions();
|
||||
|
||||
foreach (var session in this.sessions.ToList())
|
||||
{
|
||||
if (!sessions.Contains(session))
|
||||
{
|
||||
this.sessions.Remove(session);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var session in sessions)
|
||||
{
|
||||
if (!this.sessions.Contains(session))
|
||||
{
|
||||
this.sessions.Add(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Channels;
|
||||
using Windows.Media.Control;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class MediaControllerManager :
|
||||
IInitializer
|
||||
{
|
||||
private readonly ConcurrentDictionary<GlobalSystemMediaTransportControlsSession, MediaController> cachedSessions = [];
|
||||
private readonly IMediator mediator;
|
||||
private readonly Queue<MediaController> mediaControllers;
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
|
||||
public MediaControllerManager(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
Queue<MediaController> mediaControllers)
|
||||
{
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.mediator = mediator;
|
||||
this.mediaControllers = mediaControllers;
|
||||
}
|
||||
|
||||
private Channel<MediaController> d;
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
d = Channel.CreateUnbounded<MediaController>();
|
||||
|
||||
_ = Task.Run(async () => {
|
||||
|
||||
await foreach (var coordinates in d.Reader.ReadAllAsync())
|
||||
{
|
||||
Console.WriteLine(coordinates);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager =
|
||||
await GlobalSystemMediaTransportControlsSessionManager.RequestAsync();
|
||||
mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged;
|
||||
|
||||
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
||||
mediaTransportControlsSessionManager.GetSessions();
|
||||
|
||||
foreach (GlobalSystemMediaTransportControlsSession session in sessions)
|
||||
{
|
||||
await InitializeSessionAsync(session);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeSessionAsync(GlobalSystemMediaTransportControlsSession session)
|
||||
{
|
||||
if (serviceFactory.Create<MediaController>(session) is MediaController mediaController)
|
||||
{
|
||||
await d.Writer.WriteAsync(mediaController);
|
||||
|
||||
mediaControllers.Enqueue(mediaController);
|
||||
|
||||
cachedSessions.TryAdd(session, mediaController);
|
||||
await mediator.PublishAsync(new Created<MediaController>(mediaController));
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnSessionsChanged(GlobalSystemMediaTransportControlsSessionManager sender,
|
||||
SessionsChangedEventArgs args)
|
||||
{
|
||||
IReadOnlyList<GlobalSystemMediaTransportControlsSession> sessions =
|
||||
sender.GetSessions();
|
||||
|
||||
foreach (KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController> session in
|
||||
cachedSessions)
|
||||
{
|
||||
if (!sessions.Contains(session.Key))
|
||||
{
|
||||
cachedSessions.TryRemove(session);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GlobalSystemMediaTransportControlsSession session in sessions)
|
||||
{
|
||||
await InitializeSessionAsync(session);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveSession(GlobalSystemMediaTransportControlsSession session)
|
||||
{
|
||||
if (serviceFactory.Create<MediaController>(session) is MediaController mediaController)
|
||||
{
|
||||
cachedSessions.TryAdd(session, mediaController);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public partial class MediaInformationViewModel :
|
||||
WidgetComponentViewModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string title = "this is a test";
|
||||
|
||||
[ObservableProperty]
|
||||
private string description = "this is a test description";
|
||||
|
||||
public MediaInformationViewModel(ITemplateFactory templateFactory) : base(templateFactory)
|
||||
{
|
||||
}
|
||||
}
|
||||
//public class MediaControllerViewModelFactory(IServiceFactory service,
|
||||
// IMediator mediator,
|
||||
// Queue<MediaController> mediaControllers) :
|
||||
// IFactory<MediaControllerViewModel>
|
||||
//{
|
||||
// public MediaControllerViewModel Create()
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
//}
|
||||
|
||||
public class MediaControllerViewModel :
|
||||
ObservableCollectionViewModel<WidgetComponentViewModel>,
|
||||
@@ -22,7 +19,8 @@ public class MediaControllerViewModel :
|
||||
{
|
||||
public MediaControllerViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator) : base(serviceFactory, mediator)
|
||||
IMediator mediator,
|
||||
IDisposer disposer) : base(serviceFactory, mediator, disposer)
|
||||
{
|
||||
TemplateFactory = templateFactory;
|
||||
|
||||
|
||||
@@ -9,10 +9,8 @@ public class MediaControllerWidgetProvider :
|
||||
{
|
||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||
services.AddWidgetTemplate<MediaControllerWidgetViewModel, MediaControllerWidgetView>()
|
||||
.AddTransient<IInitializer, MediaControllerInitializer>()
|
||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
|
||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
|
||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
|
||||
.AddContentTemplate<MediaInformationViewModel, MediaInformationView>();
|
||||
.AddSingleton<Queue<MediaController>>()
|
||||
.AddSingleton<IInitializer, MediaControllerManager>()
|
||||
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>();
|
||||
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IEnumerable<MediaControllerViewModel> items) :
|
||||
ObservableCollectionViewModel<MediaControllerViewModel>(serviceFactory, mediator, items),
|
||||
IDisposer disposer) :
|
||||
ObservableCollectionViewModel<MediaControllerViewModel>(serviceFactory, mediator, disposer),
|
||||
IWidgetViewModel,
|
||||
ITemplatedViewModel
|
||||
{
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public partial class MediaInformationViewModel :
|
||||
WidgetComponentViewModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string title = "this is a test";
|
||||
|
||||
[ObservableProperty]
|
||||
private string description = "this is a test description";
|
||||
|
||||
public MediaInformationViewModel(IServiceFactory serviceFactory, IMediator mediator, IDisposer disposer, ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer, templateFactory)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="System.Runtime.Caching" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Hyperbar.Windows.Primary;
|
||||
[JsonDerivedType(typeof(ProcessCommandConfiguration), typeDiscriminator: "ProcessCommand")]
|
||||
public class PrimaryCommandConfiguration
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public required Guid Id { get; set; }
|
||||
|
||||
public required string Icon { get; set; }
|
||||
}
|
||||
@@ -5,6 +5,6 @@ public class PrimaryWidgetConfiguration :
|
||||
{
|
||||
public static PrimaryWidgetConfiguration Defaults => new()
|
||||
{
|
||||
new KeyAcceleratorCommandConfiguration { Id = $"{Guid.NewGuid()}", Icon = "\uE720", Key = 91, Modifiers = [] }
|
||||
new KeyAcceleratorCommandConfiguration { Id = Guid.NewGuid(), Icon = "\uE720", Key = 91, Modifiers = [] }
|
||||
};
|
||||
}
|
||||
@@ -8,9 +8,10 @@ public class PrimaryWidgetProvider :
|
||||
{
|
||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||
services.AddConfiguration<PrimaryWidgetConfiguration>()
|
||||
.AddTransient<IFactory<IEnumerable<IWidgetComponentViewModel>>, WidgetComponentViewModelFactory>()
|
||||
.AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>()
|
||||
.AddTransient<IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
|
||||
.AddWidgetTemplate<PrimaryWidgetViewModel>()
|
||||
.AddNotificationPipeline<ConfigurationChanged<PrimaryWidgetConfiguration>,
|
||||
ValueChanging<IEnumerable<IWidgetComponentViewModel>>>();
|
||||
CollectionChanged<IEnumerable<IWidgetComponentViewModel>>>();
|
||||
|
||||
}
|
||||
@@ -4,8 +4,9 @@ namespace Hyperbar.Windows.Primary;
|
||||
public class PrimaryWidgetViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IFactory<IEnumerable<IWidgetComponentViewModel>> factory) :
|
||||
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, factory),
|
||||
IDisposer disposer,
|
||||
IViewModelEnumerator<IWidgetComponentViewModel> enumerator) :
|
||||
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, disposer, enumerator),
|
||||
IWidgetViewModel,
|
||||
ITemplatedViewModel
|
||||
{
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class WidgetComponentViewModelFactory(IServiceFactory service,
|
||||
IMediator mediator) :
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>
|
||||
{
|
||||
public async ValueTask<IWidgetComponentViewModel?> CreateAsync(PrimaryCommandConfiguration value)
|
||||
{
|
||||
if (value is KeyAcceleratorCommandConfiguration keyAcceleratorCommand)
|
||||
{
|
||||
return await ValueTask.FromResult(service.Create<WidgetButtonViewModel>(keyAcceleratorCommand.Id, keyAcceleratorCommand.Icon,
|
||||
new RelayCommand(async () => await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey)
|
||||
keyAcceleratorCommand.Key, keyAcceleratorCommand.Modifiers?
|
||||
.Select(modifier => (VirtualKey)modifier).ToArray())))));
|
||||
}
|
||||
|
||||
if (value is ProcessCommandConfiguration commandConfiguration)
|
||||
{
|
||||
return await ValueTask.FromResult(service.Create<WidgetButtonViewModel>(commandConfiguration.Id,
|
||||
commandConfiguration.Icon, new RelayCommand(async () =>
|
||||
await mediator.SendAsync(new ProcessRequest(commandConfiguration.Path)))));
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration configuration,
|
||||
IViewModelFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory) :
|
||||
IViewModelEnumerator<IWidgetComponentViewModel>
|
||||
{
|
||||
public async IAsyncEnumerable<IWidgetComponentViewModel?> Next()
|
||||
{
|
||||
foreach (PrimaryCommandConfiguration item in configuration)
|
||||
{
|
||||
yield return await factory.CreateAsync(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class WidgetComponentViewModelFactory(PrimaryWidgetConfiguration configuration,
|
||||
IServiceFactory service,
|
||||
IMediator mediator) :
|
||||
IFactory<IEnumerable<IWidgetComponentViewModel>>
|
||||
{
|
||||
public IEnumerable<IWidgetComponentViewModel> Create()
|
||||
{
|
||||
foreach (PrimaryCommandConfiguration item in configuration)
|
||||
{
|
||||
if (item is KeyAcceleratorCommandConfiguration keyAcceleratorCommandConfiguration)
|
||||
{
|
||||
yield return service.Create<WidgetButtonViewModel>(keyAcceleratorCommandConfiguration.Icon, new Action(async () =>
|
||||
await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey)keyAcceleratorCommandConfiguration.Key,
|
||||
keyAcceleratorCommandConfiguration.Modifiers?.Select(modifier => (VirtualKey)modifier).ToArray()))));
|
||||
}
|
||||
|
||||
if (item is ProcessCommandConfiguration processCommandConfiguration)
|
||||
{
|
||||
yield return service.Create<WidgetButtonViewModel>(processCommandConfiguration.Icon, new Action(async () =>
|
||||
await mediator.SendAsync(new ProcessRequest(processCommandConfiguration.Path))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@ public partial class App :
|
||||
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
|
||||
|
||||
services.AddSingleton<IMediator, Mediator>();
|
||||
services.AddSingleton<IDisposer, Disposer>();
|
||||
|
||||
services.AddHostedService<AppService>();
|
||||
|
||||
services.AddTransient<IInitializer, AppInitializer>();
|
||||
@@ -45,7 +47,7 @@ public partial class App :
|
||||
|
||||
services.AddContentTemplate<WidgetBarViewModel, WidgetBarView>();
|
||||
|
||||
services.AddWidgetProvider<MediaControllerWidgetProvider>();
|
||||
//services.AddWidgetProvider<MediaControllerWidgetProvider>();
|
||||
services.AddWidgetProvider<PrimaryWidgetProvider>();
|
||||
|
||||
services.AddTransient(provider =>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25936-preview" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Hyperbar.Windows
|
||||
|
||||
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
|
||||
isolatedServices.AddSingleton<IMediator, Mediator>();
|
||||
isolatedServices.AddSingleton<IDisposer, Disposer>();
|
||||
|
||||
isolatedServices.AddSingleton<IVirtualKeyboard, VirtualKeyboard>();
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
Width="{StaticResource ButtonWidth}"
|
||||
Height="{StaticResource ButtonHeight}"
|
||||
Padding="{StaticResource ButtonPadding}"
|
||||
FontSize="16"
|
||||
Command="{Binding Click}"
|
||||
Content="{Binding Icon}"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}" />
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16" />
|
||||
</UserControl>
|
||||
@@ -3,12 +3,20 @@
|
||||
x:Class="Hyperbar.Windows.WidgetView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:ui="using:Hyperbar.Windows.UI">
|
||||
|
||||
<ItemsControl ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactions:EventTriggerBehavior EventName="Loaded">
|
||||
<interactions:InvokeCommandAction Command="{Binding Initialize}" />
|
||||
</interactions:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ItemsControl>
|
||||
</UserControl>
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
|
||||
namespace Hyperbar.Windows;
|
||||
|
||||
@@ -7,13 +6,5 @@ public sealed partial class WidgetView :
|
||||
UserControl,
|
||||
IWidgetView
|
||||
{
|
||||
public WidgetView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyRoutedEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
}
|
||||
public WidgetView() => InitializeComponent();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IMapping<TFrom, TTo>
|
||||
{
|
||||
TTo Create();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IViewModelFactory<TIn, TOut>
|
||||
{
|
||||
ValueTask<TOut> CreateAsync(TIn value);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IFactory<T>
|
||||
{
|
||||
T Create();
|
||||
}
|
||||
@@ -10,9 +10,6 @@ public class NotficationPipelineHandler<TFromNotification, ToNotification>(IMedi
|
||||
{
|
||||
private readonly IMediator mediator = mediator;
|
||||
|
||||
public ValueTask Handle(TFromNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
return mediator.PublishAsync(new ToNotification(),
|
||||
cancellationToken);
|
||||
}
|
||||
public ValueTask Handle(TFromNotification notification, CancellationToken cancellationToken) =>
|
||||
mediator.PublishAsync(new ToNotification(), cancellationToken);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record CollectionChanged<TValue> : INotification;
|
||||
@@ -1,5 +0,0 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public record ValueChanging<TValue> : INotification;
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record Created<TValue>(TValue? Value = default) : INotification;
|
||||
@@ -8,4 +8,4 @@ public interface IObservableCollectionViewModel<TItem> :
|
||||
IList,
|
||||
IReadOnlyList<TItem>,
|
||||
INotifyCollectionChanged,
|
||||
INotificationHandler<ValueChanging<IEnumerable<TItem>>>;
|
||||
IInitializer;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public interface IViewModelEnumerator<TItem>
|
||||
{
|
||||
IAsyncEnumerable<TItem?> Next();
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
@@ -11,14 +14,19 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
{
|
||||
public ObservableCollection<TItem> collection = [];
|
||||
private readonly SynchronizationContext? context;
|
||||
private readonly IViewModelEnumerator<TItem>? enumerator;
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
private readonly IDisposer disposer;
|
||||
|
||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator)
|
||||
IMediator mediator,
|
||||
IDisposer disposer)
|
||||
{
|
||||
context = SynchronizationContext.Current;
|
||||
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.disposer = disposer;
|
||||
|
||||
mediator.Subscribe(this);
|
||||
|
||||
collection.CollectionChanged += OnCollectionChanged;
|
||||
@@ -26,28 +34,44 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
|
||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IFactory<IEnumerable<TItem>> factory)
|
||||
IDisposer disposer,
|
||||
IViewModelEnumerator<TItem> enumerator)
|
||||
{
|
||||
context = SynchronizationContext.Current;
|
||||
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.disposer = disposer;
|
||||
this.enumerator = enumerator;
|
||||
|
||||
mediator.Subscribe(this);
|
||||
|
||||
collection.CollectionChanged += OnCollectionChanged;
|
||||
|
||||
if (factory is not null && factory.Create() is { } items)
|
||||
|
||||
if (enumerator is not null)
|
||||
{
|
||||
AddRange(factory.Create());
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await foreach (TItem? item in enumerator.Next())
|
||||
{
|
||||
if (item is not null)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
IEnumerable<TItem> items)
|
||||
{
|
||||
context = SynchronizationContext.Current;
|
||||
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.disposer = disposer;
|
||||
|
||||
mediator.Subscribe(this);
|
||||
|
||||
collection.CollectionChanged += OnCollectionChanged;
|
||||
@@ -59,6 +83,10 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
|
||||
public int Count => collection.Count;
|
||||
|
||||
public ICommand Initialize => new AsyncRelayCommand(InitializeAsync);
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
bool IList.IsFixedSize => false;
|
||||
|
||||
bool ICollection<TItem>.IsReadOnly => false;
|
||||
@@ -157,6 +185,7 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
context?.Post(state => Add(item), null);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() => ClearItems();
|
||||
|
||||
public bool Contains(TItem item) => collection.Contains(item);
|
||||
@@ -171,19 +200,21 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator();
|
||||
|
||||
//public ValueTask Handle(CollectionChanged<IEnumerable<TItem>> notification,
|
||||
// CancellationToken cancellationToken)
|
||||
//{
|
||||
// context?.Post(state => Clear(), null);
|
||||
// AddRange(notification.Items);
|
||||
|
||||
// return ValueTask.CompletedTask;
|
||||
//}
|
||||
|
||||
public int IndexOf(TItem item) => collection.IndexOf(item);
|
||||
|
||||
int IList.IndexOf(object? value) => IsCompatibleObject(value) ? IndexOf((TItem)value!) : -1;
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
if (Initialized)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Initialized = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Insert(int index, TItem item) => InsertItem(index, item);
|
||||
|
||||
void IList.Insert(int index, object? value)
|
||||
@@ -209,6 +240,7 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
Remove((TItem)value!);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index) => RemoveItem(index);
|
||||
|
||||
protected virtual void ClearItems() => collection.Clear();
|
||||
@@ -217,7 +249,15 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
{
|
||||
if (value is TItem item)
|
||||
{
|
||||
collection.Insert(index, item);
|
||||
disposer.Add(item, Disposable.Create(() =>
|
||||
{
|
||||
if (Contains(item))
|
||||
{
|
||||
Remove(item);
|
||||
}
|
||||
}));
|
||||
|
||||
context?.Post(state => collection.Insert(index, item), null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,13 +270,9 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
|
||||
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) =>
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
|
||||
public ValueTask Handle(ValueChanging<IEnumerable<TItem>> notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class ObservableCollectionViewModel(IServiceFactory serviceFactory, IMediator mediator) :
|
||||
ObservableCollectionViewModel<object>(serviceFactory, mediator);
|
||||
public class ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer) :
|
||||
ObservableCollectionViewModel<object>(serviceFactory, mediator, disposer);
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record Removed<TValue>(TValue Value) : INotification;
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
namespace Hyperbar;
|
||||
namespace Hyperbar;
|
||||
|
||||
public partial class WidgetBarViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
IEnumerable<WidgetContainerViewModel> items) :
|
||||
ObservableCollectionViewModel<WidgetContainerViewModel>(serviceFactory, mediator, items),
|
||||
ObservableCollectionViewModel<WidgetContainerViewModel>(serviceFactory, mediator, disposer, items),
|
||||
ITemplatedViewModel
|
||||
{
|
||||
public ITemplateFactory TemplateFactory => templateFactory;
|
||||
|
||||
@@ -3,23 +3,20 @@ using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public partial class WidgetButtonViewModel :
|
||||
WidgetComponentViewModel
|
||||
public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid id = default,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string? icon;
|
||||
private Guid id = id;
|
||||
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click;
|
||||
private string? icon = icon;
|
||||
|
||||
public WidgetButtonViewModel(ITemplateFactory templateFactory,
|
||||
string? icon = null,
|
||||
Action? action = null) : base(templateFactory)
|
||||
{
|
||||
this.icon = icon;
|
||||
if (action is not null)
|
||||
{
|
||||
click = new RelayCommand(action);
|
||||
}
|
||||
}
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
}
|
||||
@@ -2,8 +2,19 @@
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
public partial class WidgetComponentViewModel(ITemplateFactory templateFactory) :
|
||||
public class ObservableViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer) :
|
||||
ObservableObject,
|
||||
IDisposable
|
||||
{
|
||||
public void Dispose() => disposer.Dispose(this);
|
||||
}
|
||||
|
||||
public partial class WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory) : ObservableViewModel(serviceFactory, mediator, disposer),
|
||||
IWidgetComponentViewModel,
|
||||
ITemplatedViewModel
|
||||
{
|
||||
|
||||
@@ -6,9 +6,10 @@ namespace Hyperbar;
|
||||
public partial class WidgetContainerViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
IEnumerable<IWidgetViewModel> items,
|
||||
bool alternate) :
|
||||
ObservableCollectionViewModel<IWidgetViewModel>(serviceFactory, mediator, items),
|
||||
ObservableCollectionViewModel<IWidgetViewModel>(serviceFactory, mediator, disposer, items),
|
||||
ITemplatedViewModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
|
||||
Reference in New Issue
Block a user