Replace Mediator with Messenger

This commit is contained in:
Dan Clark
2024-11-16 13:52:03 +00:00
parent 469a52efaa
commit f16dbaf375
73 changed files with 615 additions and 1345 deletions
@@ -6,9 +6,9 @@ using Toolkit.Foundation;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class ClassicDesktopStyleApplicationHandler : public class ClassicDesktopStyleApplicationHandler :
INotificationHandler<NavigateEventArgs<IClassicDesktopStyleApplicationLifetime>> IHandler<NavigateEventArgs<IClassicDesktopStyleApplicationLifetime>>
{ {
public Task Handle(NavigateEventArgs<IClassicDesktopStyleApplicationLifetime> args) public void Handle(NavigateEventArgs<IClassicDesktopStyleApplicationLifetime> args)
{ {
if (Application.Current?.ApplicationLifetime is if (Application.Current?.ApplicationLifetime is
IClassicDesktopStyleApplicationLifetime lifeTime) IClassicDesktopStyleApplicationLifetime lifeTime)
@@ -19,7 +19,5 @@ public class ClassicDesktopStyleApplicationHandler :
window.DataContext = args.Content; window.DataContext = args.Content;
} }
} }
return Task.CompletedTask;
} }
} }
+10 -12
View File
@@ -5,9 +5,9 @@ using Toolkit.Foundation;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class ContentControlHandler : public class ContentControlHandler :
INotificationHandler<NavigateEventArgs<ContentControl>> IHandler<NavigateEventArgs<ContentControl>>
{ {
public async Task Handle(NavigateEventArgs<ContentControl> args) public void Handle(NavigateEventArgs<ContentControl> args)
{ {
if (args.Region is ContentControl contentControl) if (args.Region is ContentControl contentControl)
{ {
@@ -19,10 +19,10 @@ public class ContentControlHandler :
control.Loaded -= HandleLoaded; control.Loaded -= HandleLoaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IActivated activated) //if (content is IActivated activated)
{ //{
await activated.OnActivated(); // await activated.OnActivated();
} //}
} }
taskCompletionSource.SetResult(); taskCompletionSource.SetResult();
@@ -33,10 +33,10 @@ public class ContentControlHandler :
control.Unloaded -= HandleLoaded; control.Unloaded -= HandleLoaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IDeactivated deactivated) //if (content is IDeactivated deactivated)
{ //{
await deactivated.OnDeactivated(); // await deactivated.OnDeactivated();
} //}
if (content is IDisposable disposable) if (content is IDisposable disposable)
{ {
@@ -52,8 +52,6 @@ public class ContentControlHandler :
contentControl.Content = null; contentControl.Content = null;
contentControl.Content = control; contentControl.Content = control;
await taskCompletionSource.Task;
} }
} }
} }
+12 -20
View File
@@ -5,9 +5,9 @@ using Toolkit.UI.Controls.Avalonia;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class ContentDialogHandler : public class ContentDialogHandler :
INotificationHandler<NavigateEventArgs<ContentDialog>> IHandler<NavigateEventArgs<ContentDialog>>
{ {
public async Task Handle(NavigateEventArgs<ContentDialog> args) public async void Handle(NavigateEventArgs<ContentDialog> args)
{ {
if (args.Template is ContentDialog dialog) if (args.Template is ContentDialog dialog)
{ {
@@ -76,41 +76,33 @@ public class ContentDialogHandler :
deferral.Complete(); deferral.Complete();
} }
if (!cancelled)
{
if (content is IDeactivating deactivating)
{
await deactivating.OnDeactivating();
}
}
} }
} }
} }
async void HandleOpened(FluentAvalonia.UI.Controls.ContentDialog sender, void HandleOpened(FluentAvalonia.UI.Controls.ContentDialog sender,
EventArgs args) EventArgs args)
{ {
dialog.Opened -= HandleOpened; dialog.Opened -= HandleOpened;
if (dialog.DataContext is object content) if (dialog.DataContext is object content)
{ {
if (content is IActivated activated) //if (content is IActivated activated)
{ //{
await activated.OnActivated(); // activated.OnActivated();
} //}
} }
} }
async void HandleClosed(FluentAvalonia.UI.Controls.ContentDialog sender, void HandleClosed(FluentAvalonia.UI.Controls.ContentDialog sender,
FluentAvalonia.UI.Controls.ContentDialogClosedEventArgs args) FluentAvalonia.UI.Controls.ContentDialogClosedEventArgs args)
{ {
dialog.Closed -= HandleClosed; dialog.Closed -= HandleClosed;
if (dialog.DataContext is object content) if (dialog.DataContext is object content)
{ {
if (content is IDeactivated deactivated) //if (content is IDeactivated deactivated)
{ //{
await deactivated.OnDeactivated(); // deactivated.OnDeactivated();
} //}
} }
} }
+24 -24
View File
@@ -30,10 +30,10 @@ public class ContentTemplate :
control.Loaded -= HandleLoaded; control.Loaded -= HandleLoaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IActivated activated) //if (content is IActivated activated)
{ //{
await activated.OnActivated(); // await activated.OnActivated();
} //}
} }
} }
@@ -41,10 +41,10 @@ public class ContentTemplate :
{ {
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IActivated activated) //if (content is IActivated activated)
{ //{
await activated.OnActivated(); // await activated.OnActivated();
} //}
} }
} }
@@ -53,10 +53,10 @@ public class ContentTemplate :
control.Unloaded -= HandleUnloaded; control.Unloaded -= HandleUnloaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IDeactivated deactivated) //if (content is IDeactivated deactivated)
{ //{
await deactivated.OnDeactivated(); // await deactivated.OnDeactivated();
} //}
} }
} }
@@ -92,10 +92,10 @@ public class ContentTemplate :
control.Loaded -= HandleLoaded; control.Loaded -= HandleLoaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IActivated activated) //if (content is IActivated activated)
{ //{
await activated.OnActivated(); // await activated.OnActivated();
} //}
} }
} }
@@ -103,10 +103,10 @@ public class ContentTemplate :
{ {
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IActivated activated) //if (content is IActivated activated)
{ //{
await activated.OnActivated(); // await activated.OnActivated();
} //}
} }
} }
@@ -115,10 +115,10 @@ public class ContentTemplate :
control.Unloaded -= HandleUnloaded; control.Unloaded -= HandleUnloaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IDeactivated deactivated) //if (content is IDeactivated deactivated)
{ //{
await deactivated.OnDeactivated(); // await deactivated.OnDeactivated();
} //}
} }
} }
+20 -24
View File
@@ -8,10 +8,10 @@ using Toolkit.UI.Controls.Avalonia;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class FrameHandler(ITransientNavigationStore<Frame> navigationStore) : public class FrameHandler(ITransientNavigationStore<Frame> navigationStore) :
INotificationHandler<NavigateEventArgs<Frame>>, IHandler<NavigateEventArgs<Frame>>,
INotificationHandler<NavigateBackEventArgs<Frame>> IHandler<NavigateBackEventArgs<Frame>>
{ {
public Task Handle(NavigateEventArgs<Frame> args) public void Handle(NavigateEventArgs<Frame> args)
{ {
if (args.Region is Frame frame) if (args.Region is Frame frame)
{ {
@@ -32,10 +32,10 @@ public class FrameHandler(ITransientNavigationStore<Frame> navigationStore) :
sender.RemoveHandler(Frame.NavigatedFromEvent, HandleNavigatedFrom); sender.RemoveHandler(Frame.NavigatedFromEvent, HandleNavigatedFrom);
if (sender.DataContext is object content) if (sender.DataContext is object content)
{ {
if (content is IDeactivated deactivated) //if (content is IDeactivated deactivated)
{ //{
await deactivated.OnDeactivated(); // await deactivated.OnDeactivated();
} //}
if (content is IDisposable disposable) if (content is IDisposable disposable)
{ {
@@ -61,10 +61,10 @@ public class FrameHandler(ITransientNavigationStore<Frame> navigationStore) :
if (!args.Cancel) if (!args.Cancel)
{ {
if (content is IDeactivating deactivating) //if (content is IDeactivating deactivating)
{ //{
await deactivating.OnDeactivating(); // await deactivating.OnDeactivating();
} //}
} }
} }
} }
@@ -72,10 +72,10 @@ public class FrameHandler(ITransientNavigationStore<Frame> navigationStore) :
sender.AddHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom); sender.AddHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom);
if (sender.DataContext is object content) if (sender.DataContext is object content)
{ {
if (content is IActivated activated) //if (content is IActivated activated)
{ //{
await activated.OnActivated(); // await activated.OnActivated();
} //}
} }
async void HandleUnloaded(object? _, RoutedEventArgs __) async void HandleUnloaded(object? _, RoutedEventArgs __)
@@ -87,10 +87,10 @@ public class FrameHandler(ITransientNavigationStore<Frame> navigationStore) :
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IDeactivated deactivated) //if (content is IDeactivated deactivated)
{ //{
await deactivated.OnDeactivated(); // await deactivated.OnDeactivated();
} //}
if (content is IDisposable disposable) if (content is IDisposable disposable)
{ {
@@ -206,17 +206,13 @@ public class FrameHandler(ITransientNavigationStore<Frame> navigationStore) :
} }
} }
} }
return Task.CompletedTask;
} }
public Task Handle(NavigateBackEventArgs<Frame> args) public void Handle(NavigateBackEventArgs<Frame> args)
{ {
if (args.Context is Frame frame) if (args.Context is Frame frame)
{ {
frame.GoBack(); frame.GoBack();
} }
return Task.CompletedTask;
} }
} }
@@ -26,19 +26,20 @@ public static class IServiceCollectionExtensions
services.AddTransient<IContentTemplate, ContentTemplate>(); services.AddTransient<IContentTemplate, ContentTemplate>();
services.AddTransient<INavigationRegion, NavigationRegion>(); services.AddTransient<INavigationRegion, NavigationRegion>();
services.AddHandler<WriteClipboardHandler>(); services.AddAsyncHandler<WriteEventArgs<Clipboard<object>>, WriteClipboardHandler>();
services.AddHandler<SelectFoldersHandler>(); services.AddAsyncHandler<SelectionEventArgs<FolderFilter>, IReadOnlyCollection<string>?, SelectFoldersHandler>();
services.AddHandler<SelectFilesHandler>(); services.AddAsyncHandler<SelectionEventArgs<FileFilter>, IReadOnlyCollection<string>?, SelectFilesHandler>();
services.AddHandler<ClassicDesktopStyleApplicationHandler>(nameof(IClassicDesktopStyleApplicationLifetime)); services.AddHandler<NavigateEventArgs<IClassicDesktopStyleApplicationLifetime>, ClassicDesktopStyleApplicationHandler>(nameof(IClassicDesktopStyleApplicationLifetime));
services.AddHandler<SingleViewApplicationHandler>(nameof(ISingleViewApplicationLifetime)); services.AddHandler<NavigateEventArgs<ISingleViewApplicationLifetime>, SingleViewApplicationHandler>(nameof(ISingleViewApplicationLifetime));
services.AddHandler<ContentControlHandler>(nameof(ContentControl));
services.AddHandler<FrameHandler>(nameof(Frame)); services.AddHandler<NavigateEventArgs<ContentControl>, ContentControlHandler>(nameof(ContentControl));
services.AddHandler<NavigateEventArgs<Frame>, FrameHandler>(nameof(Frame));
services.TryAddSingleton<ITransientNavigationStore<Frame>, TransientNavigationStore<Frame>>(); services.TryAddSingleton<ITransientNavigationStore<Frame>, TransientNavigationStore<Frame>>();
services.AddHandler<ContentDialogHandler>(nameof(ContentDialog)); services.AddHandler<NavigateEventArgs<ContentDialog>, ContentDialogHandler>(nameof(ContentDialog));
services.AddHandler<TaskDialogHandler>(nameof(TaskDialog)); services.AddHandler<NavigateEventArgs<TaskDialog>, TaskDialogHandler>(nameof(TaskDialog));
services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(provider => new NavigationRegionCollection services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(provider => new NavigationRegionCollection
{ {
@@ -64,18 +65,17 @@ public static class IServiceCollectionExtensions
services.AddTransient<INavigationRegion, NavigationRegion>(); services.AddTransient<INavigationRegion, NavigationRegion>();
services.AddHandler<WriteClipboardHandler>(); services.AddAsyncHandler<WriteEventArgs<Clipboard<object>>, WriteClipboardHandler>();
services.AddHandler<SelectFoldersHandler>(); services.AddAsyncHandler<SelectionEventArgs<FolderFilter>, IReadOnlyCollection<string>?, SelectFoldersHandler>();
services.AddHandler<SelectFilesHandler>(); services.AddAsyncHandler<SelectionEventArgs<FileFilter>, IReadOnlyCollection<string>?, SelectFilesHandler>();
services.AddHandler<ContentControlHandler>(nameof(ContentControl)); services.AddHandler<NavigateEventArgs<ContentControl>, ContentControlHandler>(nameof(ContentControl));
services.AddHandler<FrameHandler>(nameof(Frame)); services.AddHandler<NavigateEventArgs<Frame>, FrameHandler>(nameof(Frame));
services.TryAddSingleton<ITransientNavigationStore<Frame>, TransientNavigationStore<Frame>>();
services.TryAddSingleton(provider.GetRequiredService<ITransientNavigationStore<Frame>>()); services.AddHandler<NavigateEventArgs<ContentDialog>, ContentDialogHandler>(nameof(ContentDialog));
services.AddHandler<NavigateEventArgs<TaskDialog>, TaskDialogHandler>(nameof(TaskDialog));
services.AddHandler<ContentDialogHandler>(nameof(ContentDialog));
services.AddHandler<TaskDialogHandler>(nameof(TaskDialog));
}))); })));
return services; return services;
@@ -6,9 +6,9 @@ using Toolkit.Foundation;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class SingleViewApplicationHandler : public class SingleViewApplicationHandler :
INotificationHandler<NavigateEventArgs<ISingleViewApplicationLifetime>> IHandler<NavigateEventArgs<ISingleViewApplicationLifetime>>
{ {
public Task Handle(NavigateEventArgs<ISingleViewApplicationLifetime> args) public void Handle(NavigateEventArgs<ISingleViewApplicationLifetime> args)
{ {
if (Application.Current?.ApplicationLifetime is if (Application.Current?.ApplicationLifetime is
ISingleViewApplicationLifetime lifeTime) ISingleViewApplicationLifetime lifeTime)
@@ -19,7 +19,5 @@ public class SingleViewApplicationHandler :
control.DataContext = args.Content; control.DataContext = args.Content;
} }
} }
return Task.CompletedTask;
} }
} }
+6 -6
View File
@@ -6,9 +6,9 @@ using Toolkit.UI.Controls.Avalonia;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class TaskDialogHandler(ITopLevelProvider topLevelProvider) : public class TaskDialogHandler(ITopLevelProvider topLevelProvider) :
INotificationHandler<NavigateEventArgs<TaskDialog>> IHandler<NavigateEventArgs<TaskDialog>>
{ {
public async Task Handle(NavigateEventArgs<TaskDialog> args) public async void Handle(NavigateEventArgs<TaskDialog> args)
{ {
if (args.Template is TaskDialog dialog) if (args.Template is TaskDialog dialog)
{ {
@@ -45,10 +45,10 @@ public class TaskDialogHandler(ITopLevelProvider topLevelProvider) :
if (!cancelled) if (!cancelled)
{ {
if (content is IDeactivating deactivating) //if (content is IDeactivating deactivating)
{ //{
await deactivating.OnDeactivating(); // await deactivating.OnDeactivating();
} //}
} }
} }
} }
-9
View File
@@ -1,9 +0,0 @@
namespace Toolkit.Foundation;
public record Activation
{
public static ActivationEventArgs<TSender, TValue> As<TSender, TValue>(TValue value) => new(value);
public static ActivationEventArgs<TSender> As<TSender>() =>
new();
}
-3
View File
@@ -1,3 +0,0 @@
namespace Toolkit.Foundation;
public record ActivationBuilder(IActivation Value, object? Key = null);
@@ -1,7 +0,0 @@
namespace Toolkit.Foundation;
public record ActivationEventArgs<TSynchronize, TValue>(TValue? Value = default) :
IActivation;
public record ActivationEventArgs<TSynchronize>() :
IActivation;
+5 -4
View File
@@ -1,10 +1,11 @@
using Microsoft.Extensions.Hosting; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Hosting;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class AppService(IEnumerable<IInitialization> initializations, public class AppService(IEnumerable<IInitialization> initializations,
IEnumerable<IAsyncInitialization> asyncInitializations, IEnumerable<IAsyncInitialization> asyncInitializations,
IPublisher publisher) : IMessenger messenger) :
IHostedService IHostedService
{ {
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
@@ -18,8 +19,8 @@ public class AppService(IEnumerable<IInitialization> initializations,
{ {
await initialization.Initialize(); await initialization.Initialize();
} }
publisher.Publish<StartedEventArgs>(); messenger.Send<StartedEventArgs>();
} }
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
@@ -0,0 +1,20 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation;
public class AsyncHandlerInitialization<TMessage, TResponse, THandler>(IServiceProvider provider) :
IInitialization where THandler : class, IAsyncHandler<TMessage, TResponse>
where TMessage : class
{
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, AsyncResponseEventArgs<TMessage, TResponse>>(provider,
async (provider, args) => args.Reply(await provider.GetRequiredService<THandler>().Handle(args.Message, args.CancellationToken)));
}
public class AsyncHandlerInitialization<TMessage, THandler>(IServiceProvider provider) :
IInitialization where THandler : class, IAsyncHandler<TMessage>
where TMessage : class
{
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, AsyncResponseEventArgs<TMessage, Unit>>(provider,
async (provider, args) => await provider.GetRequiredService<THandler>().Handle(args.Message, args.CancellationToken));
}
@@ -0,0 +1,11 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Toolkit.Foundation;
public class AsyncResponseEventArgs<TMessage, TResponse> :
AsyncRequestMessage<TResponse>
{
public TMessage? Message { get; set; }
public CancellationToken CancellationToken { get; set; }
}
+3 -4
View File
@@ -1,14 +1,13 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class CommandValueViewModel<TValue>(IServiceProvider provider, public partial class CommandValueViewModel<TValue>(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : IDisposer disposer) :
Observable<TValue>(provider, factory, mediator, publisher, subscriber, disposer) Observable<TValue>(provider, factory, messenger, disposer)
where TValue : notnull where TValue : notnull
{ {
public IRelayCommand InvokeCommand => public IRelayCommand InvokeCommand =>
+3 -4
View File
@@ -1,14 +1,13 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class CommandViewModel(IServiceProvider provider, public partial class CommandViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : IDisposer disposer) :
Observable(provider, factory, mediator, publisher, subscriber, disposer) Observable(provider, factory, messenger, disposer)
{ {
public IRelayCommand InvokeCommand => public IRelayCommand InvokeCommand =>
new AsyncRelayCommand(InvokeAsync); new AsyncRelayCommand(InvokeAsync);
+7 -7
View File
@@ -26,12 +26,12 @@ public class ComponentBuilder :
services.AddSingleton<IDisposer, Disposer>(); services.AddSingleton<IDisposer, Disposer>();
services.AddScoped<SubscriptionCollection>(); //services.AddScoped<SubscriptionCollection>();
services.AddTransient<IHandlerProvider, HandlerProvider>(); //services.AddTransient<IHandlerProvider, HandlerProvider>();
services.AddScoped<ISubscriber, Subscriber>(); //services.AddScoped<ISubscriber, Subscriber>();
services.AddTransient<IPublisher, Publisher>(); //services.AddTransient<IPublisher, Publisher>();
services.AddTransient<IMediator, Mediator>(); //services.AddTransient<IMediator, Mediator>();
services.AddTransient<IValidation, Validation>(); services.AddTransient<IValidation, Validation>();
services.AddTransient<IValidatorCollection, ValidatorCollection>(); services.AddTransient<IValidatorCollection, ValidatorCollection>();
@@ -42,8 +42,8 @@ public class ComponentBuilder :
services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(); services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>();
services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>(); services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>();
services.AddHandler<NavigateHandler>(); //services.AddHandler<NavigateHandler>();
services.AddHandler<NavigateBackHandler>(); //services.AddHandler<NavigateBackHandler>();
}); });
} }
+3 -2
View File
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
@@ -28,7 +29,7 @@ public class ComponentFactory(IServiceProvider provider,
provider.GetRequiredService<IComponentFactory>()); provider.GetRequiredService<IComponentFactory>());
services.AddTransient(_ => services.AddTransient(_ =>
provider.GetRequiredService<IProxyService<IPublisher>>()); provider.GetRequiredService<IProxyService<IMessenger>>());
services.AddTransient(_ => services.AddTransient(_ =>
provider.GetRequiredService<IProxyService<IComponentHostCollection>>()); provider.GetRequiredService<IProxyService<IComponentHostCollection>>());
+3 -2
View File
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
@@ -18,7 +19,7 @@ public class ComponentInitializer(IEnumerable<IComponent> components,
builder.AddServices(services => builder.AddServices(services =>
{ {
services.AddTransient(_ => services.AddTransient(_ =>
provider.GetRequiredService<IProxyService<IPublisher>>()); provider.GetRequiredService<IProxyService<IMessenger>>());
services.AddTransient(_ => services.AddTransient(_ =>
provider.GetRequiredService<IProxyService<IComponentHostCollection>>()); provider.GetRequiredService<IProxyService<IComponentHostCollection>>());
@@ -1,11 +1,11 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class ConfigurationChangedHandler<TConfiguration, TValue>(ConfigurationValue<TConfiguration, TValue> configurationValue) : public class ConfigurationChangedHandler<TConfiguration, TValue>(ConfigurationValue<TConfiguration, TValue> configurationValue) :
INotificationHandler<ChangedEventArgs<TConfiguration>> IHandler<ChangedEventArgs<TConfiguration>>
where TValue : where TValue :
class, new() class, new()
{ {
public Task Handle(ChangedEventArgs<TConfiguration> args) public void Handle(ChangedEventArgs<TConfiguration> args)
{ {
if (args.Sender is TConfiguration configuration) if (args.Sender is TConfiguration configuration)
{ {
@@ -13,7 +13,5 @@ public class ConfigurationChangedHandler<TConfiguration, TValue>(ConfigurationVa
{ {
} }
} }
return Task.CompletedTask;
} }
} }
@@ -1,9 +1,11 @@
namespace Toolkit.Foundation; using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation;
public class ConfigurationInitializer<TConfiguration>(IConfigurationReader<TConfiguration> reader, public class ConfigurationInitializer<TConfiguration>(IConfigurationReader<TConfiguration> reader,
IConfigurationWriter<TConfiguration> writer, IConfigurationWriter<TConfiguration> writer,
IConfigurationFactory<TConfiguration> factory, IConfigurationFactory<TConfiguration> factory,
IPublisher publisher) : IMessenger messenger) :
IConfigurationInitializer<TConfiguration>, IConfigurationInitializer<TConfiguration>,
IInitialization IInitialization
where TConfiguration : where TConfiguration :
@@ -20,6 +22,6 @@ public class ConfigurationInitializer<TConfiguration>(IConfigurationReader<TConf
} }
} }
publisher.PublishUI(new ActivatedEventArgs<TConfiguration>(configuration)); messenger.Send(new ActivatedEventArgs<TConfiguration>(configuration));
} }
} }
+6 -5
View File
@@ -1,12 +1,13 @@
using Microsoft.Extensions.DependencyInjection; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class ConfigurationMonitor<TConfiguration>(string section, public class ConfigurationMonitor<TConfiguration>(string section,
IConfigurationCache cache, IConfigurationCache cache,
IConfigurationFile<TConfiguration> file, IConfigurationFile<TConfiguration> file,
IServiceProvider serviceProvider, IServiceProvider provider,
IPublisher publisher) : IMessenger messenger) :
IConfigurationMonitor<TConfiguration> IConfigurationMonitor<TConfiguration>
where TConfiguration : where TConfiguration :
class class
@@ -18,11 +19,11 @@ public class ConfigurationMonitor<TConfiguration>(string section,
void ChangedHandler(object sender, void ChangedHandler(object sender,
FileSystemEventArgs args) FileSystemEventArgs args)
{ {
if (serviceProvider.GetRequiredKeyedService<IConfigurationDescriptor<TConfiguration>>(section) is if (provider.GetRequiredKeyedService<IConfigurationDescriptor<TConfiguration>>(section) is
IConfigurationDescriptor<TConfiguration> configuration) IConfigurationDescriptor<TConfiguration> configuration)
{ {
cache.Remove(section); cache.Remove(section);
publisher.PublishUI(new ChangedEventArgs<TConfiguration>(configuration.Value)); messenger.Send(new ChangedEventArgs<TConfiguration>(configuration.Value));
} }
} }
@@ -1,39 +1,39 @@
using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class ConfigurationValueViewModel<TConfiguration, TValue>(IServiceProvider provider, public partial class ConfigurationValueViewModel<TConfiguration, TValue>(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TConfiguration configuration, TConfiguration configuration,
IWritableConfiguration<TConfiguration> writer, IWritableConfiguration<TConfiguration> writer,
Func<TConfiguration, TValue?> read, Func<TConfiguration, TValue?> read,
Action<TValue?, TConfiguration> write) : Action<TValue?, TConfiguration> write) :
Observable<TValue>(provider, factory, mediator, publisher, subscriber, disposer), Observable<TValue>(provider, factory, messenger, disposer),
INotificationHandler<ChangedEventArgs<TConfiguration>> IHandler<ChangedEventArgs<TConfiguration>>
where TConfiguration : class where TConfiguration : class
{ {
public async Task Handle(ChangedEventArgs<TConfiguration> args) public void Handle(ChangedEventArgs<TConfiguration> args)
{ {
if (args.Sender is TConfiguration configuration) if (args.Sender is TConfiguration configuration)
{ {
await Task.Run(() => Value = read(configuration)); Value = read(configuration);
} }
} }
public override async Task OnActivated() protected override void OnActivated()
{ {
await Task.Run(() => Value = read(configuration)); Value = read(configuration);
await base.OnActivated(); base.OnActivated();
} }
protected override async void OnChanged(TValue? value) protected override void OnChanged(TValue? value)
{ {
if (IsActivated) if (IsActive)
{ {
await Task.Run(() => writer.Write(args => write(value, args))); writer.Write(args => write(value, args));
} }
base.OnChanged(value); base.OnChanged(value);
@@ -42,7 +42,7 @@ public partial class ConfigurationValueViewModel<TConfiguration, TValue>(IServic
public partial class ConfigurationValueViewModel<TConfiguration, TValue, TItem> : public partial class ConfigurationValueViewModel<TConfiguration, TValue, TItem> :
ObservableCollection<TValue, TItem>, ObservableCollection<TValue, TItem>,
INotificationHandler<ChangedEventArgs<TConfiguration>> IHandler<ChangedEventArgs<TConfiguration>>
where TConfiguration : class where TConfiguration : class
where TItem : notnull, where TItem : notnull,
IDisposable IDisposable
@@ -51,17 +51,16 @@ public partial class ConfigurationValueViewModel<TConfiguration, TValue, TItem>
private readonly Func<TConfiguration, TValue?> read; private readonly Func<TConfiguration, TValue?> read;
private readonly Action<TValue?, TConfiguration> write; private readonly Action<TValue?, TConfiguration> write;
private readonly IWritableConfiguration<TConfiguration> writer; private readonly IWritableConfiguration<TConfiguration> writer;
public ConfigurationValueViewModel(IServiceProvider provider, public ConfigurationValueViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TConfiguration configuration, TConfiguration configuration,
IWritableConfiguration<TConfiguration> writer, IWritableConfiguration<TConfiguration> writer,
Func<TConfiguration, TValue?> read, Func<TConfiguration, TValue?> read,
Action<TValue?, TConfiguration> write, Action<TValue?, TConfiguration> write,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer, value) TValue? value = default) : base(provider, factory, messenger, disposer, value)
{ {
this.configuration = configuration; this.configuration = configuration;
this.writer = writer; this.writer = writer;
@@ -73,16 +72,14 @@ public partial class ConfigurationValueViewModel<TConfiguration, TValue, TItem>
public ConfigurationValueViewModel(IServiceProvider provider, public ConfigurationValueViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
IEnumerable<TItem> items, IEnumerable<TItem> items,
TConfiguration configuration, TConfiguration configuration,
IWritableConfiguration<TConfiguration> writer, IWritableConfiguration<TConfiguration> writer,
Func<TConfiguration, TValue?> read, Func<TConfiguration, TValue?> read,
Action<TValue?, TConfiguration> write, Action<TValue?, TConfiguration> write,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer, items, value) TValue? value = default) : base(provider, factory, messenger, disposer, items, value)
{ {
this.configuration = configuration; this.configuration = configuration;
this.writer = writer; this.writer = writer;
@@ -92,25 +89,25 @@ public partial class ConfigurationValueViewModel<TConfiguration, TValue, TItem>
Value = value; Value = value;
} }
public async Task Handle(ChangedEventArgs<TConfiguration> args) public void Handle(ChangedEventArgs<TConfiguration> args)
{ {
if (args.Sender is TConfiguration configuration) if (args.Sender is TConfiguration configuration)
{ {
await Task.Run(() => Value = read(configuration)); Value = read(configuration);
} }
} }
public override async Task OnActivated() protected override void OnActivated()
{ {
await Task.Run(() => Value = read(configuration)); Value = read(configuration);
await base.OnActivated(); base.OnActivated();
} }
protected override async void OnChanged(TValue? value) protected override void OnChanged(TValue? value)
{ {
if (IsActivated) if (IsActive)
{ {
await Task.Run(() => writer.Write(args => write(value, args))); writer.Write(args => write(value, args));
} }
base.OnChanged(value); base.OnChanged(value);
+12 -11
View File
@@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@@ -20,19 +20,20 @@ public class DefaultHostBuilder :
ComponentHostCollection>(); ComponentHostCollection>();
services.AddSingleton<IDisposer, Disposer>(); services.AddSingleton<IDisposer, Disposer>();
services.AddSingleton<IMessenger, WeakReferenceMessenger>(_ => WeakReferenceMessenger.Default);
services.AddScoped<SubscriptionCollection>(); //services.AddScoped<SubscriptionCollection>();
services.AddTransient<IHandlerProvider, HandlerProvider>(); //services.AddTransient<IHandlerProvider, HandlerProvider>();
services.AddScoped<ISubscriber, Subscriber>(); //services.AddScoped<ISubscriber, Subscriber>();
services.AddTransient<IPublisher, Publisher>(); //services.AddTransient<IPublisher, Publisher>();
services.AddTransient<IMediator, Mediator>(); //services.AddTransient<IMediator, Mediator>();
services.AddTransient<IValidation, Validation>(); services.AddTransient<IValidation, Validation>();
services.AddTransient<IValidatorCollection, ValidatorCollection>(); services.AddTransient<IValidatorCollection, ValidatorCollection>();
services.AddScoped<IProxyService<IPublisher>>(provider => services.AddScoped<IProxyService<IMessenger>>(provider =>
new ProxyService<IPublisher>(provider.GetRequiredService<IPublisher>())); new ProxyService<IMessenger>(provider.GetRequiredService<IMessenger>()));
services.AddScoped<IProxyService<INavigationRegionProvider>>(provider => services.AddScoped<IProxyService<INavigationRegionProvider>>(provider =>
new ProxyService<INavigationRegionProvider>(provider.GetRequiredService<INavigationRegionProvider>())); new ProxyService<INavigationRegionProvider>(provider.GetRequiredService<INavigationRegionProvider>()));
@@ -56,10 +57,10 @@ public class DefaultHostBuilder :
services.AddTransient<IComponentFactory, ComponentFactory>(); services.AddTransient<IComponentFactory, ComponentFactory>();
services.AddTransient<IComponentScopeProvider, ComponentScopeProvider>(); services.AddTransient<IComponentScopeProvider, ComponentScopeProvider>();
services.AddHandler<NavigateHandler>(); //services.AddHandler<NavigateHandler>();
services.AddHandler<NavigateBackHandler>(); //services.AddHandler<NavigateBackHandler>();
services.AddInitialization<ComponentInitializer>(); //services.AddInitialization<ComponentInitializer>();
services.AddHostedService<AppService>(); services.AddHostedService<AppService>();
}); });
} }
@@ -0,0 +1,20 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation;
public class HandlerInitialization<TMessage, TResponse, THandler>(IServiceProvider provider) :
IInitialization where THandler : class, IHandler<TMessage, TResponse>
where TMessage : class
{
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, ResponseEventArgs<TMessage, TResponse>>(provider,
(provider, args) => args.Reply(provider.GetRequiredService<THandler>().Handle(args.Message)));
}
public class HandlerInitialization<TMessage, THandler>(string key, IServiceProvider provider) :
IInitialization where THandler : class, IHandler<TMessage>
where TMessage : class
{
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, TMessage, string>(provider, key,
(provider, args) => provider.GetRequiredKeyedService<THandler>(key).Handle(args));
}
-25
View File
@@ -1,25 +0,0 @@
namespace Toolkit.Foundation;
public class HandlerProvider(SubscriptionCollection subscriptions) :
IHandlerProvider
{
public IEnumerable<object?> Get(object key)
{
var d = subscriptions;
if (subscriptions.TryGetValue(key, out List<WeakReference>? subscribers))
{
foreach (WeakReference weakRef in subscribers.ToArray())
{
object? target = weakRef.Target;
if (target != null)
{
yield return target;
}
else
{
subscribers.Remove(weakRef);
}
}
}
}
}
-25
View File
@@ -1,25 +0,0 @@
namespace Toolkit.Foundation;
public class HandlerWrapper<TRequest, TResponse>(IHandler<TRequest, TResponse> handler,
IEnumerable<IPipelineBehaviour<TRequest, TResponse>> pipelineBehaviours)
where TRequest : notnull
{
private readonly IEnumerable<IPipelineBehaviour<TRequest, TResponse>> pipelineBehaviours =
pipelineBehaviours.Reverse();
public async Task<TResponse> Handle(TRequest request,
CancellationToken cancellationToken)
{
HandlerDelegate<TRequest, TResponse> currentHandler = handler.Handle;
foreach (IPipelineBehaviour<TRequest, TResponse> behaviour in pipelineBehaviours)
{
HandlerDelegate<TRequest, TResponse> previousHandler = currentHandler;
currentHandler = async (args, token) =>
{
return await behaviour.Handle(args, previousHandler, token);
};
}
return await currentHandler(request, cancellationToken);
}
}
-6
View File
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IActivated
{
Task OnActivated();
}
-3
View File
@@ -1,3 +0,0 @@
namespace Toolkit.Foundation;
public interface IActivation;
+13
View File
@@ -0,0 +1,13 @@
namespace Toolkit.Foundation;
public interface IAsyncHandler<TMessage, TResponse> :
IHandler
{
Task<TResponse> Handle(TMessage args, CancellationToken cancellationToken = default);
}
public interface IAsyncHandler<TMessage> :
IHandler
{
Task Handle(TMessage args, CancellationToken cancellationToken = default);
}
-6
View File
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IDeactivated
{
Task OnDeactivated();
}
-6
View File
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IDeactivating
{
Task OnDeactivating();
}
+7 -5
View File
@@ -2,12 +2,14 @@
public interface IHandler; public interface IHandler;
public interface IHandler<in TRequest, TResponse> : public interface IHandler<TMessage> :
IHandler IHandler
{ {
Task<TResponse> Handle(TRequest args, void Handle(TMessage args);
CancellationToken cancellationToken);
} }
public interface IHandler<in TRequest> : public interface IHandler<TMessage, TResponse> :
IHandler<TRequest, Unit>; IHandler
{
TResponse Handle(TMessage args);
}
-6
View File
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IHandlerProvider
{
IEnumerable<object?> Get(object key);
}
+4 -3
View File
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
@@ -163,7 +164,7 @@ public static class IHostBuilderExtension
new ConfigurationInitializer<TConfiguration>(provider.GetRequiredKeyedService<IConfigurationReader<TConfiguration>>(section), new ConfigurationInitializer<TConfiguration>(provider.GetRequiredKeyedService<IConfigurationReader<TConfiguration>>(section),
provider.GetRequiredKeyedService<IConfigurationWriter<TConfiguration>>(section), provider.GetRequiredKeyedService<IConfigurationWriter<TConfiguration>>(section),
provider.GetRequiredKeyedService<IConfigurationFactory<TConfiguration>>(section), provider.GetRequiredKeyedService<IConfigurationFactory<TConfiguration>>(section),
provider.GetRequiredService<IPublisher>())); provider.GetRequiredService<IMessenger>()));
services.AddTransient<IConfigurationInitializer<TConfiguration>, ConfigurationInitializer<TConfiguration>>(provider => services.AddTransient<IConfigurationInitializer<TConfiguration>, ConfigurationInitializer<TConfiguration>>(provider =>
provider.GetRequiredService<IServiceFactory>().Create<ConfigurationInitializer<TConfiguration>>(section)); provider.GetRequiredService<IServiceFactory>().Create<ConfigurationInitializer<TConfiguration>>(section));
@@ -188,7 +189,7 @@ public static class IHostBuilderExtension
provider.GetRequiredKeyedService<IConfigurationCache>(section), provider.GetRequiredKeyedService<IConfigurationCache>(section),
provider.GetRequiredKeyedService<IConfigurationFile<TConfiguration>>(section), provider.GetRequiredKeyedService<IConfigurationFile<TConfiguration>>(section),
provider.GetRequiredService<IServiceProvider>(), provider.GetRequiredService<IServiceProvider>(),
provider.GetRequiredService<IPublisher>())); provider.GetRequiredService<IMessenger>()));
} }
}); });
-29
View File
@@ -1,29 +0,0 @@
namespace Toolkit.Foundation;
public interface IMediator
{
Task<object?> Handle(Type responseType,
object message,
object? key = null,
CancellationToken cancellationToken = default);
Task<TResponse?> Handle<TMessage, TResponse>(TMessage message,
object? key = null,
CancellationToken cancellationToken = default)
where TMessage : notnull;
IAsyncEnumerable<object?> HandleAsyncMany(Type responseType,
object message,
object? key = null,
CancellationToken cancellationToken = default);
IAsyncEnumerable<TResponse?> HandleAsyncMany<TMessage, TResponse>(TMessage message,
object? key = null,
CancellationToken cancellationToken = default)
where TMessage : notnull;
Task<IList<TResponse?>> HandleMany<TMessage, TResponse>(TMessage message,
object? key = null,
CancellationToken cancellationToken = default)
where TMessage : notnull;
}
-6
View File
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IMediatorRequired
{
IMediator Mediator { get; }
}
-3
View File
@@ -1,3 +0,0 @@
namespace Toolkit.Foundation;
public interface IMessage;
@@ -0,0 +1,19 @@
using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation;
public static class IMessengerExtensions
{
public static TResponse Send<TMessage, TResponse>(this IMessenger messenger)
where TMessage : class, new()
{
ResponseEventArgs<TMessage, TResponse> args = messenger.Send(new ResponseEventArgs<TMessage, TResponse> { Message = new TMessage() });
return args.Response;
}
public static void Send<TMessage>(this IMessenger messenger, string key)
where TMessage : class, new() => messenger.Send(new TMessage(), key);
public static async Task<TResponse> SendAsync<TMessage, TResponse>(this IMessenger messenger)
where TMessage : class, new() => await messenger.Send(new AsyncResponseEventArgs<TMessage, TResponse> { Message = new TMessage() });
}
+8
View File
@@ -0,0 +1,8 @@
using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation;
public interface IMessengerRequired
{
IMessenger Messenger { get; }
}
@@ -1,7 +0,0 @@
namespace Toolkit.Foundation;
public interface INotificationHandler<in TMessage> :
IHandler
{
Task Handle(TMessage args);
}
+4 -2
View File
@@ -1,11 +1,13 @@
namespace Toolkit.Foundation; using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation;
public interface IObservableViewModel : public interface IObservableViewModel :
IDisposable IDisposable
{ {
public IDisposer Disposer { get; } public IDisposer Disposer { get; }
public IPublisher Publisher { get; } public IMessenger Messenger { get; }
public IServiceFactory Factory { get; } public IServiceFactory Factory { get; }
-15
View File
@@ -1,15 +0,0 @@
namespace Toolkit.Foundation;
public interface IPipelineBehaviour<TMessage, TResponse>
{
Task<TResponse> Handle(TMessage message,
HandlerDelegate<TMessage, TResponse> next,
CancellationToken cancellationToken = default);
}
public interface IPipelineBehaviour<TMessage>
{
Task Handle(TMessage message,
NotificationHandlerDelegate<TMessage> next,
CancellationToken cancellationToken = default);
}
-37
View File
@@ -1,37 +0,0 @@
namespace Toolkit.Foundation;
public interface IPublisher
{
void Publish<TMessage>(object? key = null)
where TMessage : new();
void Publish<TMessage>(TMessage message)
where TMessage : notnull;
void Publish<TMessage>(TMessage message,
object? key = null)
where TMessage : notnull;
void Publish(object message,
Func<Func<Task>, Task> marshal,
object? key = null);
void Publish<TMessage>()
where TMessage : new();
void Publish(object message);
void PublishUI<TMessage>(TMessage message,
object? key = null) where TMessage : notnull;
void PublishUI<TMessage>(object? key = null)
where TMessage : new();
void PublishUI<TMessage>(TMessage message)
where TMessage : notnull;
void PublishUI(object message);
void PublishUI<TMessage>()
where TMessage : new();
}
-6
View File
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IPublisherRequired
{
IPublisher Publisher { get; }
}
@@ -38,61 +38,59 @@ public static class IServiceCollectionExtensions
return services; return services;
} }
public static IServiceCollection AddHandler<THandler>(this IServiceCollection services, public static IServiceCollection AddHandler<TMessage, TResponse, THandler>(this IServiceCollection services,
string key) ServiceLifetime lifetime = ServiceLifetime.Transient)
where THandler : IHandler where THandler : class, IHandler<TMessage, TResponse>
where TMessage : class
{ {
return AddHandler<THandler>(services, ServiceLifetime.Transient, key); services.Add(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
services.AddInitialization<HandlerInitialization<TMessage, TResponse, THandler>>();
return services;
} }
public static IServiceCollection AddHandler<THandler>(this IServiceCollection services, public static IServiceCollection AddAsyncHandler<TMessage, TResponse, THandler>(this IServiceCollection services,
ServiceLifetime lifetime = ServiceLifetime.Transient)
where THandler : class, IAsyncHandler<TMessage, TResponse>
where TMessage : class
{
services.Add(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
services.AddInitialization<AsyncHandlerInitialization<TMessage, TResponse, THandler>>();
return services;
}
public static IServiceCollection AddAsyncHandler<TMessage, THandler>(this IServiceCollection services,
ServiceLifetime lifetime = ServiceLifetime.Transient)
where THandler : class, IAsyncHandler<TMessage>
where TMessage : class
{
services.Add(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
services.AddInitialization<AsyncHandlerInitialization<TMessage, THandler>>();
return services;
}
public static IServiceCollection AddHandler<TMessage, THandler>(this IServiceCollection services,
string key)
where THandler : class, IHandler<TMessage>
where TMessage : class => AddHandler<TMessage, THandler>(services, ServiceLifetime.Transient, key);
public static IServiceCollection AddHandler<TMessage, THandler>(this IServiceCollection services,
ServiceLifetime lifetime = ServiceLifetime.Transient, ServiceLifetime lifetime = ServiceLifetime.Transient,
string? key = null) string? key = null)
where THandler : IHandler where THandler : class, IHandler<TMessage>
where TMessage : class
{ {
if (typeof(THandler).GetInterfaces() is Type[] handlerTypes) if (key is { Length: > 0})
{ {
foreach (Type handlerType in handlerTypes) services.Add(new ServiceDescriptor(typeof(THandler), key, typeof(THandler), lifetime));
{ services.AddInitialization<HandlerInitialization<TMessage, THandler>>(key);
if (handlerType.Name == typeof(INotificationHandler<>).Name && }
handlerType.GetGenericArguments() is { Length: 1 } notificationHandlerArguments) else
{ {
Type notificationType = notificationHandlerArguments[0]; services.Add(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
Type wrapperType = typeof(NotificationHandlerWrapper<>).MakeGenericType(notificationType); services.AddInitialization<HandlerInitialization<TMessage, THandler>>();
string preferredKey = $"{(key is not null ? $"{key}:" : "")}{notificationType}";
services.Add(new ServiceDescriptor(typeof(INotificationHandler<>)
.MakeGenericType(notificationType), preferredKey, typeof(THandler), lifetime));
services.Add(new ServiceDescriptor(wrapperType, preferredKey, (provider, registeredKey) =>
provider.GetService<IServiceFactory>()?.Create(wrapperType,
provider.GetRequiredKeyedService(typeof(INotificationHandler<>).MakeGenericType(notificationType), registeredKey),
provider.GetServices(typeof(IPipelineBehaviour<>)
.MakeGenericType(notificationType)))!, lifetime));
}
if (handlerType.Name == typeof(IHandler<,>).Name &&
handlerType.GetGenericArguments() is { Length: 2 } handlerArguments)
{
Type requestType = handlerArguments[0];
Type responseType = handlerArguments[1];
Type wrapperType = typeof(HandlerWrapper<,>).MakeGenericType(requestType, responseType);
string preferredKey = $"{(key is not null ? $"{key}:" : "")}{wrapperType}";
services.Add(new ServiceDescriptor(typeof(THandler), preferredKey,
typeof(THandler), lifetime));
services.Add(new ServiceDescriptor(wrapperType, preferredKey, (provider, actualKey) =>
provider.GetService<IServiceFactory>()?.Create(wrapperType,
provider.GetRequiredKeyedService<THandler>(preferredKey),
provider.GetServices(typeof(IPipelineBehaviour<,>)
.MakeGenericType(requestType, responseType)))!, lifetime));
}
}
return services;
} }
return services; return services;
@@ -106,6 +104,15 @@ public static class IServiceCollectionExtensions
return services; return services;
} }
public static IServiceCollection AddInitialization<TInitialization>(this IServiceCollection services,
params object[] parameters)
where TInitialization : class,
IInitialization
{
services.AddTransient<IInitialization>(provider => provider.GetRequiredService<IServiceFactory>().Create<TInitialization>(parameters));
return services;
}
public static IServiceCollection AddRange(this IServiceCollection services, public static IServiceCollection AddRange(this IServiceCollection services,
IServiceCollection fromServices) IServiceCollection fromServices)
{ {
-8
View File
@@ -1,8 +0,0 @@
namespace Toolkit.Foundation;
public interface ISubscriber
{
void Subscribe(object subscriber);
void Unsubscribe(object subscriber);
}
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface ISubscriberRequired
{
ISubscriber Subscription { get; }
}
-165
View File
@@ -1,165 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Toolkit.Foundation;
public class Mediator(IHandlerProvider handlerProvider,
IServiceProvider provider) :
IMediator
{
public async Task<TResponse?> Handle<TMessage, TResponse>(TMessage message,
object? key = null,
CancellationToken cancellationToken = default)
where TMessage : notnull
{
Type messageType = message.GetType();
Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(messageType, typeof(TResponse));
key = $"{(key is not null ? $"{key}:" : "")}{handlerType}";
List<object?> handlers = GetHandlers(message, handlerType, key);
foreach (object? handler in handlers)
{
MethodInfo? handleMethod = handler?.GetType().GetMethod("Handle", [message.GetType(), typeof(CancellationToken)]);
if (handleMethod is not null)
{
return await (Task<TResponse?>)handleMethod.Invoke(handler, [message, cancellationToken])!;
}
}
return default;
}
public async Task<object?> Handle(Type responseType,
object message,
object? key = null,
CancellationToken cancellationToken = default)
{
Type messageType = message.GetType();
Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(message.GetType(), responseType);
key = $"{(key is not null ? $"{key}:" : "")}{handlerType}";
List<object?> handlers = GetHandlers(message, handlerType, key);
foreach (object? handler in handlers)
{
MethodInfo? handleMethod = handler?.GetType().GetMethod("Handle",
[messageType, typeof(CancellationToken)]);
if (handleMethod is not null)
{
dynamic task = handleMethod.Invoke(handler, [message, cancellationToken])!;
await task;
return task.Result;
}
}
return default;
}
public async IAsyncEnumerable<object?> HandleAsyncMany(Type responseType,
object message,
object? key = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Type messageType = message.GetType();
Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(message.GetType(), responseType);
key = $"{(key is not null ? $"{key}:" : "")}{handlerType}";
List<object?> handlers = GetHandlers(message, handlerType, key);
foreach (object? handler in handlers)
{
MethodInfo? handleMethod = handler?.GetType().GetMethod("Handle",
[messageType, typeof(CancellationToken)]);
if (handleMethod is not null)
{
yield return await (Task<object?>)handleMethod.Invoke(handler, [message, cancellationToken])!;
}
}
}
public async IAsyncEnumerable<TResponse?> HandleAsyncMany<TMessage, TResponse>(TMessage message,
object? key = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
where TMessage : notnull
{
Type messageType = message.GetType();
Type handlerType = typeof(HandlerWrapper<,>).MakeGenericType(messageType, typeof(TResponse));
key = $"{(key is not null ? $"{key}:" : "")}{handlerType}";
List<object?> handlers = GetHandlers(message, handlerType, key);
foreach (object? handler in handlers)
{
MethodInfo? handleMethod = handler?.GetType().GetMethod("Handle", [message.GetType(), typeof(CancellationToken)]);
if (handleMethod is not null)
{
yield return await (Task<TResponse?>)handleMethod.Invoke(handler, [message, cancellationToken])!;
}
}
}
public async Task<List<object?>> HandleMany(Type responseType,
object message,
object? key = null,
CancellationToken cancellationToken = default)
{
List<object?> responses = [];
await foreach (object? response in HandleAsyncMany(responseType, message, key, cancellationToken))
{
responses.Add(response);
}
return responses;
}
public async Task<IList<TResponse?>> HandleMany<TMessage, TResponse>(TMessage message,
object? key = null,
CancellationToken cancellationToken = default)
where TMessage : notnull
{
List<TResponse?> responses = [];
await foreach (TResponse? response in HandleAsyncMany<TMessage, TResponse>(message, key, cancellationToken))
{
responses.Add(response);
}
return responses;
}
private List<object?> GetHandlers(object message,
Type handlerWrapperType,
object? key)
{
Type messageType = message.GetType();
Dictionary<Type, List<object?>> handlers = [];
void AddHandlers(IEnumerable<object?> newHandlers)
{
foreach (object? handler in newHandlers)
{
if (handler == null) continue;
Type serviceType = handler.GetType();
if (!handlers.TryGetValue(serviceType, out List<object?>? handlerList))
{
handlerList = [];
handlers.Add(serviceType, handlerList);
}
handlerList.Add(handler);
}
}
IEnumerable<object?> keyedServices = key is not null ? provider.GetKeyedServices(handlerWrapperType, key) :
provider.GetServices(handlerWrapperType);
AddHandlers(keyedServices);
if (key is not null)
{
IEnumerable<object?> additionalHandlers = handlerProvider.Get(key);
AddHandlers(additionalHandlers);
}
return handlers.SelectMany(entry => entry.Value).ToList();
}
}
+2 -4
View File
@@ -3,9 +3,9 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class NavigateBackHandler(IComponentScopeProvider provider) : public class NavigateBackHandler(IComponentScopeProvider provider) :
INotificationHandler<NavigateBackEventArgs> IHandler<NavigateBackEventArgs>
{ {
public Task Handle(NavigateBackEventArgs args) public void Handle(NavigateBackEventArgs args)
{ {
if (provider.Get(args.Scope ?? "Root") if (provider.Get(args.Scope ?? "Root")
is ComponentScopeDescriptor descriptor) is ComponentScopeDescriptor descriptor)
@@ -16,7 +16,5 @@ public class NavigateBackHandler(IComponentScopeProvider provider) :
navigationScope.Back(args.Context); navigationScope.Back(args.Context);
} }
} }
return Task.CompletedTask;
} }
} }
+2 -4
View File
@@ -4,9 +4,9 @@ namespace Toolkit.Foundation;
public class NavigateHandler(NamedComponent scope, public class NavigateHandler(NamedComponent scope,
IComponentScopeProvider componentScopeProvider) : IComponentScopeProvider componentScopeProvider) :
INotificationHandler<NavigateEventArgs> IHandler<NavigateEventArgs>
{ {
public Task Handle(NavigateEventArgs args) public void Handle(NavigateEventArgs args)
{ {
INavigation? navigation = null; INavigation? navigation = null;
if (args.Scope is "self" || args.Scope is "new") if (args.Scope is "self" || args.Scope is "new")
@@ -34,7 +34,5 @@ public class NavigateHandler(NamedComponent scope,
navigation?.Navigate(args.Route, args.Sender, navigation?.Navigate(args.Route, args.Sender,
args.Region, args.Navigated, args.Parameters); args.Region, args.Navigated, args.Parameters);
return Task.CompletedTask;
} }
} }
+5 -4
View File
@@ -1,11 +1,12 @@
using Microsoft.Extensions.DependencyInjection; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class Navigation(IServiceProvider provider, public class Navigation(IServiceProvider provider,
INavigationRegionProvider navigationRegionProvider, INavigationRegionProvider navigationRegionProvider,
IContentFactory contentFactory, IContentFactory contentFactory,
IPublisher publisher) : IMessenger messenger) :
INavigation INavigation
{ {
public void Navigate(string route, public void Navigate(string route,
@@ -71,7 +72,7 @@ public class Navigation(IServiceProvider provider,
if (Activator.CreateInstance(navigateEventType, [region, template, content, sender, parameters]) if (Activator.CreateInstance(navigateEventType, [region, template, content, sender, parameters])
is object navigateEvent) is object navigateEvent)
{ {
publisher.Publish(navigateEvent, navigationType.Name); messenger.Send(navigateEvent, navigationType.Name);
if (currentSegmentIndex == segmentCount) if (currentSegmentIndex == segmentCount)
{ {
navigated?.Invoke(this, EventArgs.Empty); navigated?.Invoke(this, EventArgs.Empty);
@@ -97,7 +98,7 @@ public class Navigation(IServiceProvider provider,
Type navigateType = typeof(NavigateBackEventArgs<>).MakeGenericType(navigationType); Type navigateType = typeof(NavigateBackEventArgs<>).MakeGenericType(navigationType);
if (Activator.CreateInstance(navigateType, [region]) is object navigate) if (Activator.CreateInstance(navigateType, [region]) is object navigate)
{ {
publisher.Publish(navigate, navigationType.Name); messenger.Send(navigate, navigationType.Name);
} }
} }
} }
@@ -1,3 +0,0 @@
namespace Toolkit.Foundation;
public delegate Task NotificationHandlerDelegate<TMessage>(TMessage message);
@@ -1,23 +0,0 @@
namespace Toolkit.Foundation;
public class NotificationHandlerWrapper<TMessage>(INotificationHandler<TMessage> handler,
IEnumerable<IPipelineBehaviour<TMessage>> pipelineBehaviours)
{
private readonly IEnumerable<IPipelineBehaviour<TMessage>> pipelineBehaviours =
pipelineBehaviours.Reverse();
public async Task Handle(TMessage message)
{
NotificationHandlerDelegate<TMessage> currentHandler = handler.Handle;
foreach (IPipelineBehaviour<TMessage> behaviour in pipelineBehaviours)
{
NotificationHandlerDelegate<TMessage> previousHandler = currentHandler;
currentHandler = async (args) =>
{
await behaviour.Handle(args, previousHandler);
};
}
await currentHandler(message);
}
}
+9 -63
View File
@@ -1,55 +1,30 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class Observable(IServiceProvider provider, public partial class Observable(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : IDisposer disposer) :
ObservableObject, ObservableRecipient,
IObservableViewModel, IObservableViewModel,
IActivityIndicator, IActivityIndicator,
IInitialization,
IActivated,
IDeactivating,
IDeactivated,
IDisposable, IDisposable,
IServiceProviderRequired, IServiceProviderRequired,
IServiceFactoryRequired, IServiceFactoryRequired,
IMediatorRequired, IMessengerRequired,
IPublisherRequired,
IDisposerRequired IDisposerRequired
{ {
private readonly Dictionary<string, object> trackedProperties = []; private readonly Dictionary<string, object> trackedProperties = [];
[ObservableProperty]
private bool isActivated;
[ObservableProperty]
private bool isActive;
[ObservableProperty]
private bool isInitialized;
public IDisposer Disposer { get; } = disposer; public IDisposer Disposer { get; } = disposer;
public IServiceFactory Factory { get; } = factory; public IServiceFactory Factory { get; } = factory;
public IMediator Mediator { get; } = mediator;
public IServiceProvider Provider { get; } = provider; public IServiceProvider Provider { get; } = provider;
public IPublisher Publisher { get; } = publisher; public IMessenger Messenger { get; } = messenger;
public ISubscriber Subscriber { get; } = subscriber;
public virtual Task OnActivated()
{
IsActivated = true;
return Task.CompletedTask;
}
public void Commit() public void Commit()
{ {
@@ -59,37 +34,12 @@ public partial class Observable(IServiceProvider provider,
} }
} }
public virtual Task OnDeactivated()
{
IsActivated = false;
return Task.CompletedTask;
}
public virtual Task OnDeactivating() =>
Task.CompletedTask;
public virtual void Dispose() public virtual void Dispose()
{ {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
Disposer.Dispose(this); Disposer.Dispose(this);
} }
public virtual void OnInitialize()
{
}
public virtual void Initialize()
{
if (IsInitialized)
{
return;
}
IsInitialized = true;
Subscriber.Subscribe(this);
OnInitialize();
}
public void Revert() public void Revert()
{ {
foreach (object trackedProperty in trackedProperties.Values) foreach (object trackedProperty in trackedProperties.Values)
@@ -116,11 +66,9 @@ public partial class Observable<TValue> :
public Observable(IServiceProvider provider, public Observable(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer) TValue? value = default) : base(provider, factory, messenger, disposer)
{ {
Value = value; Value = value;
} }
@@ -143,12 +91,10 @@ public partial class Observable<TKey, TValue> :
public Observable(IServiceProvider provider, public Observable(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TKey key, TKey key,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer) TValue? value = default) : base(provider, factory, messenger, disposer)
{ {
Key = key; Key = key;
Value = value; Value = value;
+93 -279
View File
@@ -1,5 +1,5 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Collections; using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
@@ -8,12 +8,8 @@ using System.Reactive.Disposables;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class ObservableCollection<TViewModel> : public partial class ObservableCollection<TViewModel> :
ObservableObject, ObservableRecipient,
IObservableCollectionViewModel<TViewModel>, IObservableCollectionViewModel<TViewModel>,
IInitialization,
IActivated,
IDeactivating,
IDeactivated,
IList<TViewModel>, IList<TViewModel>,
IList, IList,
IReadOnlyList<TViewModel>, IReadOnlyList<TViewModel>,
@@ -21,17 +17,15 @@ public partial class ObservableCollection<TViewModel> :
ICollectionSynchronization<TViewModel>, ICollectionSynchronization<TViewModel>,
IServiceProviderRequired, IServiceProviderRequired,
IServiceFactoryRequired, IServiceFactoryRequired,
IMediatorRequired, IMessengerRequired,
IPublisherRequired,
IDisposerRequired, IDisposerRequired,
INotificationHandler<RemoveEventArgs<TViewModel>>, IRecipient<RemoveEventArgs<TViewModel>>,
INotificationHandler<RemoveAtEventArgs<TViewModel>>, IRecipient<RemoveAtEventArgs<TViewModel>>,
INotificationHandler<CreateEventArgs<TViewModel>>, IRecipient<CreateEventArgs<TViewModel>>,
INotificationHandler<InsertEventArgs<TViewModel>>, IRecipient<InsertEventArgs<TViewModel>>,
INotificationHandler<MoveEventArgs<TViewModel>>, IRecipient<MoveEventArgs<TViewModel>>,
INotificationHandler<MoveToEventArgs<TViewModel>>, IRecipient<MoveToEventArgs<TViewModel>>,
INotificationHandler<ReplaceEventArgs<TViewModel>>, IRecipient<ReplaceEventArgs<TViewModel>>
INotificationHandler<SelectionEventArgs<TViewModel>>
where TViewModel : notnull, where TViewModel : notnull,
IDisposable IDisposable
{ {
@@ -39,8 +33,6 @@ public partial class ObservableCollection<TViewModel> :
private readonly IDispatcher dispatcher; private readonly IDispatcher dispatcher;
private readonly Queue<object> pendingEvents = [];
private readonly Dictionary<string, object> trackedProperties = []; private readonly Dictionary<string, object> trackedProperties = [];
[ObservableProperty] [ObservableProperty]
@@ -49,28 +41,20 @@ public partial class ObservableCollection<TViewModel> :
private Func<TViewModel>? defaultSelectionFactory; private Func<TViewModel>? defaultSelectionFactory;
[ObservableProperty] [ObservableProperty]
private bool isActivated; private bool initialized;
private bool isClearing; private bool isClearing;
[ObservableProperty]
private bool isInitialized;
[ObservableProperty] [ObservableProperty]
private TViewModel? selectedItem; private TViewModel? selectedItem;
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher, IDisposer disposer) : base(messenger)
ISubscriber subscriber,
IDisposer disposer)
{ {
Provider = provider; Provider = provider;
Factory = factory; Factory = factory;
Mediator = mediator;
Publisher = publisher;
Subscriber = subscriber;
Disposer = disposer; Disposer = disposer;
dispatcher = Provider.GetRequiredService<IDispatcher>(); dispatcher = Provider.GetRequiredService<IDispatcher>();
@@ -79,17 +63,12 @@ public partial class ObservableCollection<TViewModel> :
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
IEnumerable<TViewModel> items) IEnumerable<TViewModel> items) : base(messenger)
{ {
Provider = provider; Provider = provider;
Factory = factory; Factory = factory;
Mediator = mediator;
Publisher = publisher;
Subscriber = subscriber;
Disposer = disposer; Disposer = disposer;
dispatcher = Provider.GetRequiredService<IDispatcher>(); dispatcher = Provider.GetRequiredService<IDispatcher>();
@@ -105,21 +84,12 @@ public partial class ObservableCollection<TViewModel> :
public IServiceFactory Factory { get; private set; } public IServiceFactory Factory { get; private set; }
bool IList.IsFixedSize => false; bool IList.IsFixedSize => false;
bool ICollection<TViewModel>.IsReadOnly => false; bool ICollection<TViewModel>.IsReadOnly => false;
bool IList.IsReadOnly => false; bool IList.IsReadOnly => false;
bool ICollection.IsSynchronized => false; bool ICollection.IsSynchronized => false;
public new IMessenger Messenger { get; private set; }
public IMediator Mediator { get; }
public IServiceProvider Provider { get; private set; } public IServiceProvider Provider { get; private set; }
public IPublisher Publisher { get; private set; }
public ISubscriber Subscriber { get; }
object ICollection.SyncRoot => this; object ICollection.SyncRoot => this;
public TViewModel this[int index] public TViewModel this[int index]
@@ -147,29 +117,6 @@ public partial class ObservableCollection<TViewModel> :
} }
} }
public void Activate(Func<ActivationBuilder> activateDelegate,
bool reset = false)
{
if (reset)
{
Clear();
}
ActivationBuilder builder = activateDelegate.Invoke();
Publisher.Publish(builder.Value, builder.Key);
}
public void Activate(bool reset = false)
{
if (reset)
{
Clear();
}
ActivationBuilder builder = ActivationBuilder();
Publisher.PublishUI(builder.Value, builder.Key);
}
public TViewModel Add<T>(params object?[] parameters) public TViewModel Add<T>(params object?[] parameters)
where T : TViewModel where T : TViewModel
{ {
@@ -289,129 +236,6 @@ public partial class ObservableCollection<TViewModel> :
IEnumerator IEnumerable.GetEnumerator() => IEnumerator IEnumerable.GetEnumerator() =>
((IEnumerable)collection).GetEnumerator(); ((IEnumerable)collection).GetEnumerator();
public Task Handle(RemoveEventArgs<TViewModel> args)
{
if (IsActivated)
{
foreach (TViewModel item in this.ToList())
{
if (args.Sender is not null && args.Sender.Equals(item))
{
Remove(item);
}
}
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask;
}
public Task Handle(CreateEventArgs<TViewModel> args)
{
if (IsActivated)
{
if (args.Sender is TViewModel item)
{
Add(item);
}
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask;
}
public Task Handle(InsertEventArgs<TViewModel> args)
{
if (IsActivated)
{
if (args.Sender is TViewModel item)
{
Insert(args.Index, item);
}
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask;
}
public Task Handle(MoveToEventArgs<TViewModel> args)
{
if (IsActivated)
{
Move(args.OldIndex, args.NewIndex);
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask;
}
public Task Handle(MoveEventArgs<TViewModel> args)
{
if (IsActivated)
{
if (args.Sender is TViewModel item)
{
Move(args.Index, item);
}
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask;
}
public Task Handle(ReplaceEventArgs<TViewModel> args)
{
if (IsActivated)
{
if (args.Sender is TViewModel item)
{
Replace(args.Index, item);
}
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask;
}
public Task Handle(RemoveAtEventArgs<TViewModel> args)
{
if (IsActivated)
{
int index = args.Index;
if (index >= 0 && index <= Count - 1)
{
RemoveAt(index);
}
}
else
{
pendingEvents.Enqueue(args);
}
return Task.CompletedTask;
}
public Task Handle(SelectionEventArgs<TViewModel> args) =>
Task.CompletedTask;
public int IndexOf(TViewModel item) => public int IndexOf(TViewModel item) =>
collection.IndexOf(item); collection.IndexOf(item);
@@ -419,22 +243,6 @@ public partial class ObservableCollection<TViewModel> :
IsCompatibleObject(value) ? IsCompatibleObject(value) ?
IndexOf((TViewModel)value!) : -1; IndexOf((TViewModel)value!) : -1;
[RelayCommand]
public virtual void Initialize()
{
if (IsInitialized)
{
return;
}
IsInitialized = true;
Subscriber.Subscribe(this);
OnInitialize();
Activate();
}
public TViewModel Insert<T>(int index = 0, public TViewModel Insert<T>(int index = 0,
params object?[] parameters) params object?[] parameters)
where T : where T :
@@ -519,31 +327,67 @@ public partial class ObservableCollection<TViewModel> :
return true; return true;
} }
public virtual Task OnActivated()
{
IsActivated = true;
while (pendingEvents.Count > 0)
{
object current = pendingEvents.Dequeue();
Handle((dynamic)current);
}
return Task.CompletedTask;
}
public virtual Task OnDeactivated()
{
IsActivated = false;
return Task.CompletedTask;
}
public virtual Task OnDeactivating() =>
Task.CompletedTask;
public virtual void OnInitialize() public virtual void OnInitialize()
{ {
} }
public void Receive(RemoveEventArgs<TViewModel> args)
{
foreach (TViewModel item in this.ToList())
{
if (args.Sender is not null && args.Sender.Equals(item))
{
Remove(item);
}
}
}
public void Receive(CreateEventArgs<TViewModel> args)
{
if (args.Sender is TViewModel item)
{
Add(item);
}
}
public void Receive(InsertEventArgs<TViewModel> args)
{
if (args.Sender is TViewModel item)
{
Insert(args.Index, item);
}
}
public void Receive(MoveToEventArgs<TViewModel> args)
{
Move(args.OldIndex, args.NewIndex);
}
public void Receive(MoveEventArgs<TViewModel> args)
{
if (args.Sender is TViewModel item)
{
Move(args.Index, item);
}
}
public void Receive(ReplaceEventArgs<TViewModel> args)
{
if (args.Sender is TViewModel item)
{
Replace(args.Index, item);
}
}
public void Receive(RemoveAtEventArgs<TViewModel> args)
{
int index = args.Index;
if (index >= 0 && index <= Count - 1)
{
RemoveAt(index);
}
}
public bool Remove(TViewModel item) public bool Remove(TViewModel item)
{ {
int index = collection.IndexOf(item); int index = collection.IndexOf(item);
@@ -637,7 +481,7 @@ public partial class ObservableCollection<TViewModel> :
} }
} }
public void SetSource(IList<TViewModel> source, Func<TViewModel>? defaultSelectionFactory) public void SetSource(IList<TViewModel> source, Func<TViewModel>? defaultSelectionFactory)
{ {
foreach (TViewModel item in source) foreach (TViewModel item in source)
{ {
@@ -666,9 +510,6 @@ public partial class ObservableCollection<TViewModel> :
} }
} }
protected virtual ActivationBuilder ActivationBuilder() =>
new(new ActivationEventArgs<TViewModel>());
protected virtual void ClearItems() => protected virtual void ClearItems() =>
collection.Clear(); collection.Clear();
@@ -692,10 +533,6 @@ public partial class ObservableCollection<TViewModel> :
collection.Insert(index > Count ? Count : index, item); collection.Insert(index > Count ? Count : index, item);
} }
protected virtual void OnSelectedItemChanged()
{
}
protected virtual void RemoveItem(int index) => protected virtual void RemoveItem(int index) =>
collection.RemoveAt(index); collection.RemoveAt(index);
@@ -711,18 +548,6 @@ public partial class ObservableCollection<TViewModel> :
CollectionChanged?.Invoke(this, args); CollectionChanged?.Invoke(this, args);
} }
partial void OnIsActivatedChanged(bool value)
{
if (value)
{
while (pendingEvents.Count > 0)
{
object current = pendingEvents.Dequeue();
Handle((dynamic)current);
}
}
}
partial void OnSelectedItemChanged(TViewModel? oldValue, TViewModel? newValue) partial void OnSelectedItemChanged(TViewModel? oldValue, TViewModel? newValue)
{ {
if (oldValue is ISelectable oldSelection) if (oldValue is ISelectable oldSelection)
@@ -735,8 +560,7 @@ public partial class ObservableCollection<TViewModel> :
newSelection.IsSelected = true; newSelection.IsSelected = true;
} }
Publisher.Publish(Selection.As(SelectedItem)); Messenger.Send(Selection.As(SelectedItem));
OnSelectedItemChanged();
} }
private void SourceCollectionChanged(object? sender, private void SourceCollectionChanged(object? sender,
@@ -785,6 +609,7 @@ public partial class ObservableCollection<TViewModel> :
break; break;
} }
} }
private void UpdateSelection(TViewModel item) private void UpdateSelection(TViewModel item)
{ {
if (item is ISelectable newSelection) if (item is ISelectable newSelection)
@@ -809,32 +634,27 @@ public partial class ObservableCollection<TValue, TViewModel> :
[ObservableProperty] [ObservableProperty]
private TValue? value; private TValue? value;
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer) TValue? value = default) : base(provider, factory, messenger, disposer)
{ {
Value = value; Value = value;
} }
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher, IDisposer disposer,
ISubscriber subscriber,
IDisposer disposer,
IEnumerable<TViewModel> items, IEnumerable<TViewModel> items,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer, items) TValue? value = default) : base(provider, factory, messenger, disposer, items)
{ {
Value = value; Value = value;
} }
protected virtual void OnChanged(TValue? value) protected virtual void OnChanged(TValue? value)
{ {
} }
partial void OnValueChanged(TValue? value) => OnChanged(value); partial void OnValueChanged(TValue? value) => OnChanged(value);
@@ -852,12 +672,10 @@ public partial class ObservableCollection<TViewModel, TKey, TValue> :
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TKey key, TKey key,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer) TValue? value = default) : base(provider, factory, messenger, disposer)
{ {
Key = key; Key = key;
Value = value; Value = value;
@@ -865,13 +683,11 @@ public partial class ObservableCollection<TViewModel, TKey, TValue> :
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
IEnumerable<TViewModel> items, IEnumerable<TViewModel> items,
TKey key, TKey key,
TValue? value = default) : base(provider, factory, mediator, publisher, subscriber, disposer, items) TValue? value = default) : base(provider, factory, messenger, disposer, items)
{ {
Key = key; Key = key;
Value = value; Value = value;
@@ -883,20 +699,18 @@ public class ObservableCollection :
{ {
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher, IDisposer disposer) : base(provider, factory, messenger, disposer)
ISubscriber subscriber,
IDisposer disposer) : base(provider, factory, mediator, publisher, subscriber, disposer)
{ {
} }
public ObservableCollection(IServiceProvider provider, public ObservableCollection(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMessenger messenger,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
IEnumerable<IDisposable> items) : base(provider, factory, mediator, publisher, subscriber, disposer, items) IEnumerable<IDisposable> items) : base(provider, factory, messenger, disposer, items)
{ {
} }
} }
-88
View File
@@ -1,88 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace Toolkit.Foundation;
public class Publisher(IHandlerProvider handlerProvider,
IServiceFactory serviceFactory,
IServiceProvider serviceProvider,
IDispatcher dispatcher) :
IPublisher
{
public void Publish<TMessage>(object? key = null)
where TMessage : new() =>
Publish(serviceFactory.Create<TMessage>() ?? new TMessage(), async args => await args(), key);
public void Publish<TMessage>(TMessage message)
where TMessage : notnull =>
Publish(message, async args => await args(), null);
public void Publish<TMessage>(TMessage message,
object? key = null)
where TMessage : notnull =>
Publish(message, async args => await args(), key);
public void Publish(object message,
Func<Func<Task>, Task> marshal,
object? key = null)
{
Type notificationType = message.GetType();
Type handlerType = typeof(NotificationHandlerWrapper<>)
.MakeGenericType(notificationType);
key = $"{(key is not null ? $"{key}:" : "")}{notificationType}";
List<object?> handlers = [];
foreach (object? handler in handlerProvider.Get(key))
{
handlers.Add(handler);
}
foreach (object? handler in serviceProvider.GetKeyedServices(handlerType, key))
{
handlers.Add(handler);
}
foreach (object? handler in handlers)
{
if (handler is not null)
{
MethodInfo? handleMethod = handler.GetType().GetMethod("Handle",
[notificationType]);
if (handleMethod is not null)
{
marshal(() => (Task)handleMethod.Invoke(handler, new object[]
{ message })!);
}
}
}
}
public void Publish(object message) => Publish(message,
async args => await args(), null);
public void Publish<TMessage>()
where TMessage : new() =>
Publish(new TMessage(), async args => await args(), null);
public void PublishUI<TMessage>(object? key = null)
where TMessage : new() =>
Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), key);
public void PublishUI<TMessage>(TMessage message)
where TMessage : notnull =>
Publish(message, args => dispatcher.Invoke(async () => await args()), null);
public void PublishUI<TMessage>(TMessage message,
object? key = null)
where TMessage : notnull =>
Publish(message, args => dispatcher.Invoke(async () => await args()), key);
public void PublishUI<TMessage>()
where TMessage : new() =>
Publish(new TMessage(), args => dispatcher.Invoke(async () => await args()), null);
public void PublishUI(object message) => Publish(message, args =>
dispatcher.Invoke(async () => await args()), null);
}
+9
View File
@@ -0,0 +1,9 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace Toolkit.Foundation;
public class ResponseEventArgs<TMessage, TResponse> :
RequestMessage<TResponse>
{
public TMessage? Message { get; set; }
}
+1 -1
View File
@@ -1,7 +1,7 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class SelectFilesHandler(IFileProvider fileProvider) : public class SelectFilesHandler(IFileProvider fileProvider) :
IHandler<SelectionEventArgs<FileFilter>, IReadOnlyCollection<string>?> IAsyncHandler<SelectionEventArgs<FileFilter>, IReadOnlyCollection<string>?>
{ {
public async Task<IReadOnlyCollection<string>?> Handle(SelectionEventArgs<FileFilter> args, public async Task<IReadOnlyCollection<string>?> Handle(SelectionEventArgs<FileFilter> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
+3 -3
View File
@@ -2,10 +2,10 @@
public class SelectFoldersHandler(IFolderProvider folderProvider) : public class SelectFoldersHandler(IFolderProvider folderProvider) :
IHandler<SelectionEventArgs<FolderFilter>, IReadOnlyCollection<string>?> IAsyncHandler<SelectionEventArgs<FolderFilter>, IReadOnlyCollection<string>?>
{ {
public async Task<IReadOnlyCollection<string>?> Handle(SelectionEventArgs<FolderFilter> args, public async Task<IReadOnlyCollection<string>?> Handle(SelectionEventArgs<FolderFilter> args,
CancellationToken cancellationToken) CancellationToken cancellationToken = default)
{ {
if (args.Sender is FolderFilter filter) if (args.Sender is FolderFilter filter)
{ {
-165
View File
@@ -1,165 +0,0 @@
using System.Reactive.Disposables;
namespace Toolkit.Foundation;
public class Subscriber(SubscriptionCollection subscriptions,
IDisposer disposer) :
ISubscriber
{
public void Subscribe(object subscriber)
{
IDictionary<Type, List<object>> subscribers = GetSubscriptionKeys(subscriber);
foreach (Type handlerType in GetHandlerInterfaces(subscriber.GetType()))
{
if (handlerType.Name == typeof(INotificationHandler<>).Name &&
handlerType.GetGenericArguments() is { Length: 1 } notificationHandlerArguments)
{
Type notificationType = notificationHandlerArguments[0];
AddSubscriptions(subscriber, subscribers, notificationType);
}
if (handlerType.Name == typeof(IHandler<,>).Name &&
handlerType.GetGenericArguments() is { Length: 2 } handlerArguments)
{
Type requestType = handlerArguments[0];
Type responseType = handlerArguments[1];
Type wrapperType = typeof(HandlerWrapper<,>).MakeGenericType(requestType, responseType);
AddSubscriptions(subscriber, subscribers, wrapperType);
}
}
}
public void Unsubscribe(object subscriber)
{
IDictionary<Type, List<object>> subscribers = GetSubscriptionKeys(subscriber);
foreach (Type handlerType in GetHandlerInterfaces(subscriber.GetType()))
{
if (handlerType.Name == typeof(INotificationHandler<>).Name &&
handlerType.GetGenericArguments() is { Length: 1 } notificationHandlerArguments)
{
Type notificationType = notificationHandlerArguments[0];
RemoveSubscriptions(subscriber, subscribers, notificationType);
}
if (handlerType.Name == typeof(IHandler<,>).Name &&
handlerType.GetGenericArguments() is { Length: 2 } handlerArguments)
{
Type requestType = handlerArguments[0];
Type responseType = handlerArguments[1];
Type wrapperType = typeof(HandlerWrapper<,>).MakeGenericType(requestType, responseType);
RemoveSubscriptions(subscriber, subscribers, wrapperType);
}
}
}
private void AddOrUpdateSubscription(object subscriber,
string preferredKey)
{
subscriptions.AddOrUpdate(preferredKey, _ => new List<WeakReference> { new(subscriber) }, (_, collection) =>
{
collection.Add(new WeakReference(subscriber));
return collection;
});
disposer.Add(subscriber, Disposable.Create(() => {
RemoveSubscription(subscriber, preferredKey);
}));
}
private void AddSubscriptions(object subscriber,
IDictionary<Type, List<object>> subscribers,
Type handlerType)
{
if (subscribers.TryGetValue(handlerType, out List<object>? keys))
{
foreach (object key in keys)
{
string preferredKey = $"{(key is not null ? $"{key}:" : "")}{handlerType}";
AddOrUpdateSubscription(subscriber, preferredKey);
}
}
else
{
string preferredKey = $"{handlerType}";
AddOrUpdateSubscription(subscriber, preferredKey);
}
}
private IEnumerable<Type> GetHandlerInterfaces(Type handlerType) =>
handlerType.GetInterfaces().Where(interfaceType =>
{
Type? definition = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : null;
return definition == typeof(INotificationHandler<>) ||
definition == typeof(IHandler<>) ||
definition == typeof(IHandler<,>);
});
private IDictionary<Type, List<object>> GetSubscriptionKeys(object subscriber)
{
Dictionary<Type, List<object>> keys = [];
foreach (NotificationAttribute attribute in subscriber.GetAttributes<NotificationAttribute>())
{
if (!keys.TryGetValue(attribute.Type, out List<object>? value))
{
value = ([]);
keys[attribute.Type] = value;
}
if (subscriber.GetPropertyValue(() => attribute.Key) is object key)
{
value.Add(key);
}
else
{
value.Add(attribute.Key);
}
}
return keys;
}
private void RemoveSubscription(object subscriber,
string key)
{
if (subscriptions.TryGetValue(key, out List<WeakReference>? subscribers))
{
for (int i = subscribers.Count - 1; i >= 0; i--)
{
if (subscribers[i].Target == subscriber)
{
subscribers.RemoveAt(i);
}
}
if (subscribers.Count == 0)
{
subscriptions.TryRemove(key, out _);
}
}
}
private void RemoveSubscriptions(object subscriber,
IDictionary<Type, List<object>> subscribers,
Type handlerType)
{
if (subscribers.TryGetValue(handlerType, out List<object>? keys))
{
foreach (object key in keys)
{
string subscriptionKey = $"{(key is not null ? $"{key}:" : "")}{handlerType}";
RemoveSubscription(subscriber, subscriptionKey);
}
}
else
{
string subscriptionKey = $"{handlerType}";
RemoveSubscription(subscriber, subscriptionKey);
}
}
}
@@ -1,6 +0,0 @@
using System.Collections.Concurrent;
namespace Toolkit.Foundation;
public class SubscriptionCollection :
ConcurrentDictionary<object, List<WeakReference>>;
+3 -2
View File
@@ -1,9 +1,10 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class WriteClipboardHandler(IClipboardWriter clipboardWriter) : public class WriteClipboardHandler(IClipboardWriter clipboardWriter) :
INotificationHandler<WriteEventArgs<Clipboard<object>>> IAsyncHandler<WriteEventArgs<Clipboard<object>>>
{ {
public async Task Handle(WriteEventArgs<Clipboard<object>> args) public async Task Handle(WriteEventArgs<Clipboard<object>> args,
CancellationToken cancellationToken = default)
{ {
if (args.Sender is Clipboard<object> clipboard) if (args.Sender is Clipboard<object> clipboard)
{ {
+63
View File
@@ -0,0 +1,63 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Toolkit.Foundation;
namespace Toolkit.Test
{
public record User;
public record Hello;
public class LoggedInUserHandler3 : IHandler<Hello, User>
{
public LoggedInUserHandler3()
{
}
public User Handle(Hello? args)
{
return new User();
}
}
public class LoggedInUserHandler4 : IHandler<Hello>
{
public LoggedInUserHandler4()
{
}
public void Handle(Hello? args)
{
}
}
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
var test = DefaultHostBuilder.Create()
.ConfigureServices((context, services) =>
{
services.AddHandler<Hello, User, LoggedInUserHandler3>();
services.AddHandler<Hello, LoggedInUserHandler4>("Foo");
services.AddHostedService<AppService>();
});
var d = test.Build();
d.Start();
var dd = d.Services.GetRequiredService<IMessenger>();
// var sdd = d.Services.GetRequiredService<LoggedInUserHandler3>();
// dd.Send<Hello, User>();
dd.Send<Hello>("Foo");
}
}
}
+18
View File
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
</ItemGroup>
</Project>
+2 -1
View File
@@ -2,6 +2,7 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.Xaml.Interactivity; using Avalonia.Xaml.Interactivity;
using CommunityToolkit.Mvvm.Messaging;
using System.Collections.Immutable; using System.Collections.Immutable;
using Toolkit.Foundation; using Toolkit.Foundation;
@@ -63,7 +64,7 @@ public class NavigateAction :
ImmutableDictionary<string, object>? parameters = Parameters is { Count: > 0 } ? Parameters.ToImmutableDictionary(x => x.Key, x => x.Value) : ImmutableDictionary<string, object>? parameters = Parameters is { Count: > 0 } ? Parameters.ToImmutableDictionary(x => x.Key, x => x.Value) :
ImmutableDictionary<string, object>.Empty; ImmutableDictionary<string, object>.Empty;
observableViewModel.Publisher.Publish(new NavigateEventArgs(Route, Region == this ? content : Region, Scope ?? null, observableViewModel.Messenger.Send(new NavigateEventArgs(Route, Region == this ? content : Region, Scope ?? null,
content.DataContext, Navigated, parameters)); content.DataContext, Navigated, parameters));
} }
} }
+2 -1
View File
@@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Xaml.Interactivity; using Avalonia.Xaml.Interactivity;
using CommunityToolkit.Mvvm.Messaging;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Toolkit.UI.Avalonia; namespace Toolkit.UI.Avalonia;
@@ -34,7 +35,7 @@ public class NavigateBackAction :
{ {
if (control.DataContext is IObservableViewModel observableViewModel) if (control.DataContext is IObservableViewModel observableViewModel)
{ {
observableViewModel.Publisher.Publish(new NavigateBackEventArgs(Region observableViewModel.Messenger.Send(new NavigateBackEventArgs(Region
?? null, Scope ?? null)); ?? null, Scope ?? null));
} }
} }
+8 -8
View File
@@ -1,13 +1,13 @@
using System.Diagnostics.CodeAnalysis; using CommunityToolkit.Mvvm.Messaging;
using System.Diagnostics.CodeAnalysis;
using System.Drawing; using System.Drawing;
using Toolkit.Foundation;
using Windows.Win32; using Windows.Win32;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging; using Windows.Win32.UI.WindowsAndMessaging;
namespace Toolkit.Windows; namespace Toolkit.Windows;
public class PointerMonitor(IPublisher publisher) : public class PointerMonitor(IMessenger messenger) :
IPointerMonitor IPointerMonitor
{ {
private bool isDisposed; private bool isDisposed;
@@ -99,16 +99,16 @@ public class PointerMonitor(IPublisher publisher) :
isPointerDrag = true; isPointerDrag = true;
} }
publisher.Publish(new PointerDragEventArgs(location)); messenger.Send(new PointerDragEventArgs(location));
} }
publisher.Publish(new PointerMovedEventArgs(location)); messenger.Send(new PointerMovedEventArgs(location));
} }
private void SendPointerPressed(PointerLocation location, PointerButton button) private void SendPointerPressed(PointerLocation location, PointerButton button)
{ {
isPointerPressed = true; isPointerPressed = true;
publisher.Publish(new PointerPressedEventArgs(location, button)); messenger.Send(new PointerPressedEventArgs(location, button));
} }
private void SendPointerReleased(PointerLocation location, PointerButton button) private void SendPointerReleased(PointerLocation location, PointerButton button)
@@ -118,11 +118,11 @@ public class PointerMonitor(IPublisher publisher) :
if (isPointerDrag) if (isPointerDrag)
{ {
isPointerDrag = false; isPointerDrag = false;
publisher.Publish(new PointerDragReleasedEventArgs(location, button)); messenger.Send(new PointerDragReleasedEventArgs(location, button));
} }
isPointerPressed = false; isPointerPressed = false;
publisher.Publish(new PointerReleasedEventArgs(location, button)); messenger.Send(new PointerReleasedEventArgs(location, button));
} }
} }
+19 -25
View File
@@ -1,23 +1,25 @@
using Toolkit.Foundation; using CommunityToolkit.Mvvm.Messaging;
using Toolkit.Foundation;
using Windows.Win32; using Windows.Win32;
namespace Toolkit.Windows; namespace Toolkit.Windows;
public class Taskbar(ISubscriber subscriber, public class Taskbar(IMessenger messenger,
IPublisher publisher,
IDisposer disposer) : IDisposer disposer) :
ITaskbar, ITaskbar,
INotificationHandler<WndProcEventArgs>, IRecipient<WndProcEventArgs>,
INotificationHandler<PointerReleasedEventArgs>, IRecipient<PointerReleasedEventArgs>,
INotificationHandler<PointerMovedEventArgs>, IRecipient<PointerMovedEventArgs>,
INotificationHandler<PointerDragEventArgs> IRecipient<PointerDragEventArgs>
{ {
private bool isDrag; private bool isDrag;
private bool isWithinBounds; private bool isWithinBounds;
public void Dispose() public void Dispose()
{ {
messenger.UnregisterAll(this);
disposer.Dispose(this); disposer.Dispose(this);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@@ -40,29 +42,25 @@ public class Taskbar(ISubscriber subscriber,
public IntPtr GetHandle() => WindowHelper.Find("Shell_TrayWnd"); public IntPtr GetHandle() => WindowHelper.Find("Shell_TrayWnd");
public Task Handle(WndProcEventArgs args) public void Receive(WndProcEventArgs args)
{ {
if (args.Message == PInvoke.WM_TASKBARCREATED || if (args.Message == PInvoke.WM_TASKBARCREATED ||
args.Message == (int)WndProcMessages.WM_SETTINGCHANGE && args.Message == (int)WndProcMessages.WM_SETTINGCHANGE &&
(int)args.WParam == PInvoke.SPI_SETWORKAREA) (int)args.WParam == PInvoke.SPI_SETWORKAREA)
{ {
publisher.Publish<TaskbarChangedEventArgs>(); messenger.Send<TaskbarChangedEventArgs>();
} }
return Task.CompletedTask;
} }
public Task Handle(PointerReleasedEventArgs args) public void Receive(PointerReleasedEventArgs args)
{ {
if (isDrag) if (isDrag)
{ {
isDrag = false; isDrag = false;
} }
return Task.CompletedTask;
} }
public Task Handle(PointerMovedEventArgs args) public void Receive(PointerMovedEventArgs args)
{ {
nint taskbarHandle = GetHandle(); nint taskbarHandle = GetHandle();
if (WindowHelper.TryGetBounds(taskbarHandle, out Rect? rect)) if (WindowHelper.TryGetBounds(taskbarHandle, out Rect? rect))
@@ -71,11 +69,11 @@ public class Taskbar(ISubscriber subscriber,
{ {
if (isWithinBounds) if (isWithinBounds)
{ {
return Task.CompletedTask; return;
} }
isWithinBounds = true; isWithinBounds = true;
publisher.Publish<TaskbarEnteredEventArgs>(); messenger.Send<TaskbarEnteredEventArgs>();
} }
else else
{ {
@@ -83,21 +81,19 @@ public class Taskbar(ISubscriber subscriber,
isWithinBounds = false; isWithinBounds = false;
} }
} }
return Task.CompletedTask;
} }
public Task Handle(PointerDragEventArgs args) public void Receive(PointerDragEventArgs args)
{ {
if (isWithinBounds) if (isWithinBounds)
{ {
if (isDrag) if (isDrag)
{ {
publisher.Publish<TaskbarDragOverEventArgs>(); messenger.Send<TaskbarDragOverEventArgs>();
} }
else else
{ {
publisher.Publish<TaskbarDragEnterEventArgs>(); messenger.Send<TaskbarDragEnterEventArgs>();
} }
isDrag = true; isDrag = true;
@@ -106,9 +102,7 @@ public class Taskbar(ISubscriber subscriber,
{ {
isDrag = false; isDrag = false;
} }
return Task.CompletedTask;
} }
public void Initialize() => subscriber.Subscribe(this); public void Initialize() => messenger.RegisterAll(this);
} }
+19 -23
View File
@@ -1,31 +1,31 @@
using Toolkit.Foundation; using CommunityToolkit.Mvvm.Messaging;
using Toolkit.Foundation;
namespace Toolkit.Windows; namespace Toolkit.Windows;
public class TaskbarButton : public class TaskbarButton :
ITaskbarButton, ITaskbarButton,
INotificationHandler<PointerReleasedEventArgs>, IRecipient<PointerReleasedEventArgs>,
INotificationHandler<PointerMovedEventArgs>, IRecipient<PointerMovedEventArgs>,
INotificationHandler<PointerDragEventArgs> IRecipient<PointerDragEventArgs>
{ {
private readonly IPublisher publisher; private readonly IMessenger messenger;
private readonly IDisposer disposer; private readonly IDisposer disposer;
private bool isWithinBounds; private bool isWithinBounds;
private bool isDrag; private bool isDrag;
public TaskbarButton(string name, public TaskbarButton(string name,
Rect rect, Rect rect,
IPublisher publisher, IMessenger messenger,
ISubscriber subscriber,
IDisposer disposer) IDisposer disposer)
{ {
this.publisher = publisher; this.messenger = messenger;
this.disposer = disposer; this.disposer = disposer;
Name = name; Name = name;
Rect = rect; Rect = rect;
subscriber.Subscribe(this); messenger.RegisterAll(this);
} }
public Rect Rect { get; internal set; } public Rect Rect { get; internal set; }
@@ -34,36 +34,36 @@ public class TaskbarButton :
public void Dispose() public void Dispose()
{ {
messenger.UnregisterAll(this);
disposer.Dispose(this); disposer.Dispose(this);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public Task Handle(PointerReleasedEventArgs args) public void Receive(PointerReleasedEventArgs args)
{ {
if (!isDrag && isWithinBounds) if (!isDrag && isWithinBounds)
{ {
publisher.Publish(new TaskbarButtonInvokedEventArgs(this)); messenger.Send(new TaskbarButtonInvokedEventArgs(this));
} }
if (isDrag) if (isDrag)
{ {
isDrag = false; isDrag = false;
} }
return Task.CompletedTask;
} }
public Task Handle(PointerDragEventArgs args) public void Receive(PointerDragEventArgs args)
{ {
if (isWithinBounds) if (isWithinBounds)
{ {
if (isDrag) if (isDrag)
{ {
publisher.Publish(new TaskbarButtonDragOverEventArgs(this)); messenger.Send(new TaskbarButtonDragOverEventArgs(this));
} }
else else
{ {
publisher.Publish(new TaskbarButtonDragEnterEventArgs(this)); messenger.Send(new TaskbarButtonDragEnterEventArgs(this));
} }
isDrag = true; isDrag = true;
@@ -72,28 +72,24 @@ public class TaskbarButton :
{ {
isDrag = false; isDrag = false;
} }
return Task.CompletedTask;
} }
public Task Handle(PointerMovedEventArgs args) public void Receive(PointerMovedEventArgs args)
{ {
if (args.Location.IsWithinBounds(Rect)) if (args.Location.IsWithinBounds(Rect))
{ {
if (isWithinBounds) if (isWithinBounds)
{ {
return Task.CompletedTask; return;
} }
isWithinBounds = true; isWithinBounds = true;
publisher.Publish(new TaskbarButtonEnteredEventArgs(this)); messenger.Send(new TaskbarButtonEnteredEventArgs(this));
} }
else else
{ {
isDrag = false; isDrag = false;
isWithinBounds = false; isWithinBounds = false;
} }
return Task.CompletedTask;
} }
} }
+7 -13
View File
@@ -1,4 +1,4 @@
using System.Diagnostics; using CommunityToolkit.Mvvm.Messaging;
using Toolkit.Foundation; using Toolkit.Foundation;
using UIAutomationClient; using UIAutomationClient;
@@ -10,7 +10,7 @@ public class TaskbarButtonMonitor :
private readonly IDispatcherTimer dispatcherTimer; private readonly IDispatcherTimer dispatcherTimer;
private readonly IDispatcherTimerFactory dispatcherTimerFactory; private readonly IDispatcherTimerFactory dispatcherTimerFactory;
private readonly IDisposer disposer; private readonly IDisposer disposer;
private readonly IPublisher publisher; private readonly IMessenger messenger;
private readonly IServiceFactory serviceFactory; private readonly IServiceFactory serviceFactory;
private readonly Dictionary<string, TaskbarButton> taskbarButtons = []; private readonly Dictionary<string, TaskbarButton> taskbarButtons = [];
private readonly ITaskbarList taskbarList; private readonly ITaskbarList taskbarList;
@@ -20,13 +20,13 @@ public class TaskbarButtonMonitor :
private IntPtr taskListHandle; private IntPtr taskListHandle;
public TaskbarButtonMonitor(ITaskbarList taskbarList, public TaskbarButtonMonitor(ITaskbarList taskbarList,
IPublisher publisher, IMessenger messenger,
IDispatcherTimerFactory dispatcherTimerFactory, IDispatcherTimerFactory dispatcherTimerFactory,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IDisposer disposer) IDisposer disposer)
{ {
this.taskbarList = taskbarList; this.taskbarList = taskbarList;
this.publisher = publisher; this.messenger = messenger;
this.dispatcherTimerFactory = dispatcherTimerFactory; this.dispatcherTimerFactory = dispatcherTimerFactory;
this.serviceFactory = serviceFactory; this.serviceFactory = serviceFactory;
this.disposer = disposer; this.disposer = disposer;
@@ -121,10 +121,8 @@ public class TaskbarButtonMonitor :
string key = buttonToRemove.Key; string key = buttonToRemove.Key;
TaskbarButton button = buttonToRemove.Value; TaskbarButton button = buttonToRemove.Value;
Debug.WriteLine($"{key} button removed");
taskbarButtons.Remove(key); taskbarButtons.Remove(key);
publisher.Publish(new TaskbarButtonRemovedEventArgs(button)); messenger.Send(new TaskbarButtonRemovedEventArgs(button));
button.Dispose(); button.Dispose();
} }
@@ -141,17 +139,13 @@ public class TaskbarButtonMonitor :
if (taskbarButtons.TryGetValue(name, out TaskbarButton? taskbarButton)) if (taskbarButtons.TryGetValue(name, out TaskbarButton? taskbarButton))
{ {
Debug.WriteLine($"{name} button updated");
taskbarButtons[name].Rect = rect; taskbarButtons[name].Rect = rect;
publisher.Publish(new TaskbarButtonUpdatedEventArgs(taskbarButtons[name])); messenger.Send(new TaskbarButtonUpdatedEventArgs(taskbarButtons[name]));
} }
else else
{ {
Debug.WriteLine($"{name} button added");
taskbarButtons.Add(name, serviceFactory.Create<TaskbarButton>(name, rect)); taskbarButtons.Add(name, serviceFactory.Create<TaskbarButton>(name, rect));
publisher.Publish(new TaskbarButtonCreatedEventArgs(taskbarButtons[name])); messenger.Send(new TaskbarButtonCreatedEventArgs(taskbarButtons[name]));
} }
} }
} }
+4 -4
View File
@@ -1,4 +1,4 @@
using Toolkit.Foundation; using CommunityToolkit.Mvvm.Messaging;
using Windows.Win32; using Windows.Win32;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi; using Windows.Win32.Graphics.Gdi;
@@ -6,16 +6,16 @@ using Windows.Win32.UI.WindowsAndMessaging;
namespace Toolkit.Windows; namespace Toolkit.Windows;
public class WndProcMonitor(IPublisher publisher) : public class WndProcMonitor(IMessenger messenger) :
IWndProcMonitor IWndProcMonitor
{ {
private WNDPROC? handler; private WNDPROC? handler;
private readonly IPublisher publisher = publisher;
public IntPtr Handle { get; private set; } public IntPtr Handle { get; private set; }
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
PInvoke.DestroyWindow((HWND)Handle); PInvoke.DestroyWindow((HWND)Handle);
} }
@@ -54,7 +54,7 @@ public class WndProcMonitor(IPublisher publisher) :
private LRESULT Wndproc(HWND param0, uint param1, WPARAM param2, LPARAM param3) private LRESULT Wndproc(HWND param0, uint param1, WPARAM param2, LPARAM param3)
{ {
publisher.Publish(new WndProcEventArgs(param1, (uint)param2.Value, (uint)param3.Value)); messenger.Send(new WndProcEventArgs(param1, (uint)param2.Value, (uint)param3.Value));
return PInvoke.DefWindowProc(param0, param1, param2, param3); return PInvoke.DefWindowProc(param0, param1, param2, param3);
} }
+14
View File
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit.Windows", "Toolkit.
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.WinUI", "Toolkit.WinUI\Toolkit.WinUI.csproj", "{D90BFF29-B38C-432E-849A-28C5A88A6DDD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.WinUI", "Toolkit.WinUI\Toolkit.WinUI.csproj", "{D90BFF29-B38C-432E-849A-28C5A88A6DDD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.Test", "Toolkit.Test\Toolkit.Test.csproj", "{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -97,6 +99,18 @@ Global
{D90BFF29-B38C-432E-849A-28C5A88A6DDD}.Release|x64.Build.0 = Release|Any CPU {D90BFF29-B38C-432E-849A-28C5A88A6DDD}.Release|x64.Build.0 = Release|Any CPU
{D90BFF29-B38C-432E-849A-28C5A88A6DDD}.Release|x86.ActiveCfg = Release|Any CPU {D90BFF29-B38C-432E-849A-28C5A88A6DDD}.Release|x86.ActiveCfg = Release|Any CPU
{D90BFF29-B38C-432E-849A-28C5A88A6DDD}.Release|x86.Build.0 = Release|Any CPU {D90BFF29-B38C-432E-849A-28C5A88A6DDD}.Release|x86.Build.0 = Release|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Debug|x64.ActiveCfg = Debug|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Debug|x64.Build.0 = Debug|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Debug|x86.ActiveCfg = Debug|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Debug|x86.Build.0 = Debug|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Release|Any CPU.Build.0 = Release|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Release|x64.ActiveCfg = Release|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Release|x64.Build.0 = Release|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Release|x86.ActiveCfg = Release|Any CPU
{E125F8EF-6B66-400F-9CDE-BC3DB1463F4E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE