Initial navigaiton work; opening Windows

This commit is contained in:
TheXamlGuy
2024-02-04 22:06:55 +00:00
parent 110ac407f5
commit 43f96fd4f0
19 changed files with 228 additions and 94 deletions
+18
View File
@@ -0,0 +1,18 @@
using Microsoft.UI.Xaml;
namespace Hyperbar.UI.Windows;
public class WindowHandler :
INavigationHandler<Window>
{
public Task Handle(Navigate<Window> args,
CancellationToken cancellationToken)
{
if (args.Template is Window window)
{
window.Activate();
}
return Task.CompletedTask;
}
}
+2 -2
View File
@@ -9,8 +9,8 @@ public class WidgetExtensionEnumerator(IFactory<Type, IWidget> factory,
IMediator mediator) :
INotificationHandler<Enumerate<WidgetExtension>>
{
public Task Handle(Enumerate<WidgetExtension> notification,
CancellationToken cancellationToken)
public Task Handle(Enumerate<WidgetExtension> args,
CancellationToken cancellationToken = default)
{
string extensionsDirectory = Path.Combine(hostEnvironment.ContentRootPath, "Extensions");
if (Directory.Exists(extensionsDirectory))
+2 -1
View File
@@ -8,7 +8,6 @@ using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System.Reflection;
using Hyperbar.Widget.Windows;
using Microsoft.UI.Xaml.Markup;
namespace Hyperbar.Windows;
@@ -46,6 +45,8 @@ public partial class App :
args.Placement = DesktopApplicationBarPlacemenet.Top;
});
services.AddNavigationHandler<WindowHandler>();
services.AddSingleton<DesktopApplicationBar>();
services.AddContentTemplate<ApplicationBarViewModel, ApplicationBarView>();
services.AddContentTemplate<PrimaryViewModel, PrimaryView>();
+1 -1
View File
@@ -16,8 +16,8 @@ public partial class SecondaryViewModel :
IDisposer disposer,
int index) : base(serviceFactory, mediator, disposer)
{
TemplateFactory = templateFactory;
this.index = index;
this.TemplateFactory = templateFactory;
Add<SettingsButtonViewModel>();
}
+1
View File
@@ -18,6 +18,7 @@
Width="{ThemeResource ButtonWidth}"
Height="{ThemeResource ButtonHeight}"
Padding="{ThemeResource ButtonPadding}"
Command="{x:Bind ViewModel.InvokeCommand}"
Content="&#xE713;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16" />
+5 -2
View File
@@ -2,9 +2,12 @@ using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.Windows;
public sealed partial class SettingsButtonView :
public partial class SettingsButtonView :
UserControl
{
public SettingsButtonView() =>
this.InitializeComponent();
InitializeComponent();
protected SettingsButtonViewModel ViewModel =>
(SettingsButtonViewModel)DataContext;
}
+20 -7
View File
@@ -1,11 +1,24 @@
namespace Hyperbar.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public class SettingsButtonViewModel(ITemplateFactory templateFactory,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer) :
ObservableViewModel(serviceFactory, mediator, disposer),
namespace Hyperbar.Windows;
public partial class SettingsButtonViewModel :
ObservableViewModel,
ITemplatedViewModel
{
public ITemplateFactory TemplateFactory => templateFactory;
[ObservableProperty]
private IRelayCommand? invokeCommand;
public SettingsButtonViewModel(ITemplateFactory templateFactory,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer) : base(serviceFactory, mediator, disposer)
{
TemplateFactory = templateFactory;
InvokeCommand = new AsyncRelayCommand(async () =>
await mediator.PublishAsync(new Navigate("Settings")));
}
public ITemplateFactory TemplateFactory { get; }
}
+3 -3
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
<Window
x:Class="Hyperbar.Windows.SettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid />
</UserControl>
<Button>afsdfdg</Button>
</Window>
+2 -1
View File
@@ -1,9 +1,10 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.Windows
{
public sealed partial class SettingsView :
UserControl
Window
{
public SettingsView() =>
InitializeComponent();
@@ -3,6 +3,8 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.FileProviders.Physical;
using Microsoft.Extensions.Hosting;
using System.Diagnostics.CodeAnalysis;
using System.Net.Mime;
using System.Text.Json;
namespace Hyperbar;
@@ -128,6 +130,26 @@ public static class IServiceCollectionExtensions
return services;
}
public static IServiceCollection AddNavigationHandler<THandler>(this IServiceCollection services)
where THandler :
INavigationHandler,
IHandler
{
Type? contract = typeof(THandler).GetInterfaces()
.FirstOrDefault(t => t.Name == typeof(INavigationHandler<>).Name);
if (contract?.GetGenericArguments() is { Length: 1 } arguments)
{
services.AddTransient<INavigationDescriptor>(provider => new NavigationDescriptor
{
Type = arguments[0]
});
}
services.AddHandler<THandler>();
return services;
}
public static IServiceCollection AddContentTemplate<TContent, TTemplate>(this IServiceCollection services,
object? key = null)
{
@@ -163,52 +185,59 @@ public static class IServiceCollectionExtensions
services.AddSingleton<IDisposer, Disposer>();
services.AddHandler<NavigateHandler>();
return services;
}
public static IServiceCollection AddHandler<THandler>(this IServiceCollection services,
public static IServiceCollection AddHandler<THandler>(
this IServiceCollection services,
ServiceLifetime lifetime = ServiceLifetime.Transient)
where THandler :
IHandler
where THandler : IHandler
{
if (typeof(THandler).GetInterfaces() is { } contracts)
{
foreach (Type contract in contracts)
{
if (contract.Name == typeof(INotificationHandler<>).Name)
if (contract.Name == typeof(INotificationHandler<>).Name &&
contract.GetGenericArguments() is { Length: 1 } notificationArguments)
{
if (contract.GetGenericArguments() is { Length: 1 } arguments)
{
Type notificationType = arguments[0];
services.Add(new ServiceDescriptor(typeof(INotificationHandler<>).MakeGenericType(notificationType), typeof(THandler), lifetime));
}
Type notificationType = notificationArguments[0];
services.Add(new ServiceDescriptor(
typeof(INotificationHandler<>).MakeGenericType(notificationType),
typeof(THandler),
lifetime));
}
if (contract.Name == typeof(IHandler<,>).Name)
if (contract.Name == typeof(IHandler<,>).Name &&
contract.GetGenericArguments() is { Length: 2 } handlerArguments)
{
if (contract.GetGenericArguments() is { Length: 2 } arguments)
{
Type requestType = arguments[0];
Type responseType = arguments[1];
Type requestType = handlerArguments[0];
Type responseType = handlerArguments[1];
Type wrapperType = typeof(HandlerWrapper<,>).MakeGenericType(requestType, responseType);
services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
services.Add(new ServiceDescriptor(wrapperType,
provider => provider.GetService<IServiceFactory>()?.Create(wrapperType,
services.Add(new ServiceDescriptor(
wrapperType,
provider =>
provider.GetService<IServiceFactory>()?.Create(
wrapperType,
provider.GetRequiredService<THandler>(),
provider.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!,
provider.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType))
)!,
lifetime
));
}
}
}
return services;
}
return services;
}
public static IServiceCollection AddNotificationRelay<TFromNotification,
TToNotification>(this IServiceCollection services)
where TFromNotification :
@@ -0,0 +1,7 @@
namespace Hyperbar;
public interface INavigationDescriptor
{
Type Type { get; set; }
}
@@ -0,0 +1,7 @@
namespace Hyperbar;
public interface INavigationHandler;
public interface INavigationHandler<TNavigation> :
INotificationHandler<Navigate<TNavigation>>,
INavigationHandler;
+8
View File
@@ -0,0 +1,8 @@
namespace Hyperbar;
public record Navigate(object Key) :
INotification;
public record Navigate<TTemplate>(TTemplate Template, object Content) :
INotification;
+53
View File
@@ -0,0 +1,53 @@
using Microsoft.Extensions.DependencyInjection;
namespace Hyperbar;
public class NavigateHandler :
INotificationHandler<Navigate>
{
private readonly IEnumerable<IContentTemplateDescriptor> contentTemplateDescriptors;
private readonly IServiceProvider provider;
private readonly IMediator mediator;
private readonly IEnumerable<INavigationDescriptor> navigationDescriptors;
public NavigateHandler(IServiceProvider provider,
IMediator mediator,
IEnumerable<INavigationDescriptor> navigationDescriptors,
IEnumerable<IContentTemplateDescriptor> contentTemplateDescriptors)
{
this.provider = provider;
this.mediator = mediator;
this.navigationDescriptors = navigationDescriptors;
this.contentTemplateDescriptors = contentTemplateDescriptors;
}
public async Task Handle(Navigate args,
CancellationToken cancellationToken)
{
if (contentTemplateDescriptors.FirstOrDefault(x => x.Key == args.Key)
is IContentTemplateDescriptor contentTemplateDescriptor)
{
if (navigationDescriptors.FirstOrDefault(x => contentTemplateDescriptor.TemplateType == x.Type ||
contentTemplateDescriptor.TemplateType.BaseType == x.Type) is { } navigationDescriptor)
{
if (contentTemplateDescriptor.TemplateType == navigationDescriptor.Type ||
contentTemplateDescriptor.TemplateType.BaseType == navigationDescriptor.Type)
{
if (provider.GetRequiredKeyedService(contentTemplateDescriptor.TemplateType,
contentTemplateDescriptor.Key) is { } template)
{
Type navigateType = typeof(Navigate<>)
.MakeGenericType(navigationDescriptor.Type);
if (Activator.CreateInstance(navigateType,
new object[] { template, args.Key }) is object navigate)
{
await mediator.PublishAsync(navigate, cancellationToken);
}
}
}
}
}
}
}
@@ -0,0 +1,8 @@
namespace Hyperbar;
public record NavigationDescriptor :
INavigationDescriptor
{
public required Type Type { get; set; }
}
+1 -32
View File
@@ -1,36 +1,5 @@
namespace Hyperbar;
public record Remove<TValue>(TValue Value) : INotification;
public record Navigate(object Key) :
public record Remove<TValue>(TValue Value) :
INotification;
public class NavigateHandler :
INotificationHandler<Navigate>
{
private readonly IEnumerable<IContentTemplateDescriptor> descriptors;
public NavigateHandler(IEnumerable<IContentTemplateDescriptor> descriptors,
IServiceProvider provider)
{
this.descriptors = descriptors;
}
public Task Handle(Navigate args,
CancellationToken cancellationToken)
{
if (descriptors.FirstOrDefault(x => x.Key == args.Key)
is IContentTemplateDescriptor descriptor)
{
//if (provider.GetRequiredKeyedService(descriptor.TemplateType,
// descriptor.Key) is { } template)
//{
// return template;
//}
}
throw new NotImplementedException();
}
}
+5 -4
View File
@@ -19,12 +19,13 @@ public interface IMediator
where TNotification :
INotification;
Task PublishAsync<TNotification>(TNotification notification,
Task PublishAsync(object notification,
CancellationToken cancellationToken = default);
Task PublishAsync(object notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
CancellationToken cancellationToken = default);
Task PublishAsync<TNotification>(CancellationToken cancellationToken = default)
where TNotification :
+1 -1
View File
@@ -6,5 +6,5 @@ public interface INotificationHandler<in TNotification> :
INotification
{
Task Handle(TNotification args,
CancellationToken cancellationToken);
CancellationToken cancellationToken = default);
}
+30 -15
View File
@@ -8,7 +8,7 @@ public class Mediator(IServiceProvider provider,
IDispatcher dispatcher) :
IMediator
{
private readonly ConcurrentDictionary<object, List<dynamic>> subscriptions = [];
private readonly ConcurrentDictionary<object, List<object>> handlers = [];
public Task PublishAsync<TNotification>(object key,
CancellationToken cancellationToken = default)
@@ -36,30 +36,38 @@ public class Mediator(IServiceProvider provider,
key, cancellationToken);
}
public Task PublishAsync<TNotification>(TNotification notification,
public Task PublishAsync(object notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
List<INotificationHandler<TNotification>> handlers =
provider.GetServices<INotificationHandler<TNotification>>().ToList();
Type notificationType = notification.GetType();
foreach (KeyValuePair<object, List<dynamic>> subscriber in subscriptions)
List<object?> handlers = provider.GetServices(typeof(INotificationHandler<>)
.MakeGenericType(notificationType)).ToList();
foreach (KeyValuePair<object, List<object>> subscriber in this.handlers)
{
if (subscriber.Key.Equals($"{(key is not null ? $"{key}:" : "")}{typeof(TNotification)}"))
if (subscriber.Key.Equals($"{key?.ToString()}:{notificationType}"))
{
foreach (dynamic handler in subscriber.Value)
{
handlers.Add(handler);
}
handlers.AddRange(subscriber.Value);
}
}
foreach (INotificationHandler<TNotification> handler in handlers)
foreach (object? handler in handlers)
{
marshal(() => handler.Handle(notification, cancellationToken));
if (handler is not null)
{
Type? handlerType = handler.GetType();
MethodInfo? handleMethod = handlerType.GetMethod("Handle",
[notificationType, typeof(CancellationToken)]);
if (handleMethod is not null)
{
marshal(() => (Task)handleMethod.Invoke(handler, new object[] { notification,
cancellationToken })!);
}
}
}
return Task.CompletedTask;
@@ -71,6 +79,13 @@ public class Mediator(IServiceProvider provider,
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
public Task PublishAsync(object notification,
CancellationToken cancellationToken = default)
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
}
public Task<TResponse?> SendAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
@@ -116,7 +131,7 @@ public class Mediator(IServiceProvider provider,
{
if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType)
{
subscriptions.AddOrUpdate($"{(key is not null ? $"{key}:" : "")}{argumentType}", new List<object> { handler }, (value, collection) =>
handlers.AddOrUpdate($"{(key is not null ? $"{key}:" : "")}{argumentType}", new List<object> { handler }, (value, collection) =>
{
collection.Add(handler);
return collection;