Toolkit.Avalonia
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using Avalonia.Threading;
|
||||
using IDispatcher = Toolkit.Foundation.IDispatcher;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class AvaloniaDispatcher :
|
||||
IDispatcher
|
||||
{
|
||||
public async Task InvokeAsync(Action action)
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(action);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class ClassicDesktopStyleApplicationHandler(INavigationContext navigationContext) :
|
||||
INavigateHandler<IClassicDesktopStyleApplicationLifetime>
|
||||
{
|
||||
public Task Handle(Navigate<IClassicDesktopStyleApplicationLifetime> args,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Application.Current?.ApplicationLifetime is
|
||||
IClassicDesktopStyleApplicationLifetime lifeTime)
|
||||
{
|
||||
if (args.Template is Window window)
|
||||
{
|
||||
lifeTime.MainWindow = window;
|
||||
window.DataContext = args.Content;
|
||||
|
||||
navigationContext.Set(window);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class ContentControlHandler(INavigationContext navigationContext) :
|
||||
INavigateHandler<ContentControl>
|
||||
{
|
||||
public async Task Handle(Navigate<ContentControl> args,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (args.Context is ContentControl contentControl)
|
||||
{
|
||||
if (args.Template is Control control)
|
||||
{
|
||||
TaskCompletionSource taskCompletionSource = new();
|
||||
async void HandleLoaded(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
control.Loaded -= HandleLoaded;
|
||||
if (control.DataContext is object content)
|
||||
{
|
||||
if (content is IInitializer initializer)
|
||||
{
|
||||
await initializer.Initialize();
|
||||
}
|
||||
|
||||
if (content is IActivated activated)
|
||||
{
|
||||
await activated.Activated();
|
||||
}
|
||||
}
|
||||
|
||||
taskCompletionSource.SetResult();
|
||||
}
|
||||
|
||||
async void HandleUnloaded(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
control.Unloaded -= HandleLoaded;
|
||||
if (control.DataContext is object content)
|
||||
{
|
||||
if (content is IDeactivated deactivated)
|
||||
{
|
||||
await deactivated.Deactivated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control.Loaded += HandleLoaded;
|
||||
control.Unloaded += HandleUnloaded;
|
||||
|
||||
contentControl.Content = control;
|
||||
contentControl.DataContext = args.Content;
|
||||
|
||||
navigationContext.Set(control);
|
||||
await taskCompletionSource.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
using Toolkit.Foundation;
|
||||
using Toolkit.UI.Controls.Avalonia;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class ContentDialogHandler(IDispatcher dispatcher) :
|
||||
INavigateHandler<ContentDialog>
|
||||
{
|
||||
public async Task Handle(Navigate<ContentDialog> args,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (args.Context is ContentDialog contentDialog)
|
||||
{
|
||||
contentDialog.DataContext = args.Content;
|
||||
|
||||
async void HandlePrimaryButtonClick(FluentAvalonia.UI.Controls.ContentDialog sender,
|
||||
FluentAvalonia.UI.Controls.ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
contentDialog.PrimaryButtonClick -= HandlePrimaryButtonClick;
|
||||
if (contentDialog.DataContext is object content)
|
||||
{
|
||||
if (content is IPrimaryConfirmation primaryConfirmation)
|
||||
{
|
||||
if (!await primaryConfirmation.Confirm())
|
||||
{
|
||||
args.Cancel = true;
|
||||
contentDialog.PrimaryButtonClick += HandlePrimaryButtonClick;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void HandleSecondaryButtonClick(FluentAvalonia.UI.Controls.ContentDialog sender,
|
||||
FluentAvalonia.UI.Controls.ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
contentDialog.SecondaryButtonClick -= HandleSecondaryButtonClick;
|
||||
if (contentDialog.DataContext is object content)
|
||||
{
|
||||
if (content is ISecondaryConfirmation secondaryConfirmation)
|
||||
{
|
||||
if (!await secondaryConfirmation.Confirm())
|
||||
{
|
||||
args.Cancel = true;
|
||||
contentDialog.SecondaryButtonClick += HandleSecondaryButtonClick;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void HandleClosing(FluentAvalonia.UI.Controls.ContentDialog sender,
|
||||
FluentAvalonia.UI.Controls.ContentDialogClosingEventArgs args)
|
||||
{
|
||||
if (args.Result == FluentAvalonia.UI.Controls.ContentDialogResult.Primary ||
|
||||
args.Result == FluentAvalonia.UI.Controls.ContentDialogResult.Secondary)
|
||||
{
|
||||
contentDialog.Closing -= HandleClosing;
|
||||
if (contentDialog.DataContext is object content)
|
||||
{
|
||||
if (content is IConfirmation confirmation)
|
||||
{
|
||||
if (!await confirmation.Confirm())
|
||||
{
|
||||
args.Cancel = true;
|
||||
contentDialog.Closing += HandleClosing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void HandleOpened(FluentAvalonia.UI.Controls.ContentDialog sender,
|
||||
EventArgs args)
|
||||
{
|
||||
contentDialog.Opened -= HandleOpened;
|
||||
if (contentDialog.DataContext is object content)
|
||||
{
|
||||
if (content is IDeactivatable deactivatable)
|
||||
{
|
||||
async void DeactivateHandler(object? sender, EventArgs args)
|
||||
{
|
||||
deactivatable.DeactivateHandler -= DeactivateHandler;
|
||||
await dispatcher.InvokeAsync(contentDialog.Hide);
|
||||
}
|
||||
|
||||
deactivatable.DeactivateHandler += DeactivateHandler;
|
||||
}
|
||||
|
||||
// A hack to wait for the dialog to finish loading up to make it appear more responsive
|
||||
await Task.Delay(250, cancellationToken);
|
||||
if (content is IInitializer initializer)
|
||||
{
|
||||
await initializer.Initialize();
|
||||
}
|
||||
|
||||
if (content is IActivated activated)
|
||||
{
|
||||
await activated.Activated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentDialog.Opened += HandleOpened;
|
||||
contentDialog.Closing += HandleClosing;
|
||||
contentDialog.PrimaryButtonClick += HandlePrimaryButtonClick;
|
||||
contentDialog.SecondaryButtonClick += HandleSecondaryButtonClick;
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
|
||||
contentDialog.PrimaryButtonClick += HandlePrimaryButtonClick;
|
||||
contentDialog.SecondaryButtonClick += HandleSecondaryButtonClick;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Interactivity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class ContentTemplate :
|
||||
IContentTemplate,
|
||||
IDataTemplate
|
||||
{
|
||||
public Control? Build(object? item)
|
||||
{
|
||||
if (item is IObservableViewModel observableViewModel)
|
||||
{
|
||||
if (observableViewModel.ServiceProvider is IServiceProvider provider)
|
||||
{
|
||||
IContentTemplateDescriptorProvider? contentTemplateProvider = provider.GetService<IContentTemplateDescriptorProvider>();
|
||||
INavigationContext? viewModelContentBinder = provider.GetService<INavigationContext>();
|
||||
|
||||
if (contentTemplateProvider?.Get(item.GetType().Name) is IContentTemplateDescriptor descriptor)
|
||||
{
|
||||
if (provider.GetRequiredKeyedService(descriptor.TemplateType, descriptor.Key) is Control control)
|
||||
{
|
||||
async void HandleLoaded(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
control.Loaded -= HandleLoaded;
|
||||
if (control.DataContext is object content)
|
||||
{
|
||||
if (content is IInitializer initializer)
|
||||
{
|
||||
await initializer.Initialize();
|
||||
}
|
||||
|
||||
if (content is IActivated activated)
|
||||
{
|
||||
await activated.Activated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void HandleUnloaded(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
control.Unloaded -= HandleLoaded;
|
||||
if (control.DataContext is object content)
|
||||
{
|
||||
if (content is IDeactivated deactivated)
|
||||
{
|
||||
await deactivated.Deactivated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control.Loaded += HandleLoaded;
|
||||
control.Unloaded += HandleUnloaded;
|
||||
|
||||
viewModelContentBinder?.Set(control);
|
||||
|
||||
return control;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public bool Match(object? data) => true;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using System.Reflection;
|
||||
using Toolkit.Foundation;
|
||||
using Toolkit.UI.Controls.Avalonia;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class FrameHandler(INavigationContext navigationContext) :
|
||||
INavigateHandler<Frame>,
|
||||
INavigateBackHandler<Frame>
|
||||
{
|
||||
public Task Handle(Navigate<Frame> args,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (args.Context is Frame frame)
|
||||
{
|
||||
frame.NavigationPageFactory ??= new NavigationPageFactory();
|
||||
if (args.Template is Control control)
|
||||
{
|
||||
void NavigatingFrom(object? sender,
|
||||
Control control)
|
||||
{
|
||||
async void HandleNavigatingFrom(object? _,
|
||||
NavigatingCancelEventArgs args)
|
||||
{
|
||||
Dictionary<string,object> results = [];
|
||||
|
||||
control.RemoveHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom);
|
||||
NavigatedFrom(sender, control, () => results);
|
||||
|
||||
if (control.DataContext is object content)
|
||||
{
|
||||
if (content is IPrimaryConfirmation confirmNavigation &&
|
||||
!await confirmNavigation.Confirm())
|
||||
{
|
||||
args.Cancel = true;
|
||||
}
|
||||
|
||||
if (!args.Cancel)
|
||||
{
|
||||
if (content is IDeactivating deactivating)
|
||||
{
|
||||
await deactivating.Deactivating();
|
||||
}
|
||||
|
||||
Type contentType = content.GetType();
|
||||
if (contentType.GetInterfaces() is Type[] contracts)
|
||||
{
|
||||
foreach (Type contract in contracts)
|
||||
{
|
||||
if (contract.Name == typeof(IDeactivating<>).Name &&
|
||||
contract.GetGenericArguments() is { Length: 1 } arguments)
|
||||
{
|
||||
if (contentType.GetMethods().FirstOrDefault(x =>
|
||||
x.Name == "Deactivating" && x.ReturnType == typeof(Task<>)
|
||||
.MakeGenericType(arguments[0]))
|
||||
is MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo.GetCustomAttribute<NavigationContextAttribute>()
|
||||
is NavigationContextAttribute attribute)
|
||||
{
|
||||
if (await methodInfo.InvokeAsync<object?>(content) is object result)
|
||||
{
|
||||
results.Add(attribute.Name, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control.AddHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom);
|
||||
}
|
||||
|
||||
void NavigatedFrom(object? sender,
|
||||
Control control,
|
||||
Func<Dictionary<string, object>> resultCallBack)
|
||||
{
|
||||
async void HandleNavigatedFrom(object? _,
|
||||
NavigationEventArgs args)
|
||||
{
|
||||
control.RemoveHandler(Frame.NavigatedFromEvent, HandleNavigatedFrom);
|
||||
if (args.NavigationMode == NavigationMode.New)
|
||||
{
|
||||
NavigatedTo(sender, control);
|
||||
}
|
||||
|
||||
Dictionary<string, object> results = resultCallBack.Invoke();
|
||||
async Task DoNavigatedFromAsync(object? content)
|
||||
{
|
||||
if (content is not null)
|
||||
{
|
||||
if (content is IDeactivated deactivated)
|
||||
{
|
||||
await deactivated.Deactivated();
|
||||
}
|
||||
|
||||
Type contentType = content.GetType();
|
||||
if (contentType.GetInterfaces() is Type[] contracts)
|
||||
{
|
||||
foreach (Type contract in contracts)
|
||||
{
|
||||
if (contract.Name == typeof(IActivated<>).Name &&
|
||||
contract.GetGenericArguments() is { Length: 1 } arguments)
|
||||
{
|
||||
if (contentType.GetMethods().FirstOrDefault(x =>
|
||||
x.Name == "NavigatedToAsync" &&
|
||||
x.GetCustomAttribute<NavigationContextAttribute>()
|
||||
is NavigationContextAttribute attribute && results.ContainsKey(attribute.Name))
|
||||
is MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo.GetCustomAttribute<NavigationContextAttribute>()
|
||||
is NavigationContextAttribute attribute)
|
||||
{
|
||||
if (results.TryGetValue(attribute.Name, out object? value))
|
||||
{
|
||||
await methodInfo.InvokeAsync(content, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Source is TemplatedControl sourceTemplate)
|
||||
{
|
||||
if (sourceTemplate.DataContext is object content)
|
||||
{
|
||||
await DoNavigatedFromAsync(content);
|
||||
}
|
||||
}
|
||||
|
||||
if (sender is TemplatedControl senderTemplate)
|
||||
{
|
||||
if (senderTemplate.DataContext is object content)
|
||||
{
|
||||
await DoNavigatedFromAsync(content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await DoNavigatedFromAsync(sender);
|
||||
}
|
||||
}
|
||||
|
||||
control.AddHandler(Frame.NavigatedFromEvent, HandleNavigatedFrom);
|
||||
}
|
||||
|
||||
void NavigatedTo(object? sender,
|
||||
Control control)
|
||||
{
|
||||
async void HandleNavigatedTo(object? _,
|
||||
NavigationEventArgs __)
|
||||
{
|
||||
control.RemoveHandler(Frame.NavigatedToEvent, HandleNavigatedTo);
|
||||
NavigatingFrom(sender, control);
|
||||
|
||||
if (control.DataContext is object content)
|
||||
{
|
||||
if (content is IInitializer initializer)
|
||||
{
|
||||
await initializer.Initialize();
|
||||
}
|
||||
|
||||
if (content is IActivated activated)
|
||||
{
|
||||
await activated.Activated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control.AddHandler(Frame.NavigatedToEvent, HandleNavigatedTo);
|
||||
}
|
||||
|
||||
control.DataContext = args.Content;
|
||||
navigationContext.Set(control);
|
||||
|
||||
NavigatedTo(args.Sender, control);
|
||||
frame.NavigateFromObject(control);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(NavigateBack<Frame> args,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (args.Context is Frame frame)
|
||||
{
|
||||
frame.GoBack();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public interface INavigationContext
|
||||
{
|
||||
void Set(Control control);
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public static class IServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddComponentConfigurationTemplate<TConfiguration, TValue, THeader,
|
||||
TDescription, TAction>(this IServiceCollection services,
|
||||
params object[]? parameters)
|
||||
where TConfiguration : class
|
||||
where THeader : class
|
||||
where TDescription : class
|
||||
where TAction : class
|
||||
{
|
||||
Type viewModelType = typeof(ComponentConfigurationViewModel<TConfiguration, TValue, THeader, TDescription, TAction>);
|
||||
Type viewType = typeof(Button);
|
||||
|
||||
object key = viewModelType.Name.Replace("ViewModel", "");
|
||||
|
||||
services.AddTransient<IComponentConfigurationViewModel,
|
||||
ComponentConfigurationViewModel<TConfiguration, TValue, THeader, TDescription, TAction>>(provider =>
|
||||
provider.GetRequiredService<IServiceFactory>()
|
||||
.Create<ComponentConfigurationViewModel<TConfiguration, TValue, THeader, TDescription, TAction>>(parameters)!);
|
||||
|
||||
services.TryAddTransient(viewType);
|
||||
|
||||
services.AddKeyedTransient<IComponentConfigurationViewModel,
|
||||
ComponentConfigurationViewModel<TConfiguration, TValue, THeader, TDescription, TAction>>(key, (provider, key) =>
|
||||
provider.GetRequiredService<IServiceFactory>()
|
||||
.Create<ComponentConfigurationViewModel<TConfiguration, TValue, THeader, TDescription, TAction>>(parameters)!);
|
||||
|
||||
services.TryAddKeyedTransient(viewType, key);
|
||||
|
||||
services.AddTransient<IContentTemplateDescriptor>(provider =>
|
||||
new ContentTemplateDescriptor(key, viewModelType, viewType, parameters));
|
||||
|
||||
services.TryAddTransient<THeader>();
|
||||
services.TryAddTransient<TDescription>();
|
||||
services.TryAddTransient<TAction>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddComponentConfigurationTemplate<TConfiguration, TValue, TAction>(this IServiceCollection services,
|
||||
Func<TConfiguration, TValue> valueDelegate,
|
||||
object header,
|
||||
object description,
|
||||
params object[]? parameters)
|
||||
where TConfiguration : class
|
||||
where TAction : class
|
||||
{
|
||||
Type viewModelType = typeof(ComponentConfigurationViewModel<TConfiguration, TValue, TAction>);
|
||||
Type viewType = typeof(Button);
|
||||
|
||||
object key = viewModelType.Name.Replace("ViewModel", "");
|
||||
|
||||
parameters = [valueDelegate, header, description, .. parameters ?? Enumerable.Empty<object?>()];
|
||||
|
||||
services.AddTransient<IComponentConfigurationViewModel,
|
||||
ComponentConfigurationViewModel<TConfiguration, TValue, TAction>>(provider =>
|
||||
provider.GetRequiredService<IServiceFactory>()
|
||||
.Create<ComponentConfigurationViewModel<TConfiguration, TValue, TAction>>(parameters)!);
|
||||
|
||||
services.TryAddTransient(viewType);
|
||||
|
||||
services.AddKeyedTransient<IComponentConfigurationViewModel,
|
||||
ComponentConfigurationViewModel<TConfiguration, TValue, TAction>>(key, (provider, key) =>
|
||||
provider.GetRequiredService<IServiceFactory>()
|
||||
.Create<ComponentConfigurationViewModel<TConfiguration, TValue, TAction>>(parameters)!);
|
||||
|
||||
services.TryAddKeyedTransient(viewType, key);
|
||||
|
||||
services.AddTransient<IContentTemplateDescriptor>(provider =>
|
||||
new ContentTemplateDescriptor(key, viewModelType, viewType, parameters));
|
||||
|
||||
services.TryAddTransient<TAction>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddComponentConfigurationTemplate<TConfiguration, TValue,
|
||||
TDescription, TAction>(this IServiceCollection services,
|
||||
Func<TConfiguration, TValue> valueDelegate,
|
||||
object description,
|
||||
params object[]? parameters)
|
||||
where TConfiguration : class
|
||||
where TDescription : class
|
||||
where TAction : class
|
||||
{
|
||||
Type viewModelType = typeof(ComponentConfigurationViewModel<TConfiguration, TValue, TDescription, TAction>);
|
||||
Type viewType = typeof(Button);
|
||||
|
||||
object key = viewModelType.Name.Replace("ViewModel", "");
|
||||
|
||||
parameters = [valueDelegate, description, .. parameters ?? Enumerable.Empty<object?>()];
|
||||
|
||||
services.AddTransient<IComponentConfigurationViewModel,
|
||||
ComponentConfigurationViewModel<TConfiguration, TValue, TDescription, TAction>>(provider =>
|
||||
provider.GetRequiredService<IServiceFactory>()
|
||||
.Create<ComponentConfigurationViewModel<TConfiguration, TValue, TDescription, TAction>>(parameters)!);
|
||||
|
||||
services.TryAddTransient(viewType);
|
||||
|
||||
services.AddKeyedTransient<IComponentConfigurationViewModel,
|
||||
ComponentConfigurationViewModel<TConfiguration, TValue, TDescription, TAction>>(key, (provider, key) =>
|
||||
provider.GetRequiredService<IServiceFactory>()
|
||||
.Create<ComponentConfigurationViewModel<TConfiguration, TValue, TDescription, TAction>>(parameters)!);
|
||||
|
||||
services.TryAddKeyedTransient(viewType, key);
|
||||
|
||||
services.AddTransient<IContentTemplateDescriptor>(provider =>
|
||||
new ContentTemplateDescriptor(key, viewModelType, viewType, parameters));
|
||||
|
||||
services.TryAddTransient<TDescription>();
|
||||
services.TryAddTransient<TAction>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAvalonia(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IDispatcher, AvaloniaDispatcher>();
|
||||
|
||||
services.AddTransient<IContentTemplate, ContentTemplate>();
|
||||
services.AddTransient<INavigationContext, NavigationContext>();
|
||||
|
||||
services.AddNavigateHandler<ClassicDesktopStyleApplicationHandler>();
|
||||
services.AddNavigateHandler<SingleViewApplicationHandler>();
|
||||
services.AddNavigateHandler<ContentControlHandler>();
|
||||
services.AddNavigateHandler<FrameHandler>();
|
||||
services.AddNavigateHandler<ContentDialogHandler>();
|
||||
|
||||
services.AddScoped<INavigationContextCollection, NavigationContextCollection>(provider => new NavigationContextCollection
|
||||
{
|
||||
{ typeof(IClassicDesktopStyleApplicationLifetime), typeof(IClassicDesktopStyleApplicationLifetime) },
|
||||
{ typeof(ISingleViewApplicationLifetime), typeof(ISingleViewApplicationLifetime) }
|
||||
});
|
||||
|
||||
services.AddTransient((Func<IServiceProvider, IProxyServiceCollection<IComponentBuilder>>)(provider =>
|
||||
new ProxyServiceCollection<IComponentBuilder>(services =>
|
||||
{
|
||||
services.AddSingleton(provider.GetRequiredService<IDispatcher>());
|
||||
|
||||
services.AddTransient<IContentTemplateDescriptorProvider, ContentTemplateDescriptorProvider>();
|
||||
services.AddTransient<IContentTemplate, ContentTemplate>();
|
||||
|
||||
services.AddTransient<INavigationContext, NavigationContext>();
|
||||
|
||||
services.AddNavigateHandler<ContentControlHandler>();
|
||||
services.AddNavigateHandler<FrameHandler>();
|
||||
services.AddNavigateHandler<ContentDialogHandler>();
|
||||
})));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using HyperX.UI.Windows;
|
||||
using System.Reflection;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class NavigationContext(INavigationContextCollection contexts) :
|
||||
INavigationContext
|
||||
{
|
||||
public void Set(Control control)
|
||||
{
|
||||
if (control.GetType().GetCustomAttributes<NavigationTargetAttribute>()
|
||||
is IEnumerable<NavigationTargetAttribute> attributes)
|
||||
{
|
||||
foreach (NavigationTargetAttribute attribute in attributes)
|
||||
{
|
||||
if (!contexts.ContainsKey(attribute.Name))
|
||||
{
|
||||
if (control.Find<TemplatedControl>(attribute.Name) is TemplatedControl content)
|
||||
{
|
||||
contexts.Add(attribute.Name, content);
|
||||
void HandleUnloaded(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
control.Unloaded -= HandleUnloaded;
|
||||
contexts.Remove(attribute.Name);
|
||||
}
|
||||
|
||||
control.Unloaded += HandleUnloaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class NavigationPageFactory :
|
||||
INavigationPageFactory
|
||||
{
|
||||
public Control? GetPage(Type srcType)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Control GetPageFromObject(object target)
|
||||
{
|
||||
return (Control)target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Toolkit.Avalonia;
|
||||
|
||||
public class SingleViewApplicationHandler(INavigationContext navigationContext) :
|
||||
INavigateHandler<ISingleViewApplicationLifetime>
|
||||
{
|
||||
public Task Handle(Navigate<ISingleViewApplicationLifetime> args,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Application.Current?.ApplicationLifetime is
|
||||
ISingleViewApplicationLifetime lifeTime)
|
||||
{
|
||||
if (args.Template is Control control)
|
||||
{
|
||||
lifeTime.MainView = control;
|
||||
control.DataContext = args.Content;
|
||||
|
||||
navigationContext.Set(control);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.1.0-beta1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
|
||||
<ProjectReference Include="..\Toolkit.UI.Avalonia\Toolkit.UI.Avalonia.csproj" />
|
||||
<ProjectReference Include="..\Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+9
-3
@@ -3,11 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33110.190
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.Foundation", "Toolkit.Foundation\Toolkit.Foundation.csproj", "{66968F8D-689E-49D8-9370-DFF099C56202}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit.Foundation", "Toolkit.Foundation\Toolkit.Foundation.csproj", "{66968F8D-689E-49D8-9370-DFF099C56202}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.UI.Avalonia", "Toolkit.UI.Avalonia\Toolkit.UI.Avalonia.csproj", "{E091FA94-2F15-403A-98D1-4557C2FF9A02}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit.UI.Avalonia", "Toolkit.UI.Avalonia\Toolkit.UI.Avalonia.csproj", "{E091FA94-2F15-403A-98D1-4557C2FF9A02}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.UI.Controls.Avalonia", "Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj", "{8841990D-A246-495D-9A40-C39BA4347505}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolkit.UI.Controls.Avalonia", "Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj", "{8841990D-A246-495D-9A40-C39BA4347505}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toolkit.Avalonia", "Toolkit.Avalonia\Toolkit.Avalonia.csproj", "{9585A317-4405-4E39-BDBE-23EF822FBF34}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -27,6 +29,10 @@ Global
|
||||
{8841990D-A246-495D-9A40-C39BA4347505}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8841990D-A246-495D-9A40-C39BA4347505}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8841990D-A246-495D-9A40-C39BA4347505}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9585A317-4405-4E39-BDBE-23EF822FBF34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9585A317-4405-4E39-BDBE-23EF822FBF34}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9585A317-4405-4E39-BDBE-23EF822FBF34}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9585A317-4405-4E39-BDBE-23EF822FBF34}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Reference in New Issue
Block a user