Replace Mediator with Messenger
This commit is contained in:
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,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();
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
@@ -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; }
|
||||||
|
}
|
||||||
@@ -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 =>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>>());
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Toolkit.Foundation;
|
|
||||||
|
|
||||||
public interface IActivated
|
|
||||||
{
|
|
||||||
Task OnActivated();
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Toolkit.Foundation;
|
|
||||||
|
|
||||||
public interface IActivation;
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Toolkit.Foundation;
|
|
||||||
|
|
||||||
public interface IDeactivated
|
|
||||||
{
|
|
||||||
Task OnDeactivated();
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Toolkit.Foundation;
|
|
||||||
|
|
||||||
public interface IDeactivating
|
|
||||||
{
|
|
||||||
Task OnDeactivating();
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Toolkit.Foundation;
|
|
||||||
|
|
||||||
public interface IHandlerProvider
|
|
||||||
{
|
|
||||||
IEnumerable<object?> Get(object key);
|
|
||||||
}
|
|
||||||
@@ -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>()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Toolkit.Foundation;
|
|
||||||
|
|
||||||
public interface IMediatorRequired
|
|
||||||
{
|
|
||||||
IMediator Mediator { get; }
|
|
||||||
}
|
|
||||||
@@ -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() });
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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,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)
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>>;
|
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user