diff --git a/Controls/Avalonia/IconElement/ContentIcon.cs b/Controls/Avalonia/IconElement/ContentIcon.cs index 3c985a6..bb608d3 100644 --- a/Controls/Avalonia/IconElement/ContentIcon.cs +++ b/Controls/Avalonia/IconElement/ContentIcon.cs @@ -2,6 +2,7 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.LogicalTree; +using Avalonia.Metadata; namespace Toolkit.Controls.Avalonia; @@ -10,13 +11,14 @@ public class ContentIcon : FluentAvalonia.UI.Controls.FAIconElement public static readonly StyledProperty IconTemplateProperty = AvaloniaProperty.Register("IconTemplate"); - public static readonly StyledProperty IconProperty = - AvaloniaProperty.Register("Icon"); + public static readonly StyledProperty ContentProperty = + AvaloniaProperty.Register("Content"); - public object Icon + [Content] + public object Content { - get => GetValue(IconProperty); - set => SetValue(IconProperty, value); + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); } private ContentControl? content; @@ -64,7 +66,7 @@ public class ContentIcon : FluentAvalonia.UI.Controls.FAIconElement { content = new ContentControl(); - content.Bind(ContentControl.ContentProperty, this.GetBindingObservable(IconProperty)); + content.Bind(ContentControl.ContentProperty, this.GetBindingObservable(ContentProperty)); content.Bind(ContentControl.ContentTemplateProperty, this.GetBindingObservable(IconTemplateProperty)); LogicalChildren.Add(content); diff --git a/Controls/Avalonia/NavigationView/NavigationViewItem.cs b/Controls/Avalonia/NavigationView/NavigationViewItem.cs index ac146ec..b70d865 100644 --- a/Controls/Avalonia/NavigationView/NavigationViewItem.cs +++ b/Controls/Avalonia/NavigationView/NavigationViewItem.cs @@ -1,8 +1,34 @@ -using Avalonia.Styling; +using Avalonia.Controls.Primitives; +using Avalonia.Styling; +using Avalonia.VisualTree; +using FluentAvalonia.UI.Controls; namespace Toolkit.Controls.Avalonia; public class NavigationViewItem : FluentAvalonia.UI.Controls.NavigationViewItem, IStyleable { + private NavigationView? navigationView; + + public event EventHandler Invoked; + Type IStyleable.StyleKey => typeof(FluentAvalonia.UI.Controls.NavigationViewItem); + + protected override void OnApplyTemplate(TemplateAppliedEventArgs args) + { + navigationView = this.FindAncestorOfType(); + if (navigationView is not null) + { + navigationView.ItemInvoked += OnItemInvoked; + } + + base.OnApplyTemplate(args); + } + + private void OnItemInvoked(object? sender, NavigationViewItemInvokedEventArgs args) + { + if (args.InvokedItemContainer == this) + { + Invoked?.Invoke(this, args); + } + } } diff --git a/Framework/Avalonia/Extensions/IServiceCollectionExtensions.cs b/Framework/Avalonia/Extensions/IServiceCollectionExtensions.cs index 12b2b7a..ad8dcfc 100644 --- a/Framework/Avalonia/Extensions/IServiceCollectionExtensions.cs +++ b/Framework/Avalonia/Extensions/IServiceCollectionExtensions.cs @@ -1,6 +1,4 @@ -using Avalonia.Controls; -using FluentAvalonia.UI.Controls; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Toolkit.Framework.Foundation; @@ -12,9 +10,11 @@ public static class IServiceCollectionExtensions { serviceCollection.TryAddSingleton(); - serviceCollection.TryAddTransient, FrameNavigation>(); - serviceCollection.TryAddTransient, ContentDialogNavigation>(); - serviceCollection.TryAddTransient, ContentControlNavigation>(); + serviceCollection.AddHandler(); + serviceCollection.AddHandler(); + serviceCollection.AddHandler(); + serviceCollection.AddHandler(); + serviceCollection.AddHandler(); return serviceCollection; } diff --git a/Framework/Avalonia/Navigation/NavigateHandler.cs b/Framework/Avalonia/Navigation/NavigateHandler.cs index 35bd255..2631e83 100644 --- a/Framework/Avalonia/Navigation/NavigateHandler.cs +++ b/Framework/Avalonia/Navigation/NavigateHandler.cs @@ -1,6 +1,7 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Mediator; +using System.Diagnostics; using Toolkit.Framework.Foundation; namespace Toolkit.Framework.Avalonia; diff --git a/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs b/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs index b8a5302..017478e 100644 --- a/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs +++ b/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs @@ -1,19 +1,42 @@ -using Microsoft.Extensions.DependencyInjection; +using Mediator; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; namespace Toolkit.Framework.Foundation; public static class IServiceCollectionExtensions { - public static IServiceCollection AddHandler(this IServiceCollection serviceCollection) + public static IServiceCollection AddHandler(this IServiceCollection services) where TRequestHandler : notnull { - serviceCollection.TryAdd(new ServiceDescriptor(typeof(TRequestHandler), typeof(TRequestHandler), ServiceLifetime.Transient)); - return serviceCollection; + if (typeof(TRequestHandler).GetInterface(typeof(IRequestHandler<,>).Name) is { } contract) + { + if (contract.GetGenericArguments() is { Length: 2 } arguments) + { + Type requestType = arguments[0]; + Type responseType = arguments[1]; + Type wrapperType = typeof(RequestClassHandlerWrapper<,>).MakeGenericType(requestType, responseType); + + services.TryAdd(new ServiceDescriptor(typeof(TRequestHandler), typeof(TRequestHandler), ServiceLifetime.Transient)); + services.Add(new ServiceDescriptor(wrapperType, + sp => + { + return sp.GetService()?.Create(wrapperType, sp.GetRequiredService(), sp.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!; + }, + ServiceLifetime.Transient + )); + + } + } + + return services; } public static IServiceCollection AddFoundation(this IServiceCollection serviceCollection) { - serviceCollection.AddSingleton(provider => new ServiceFactory(provider.GetService, (instanceType, parameters) => ActivatorUtilities.CreateInstance(provider, instanceType, parameters!))) + serviceCollection + .AddSingleton() + .AddHandler() + .AddSingleton(provider => new ServiceFactory(provider.GetService, (instanceType, parameters) => ActivatorUtilities.CreateInstance(provider, instanceType, parameters!))) .AddSingleton(provider => new Initialization(() => { return serviceCollection.Where(x => x.ServiceType.GetInterfaces() diff --git a/Framework/Foundation/Lifecycles/Mediator.cs b/Framework/Foundation/Lifecycles/Mediator.cs new file mode 100644 index 0000000..94693a8 --- /dev/null +++ b/Framework/Foundation/Lifecycles/Mediator.cs @@ -0,0 +1,71 @@ +using Mediator; +using System.Diagnostics; + +namespace Toolkit.Framework.Foundation; + +public class Mediator : IMediator +{ + private readonly IServiceProvider factory; + + public Mediator(IServiceProvider factory) + { + this.factory = factory; + } + + public IAsyncEnumerable CreateStream(IStreamQuery query, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable CreateStream(IStreamRequest request, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable CreateStream(IStreamCommand command, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable CreateStream(object request, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public ValueTask Publish(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification + { + throw new NotImplementedException(); + } + + public ValueTask Publish(object notification, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public ValueTask Send(IRequest request, CancellationToken cancellationToken = default) + { + dynamic? handler = factory.GetService(typeof(RequestClassHandlerWrapper<,>).MakeGenericType(request.GetType(), typeof(TResponse))); + + if (handler is not null) + { + return handler.Handle((dynamic)request, cancellationToken); + } + + return default; + } + + public ValueTask Send(ICommand command, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public ValueTask Send(IQuery query, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public ValueTask Send(object message, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } +} diff --git a/Framework/Foundation/Lifecycles/RequestClassHandlerWrapper.cs b/Framework/Foundation/Lifecycles/RequestClassHandlerWrapper.cs new file mode 100644 index 0000000..50d0fad --- /dev/null +++ b/Framework/Foundation/Lifecycles/RequestClassHandlerWrapper.cs @@ -0,0 +1,25 @@ +using Mediator; + +namespace Toolkit.Framework.Foundation; + +public class RequestClassHandlerWrapper where TRequest : class, IRequest +{ + private readonly MessageHandlerDelegate handler; + + public RequestClassHandlerWrapper(IRequestHandler concreteHandler, + IEnumerable> pipelineBehaviours) + { + MessageHandlerDelegate handler = concreteHandler.Handle; + foreach (var pipeline in pipelineBehaviours.Reverse()) + { + MessageHandlerDelegate handlerCopy = handler; + IPipelineBehavior pipelineCopy = pipeline; + + handler = (TRequest message, CancellationToken cancellationToken) => pipelineCopy.Handle(message, cancellationToken, handlerCopy); + } + + this.handler = handler; + } + + public ValueTask Handle(TRequest request, CancellationToken cancellationToken) => handler(request, cancellationToken); +} \ No newline at end of file