This commit is contained in:
TheXamlGuy
2024-06-12 23:04:22 +01:00
parent c8f447251e
commit b155f5c6e2
40 changed files with 262 additions and 249 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ public class ContentControlHandler :
control.Loaded -= HandleLoaded; control.Loaded -= HandleLoaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IInitializer initializer) if (content is IInitialization initializer)
{ {
await initializer.Initialize(); await initializer.Initialize();
} }
+1 -1
View File
@@ -129,7 +129,7 @@ public class ContentDialogHandler(IDispatcher dispatcher) :
// A hack to wait for the dialog to finish loading up to make it appear more responsive // A hack to wait for the dialog to finish loading up to make it appear more responsive
await Task.Delay(250); await Task.Delay(250);
if (content is IInitializer initializer) if (content is IInitialization initializer)
{ {
await initializer.Initialize(); await initializer.Initialize();
} }
+4 -4
View File
@@ -16,10 +16,10 @@ public class ContentTemplate :
{ {
if (observableViewModel.Provider is IServiceProvider provider) if (observableViewModel.Provider is IServiceProvider provider)
{ {
IContentTemplateDescriptorProvider? contentTemplateProvider = provider.GetService<IContentTemplateDescriptorProvider>(); Type itemType = item.GetType();
INavigationRegion? viewModelContentBinder = provider.GetService<INavigationRegion>();
if (contentTemplateProvider?.Get(item.GetType().Name) is IContentTemplateDescriptor descriptor) if (provider.GetRequiredKeyedService<IContentTemplateDescriptor>(itemType.Name.Replace("ViewModel", ""))
is IContentTemplateDescriptor descriptor)
{ {
if (provider.GetRequiredKeyedService(descriptor.TemplateType, descriptor.Key) is Control control) if (provider.GetRequiredKeyedService(descriptor.TemplateType, descriptor.Key) is Control control)
{ {
@@ -28,7 +28,7 @@ public class ContentTemplate :
control.Loaded -= HandleLoaded; control.Loaded -= HandleLoaded;
if (control.DataContext is object content) if (control.DataContext is object content)
{ {
if (content is IInitializer initializer) if (content is IInitialization initializer)
{ {
await initializer.Initialize(); await initializer.Initialize();
} }
+1 -1
View File
@@ -72,7 +72,7 @@ public class FrameHandler :
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 IInitializer initializer) if (content is IInitialization initializer)
{ {
await initializer.Initialize(); await initializer.Initialize();
} }
@@ -3,6 +3,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Toolkit.Foundation; using Toolkit.Foundation;
using Toolkit.UI.Controls.Avalonia;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
@@ -128,11 +129,11 @@ public static class IServiceCollectionExtensions
services.AddTransient<IContentTemplate, ContentTemplate>(); services.AddTransient<IContentTemplate, ContentTemplate>();
services.AddTransient<INavigationRegion, NavigationRegion>(); services.AddTransient<INavigationRegion, NavigationRegion>();
services.AddNavigateHandler<ClassicDesktopStyleApplicationHandler>(); services.AddHandler<ClassicDesktopStyleApplicationHandler>(nameof(IClassicDesktopStyleApplicationLifetime));
services.AddNavigateHandler<SingleViewApplicationHandler>(); services.AddHandler<SingleViewApplicationHandler>(nameof(ISingleViewApplicationLifetime));
services.AddNavigateHandler<ContentControlHandler>(); services.AddHandler<ContentControlHandler>(nameof(ContentControl));
services.AddNavigateHandler<FrameHandler>(); services.AddHandler<FrameHandler>(nameof(Frame));
services.AddNavigateHandler<ContentDialogHandler>(); services.AddHandler<ContentDialogHandler>(nameof(ContentDialog));
services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(provider => new NavigationRegionCollection services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(provider => new NavigationRegionCollection
{ {
@@ -144,15 +145,13 @@ public static class IServiceCollectionExtensions
new ProxyServiceCollection<IComponentBuilder>(services => new ProxyServiceCollection<IComponentBuilder>(services =>
{ {
services.AddSingleton(provider.GetRequiredService<IDispatcher>()); services.AddSingleton(provider.GetRequiredService<IDispatcher>());
services.AddTransient<IContentTemplateDescriptorProvider, ContentTemplateDescriptorProvider>();
services.AddTransient<IContentTemplate, ContentTemplate>(); services.AddTransient<IContentTemplate, ContentTemplate>();
services.AddTransient<INavigationRegion, NavigationRegion>(); services.AddTransient<INavigationRegion, NavigationRegion>();
services.AddNavigateHandler<ContentControlHandler>(); services.AddHandler<ContentControlHandler>(nameof(ContentControl));
services.AddNavigateHandler<FrameHandler>(); services.AddHandler<FrameHandler>(nameof(Frame));
services.AddNavigateHandler<ContentDialogHandler>(); services.AddHandler<ContentDialogHandler>(nameof(ContentDialog));
}))); })));
return services; return services;
+2 -2
View File
@@ -2,13 +2,13 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class AppService(IEnumerable<IInitializer> initializers, public class AppService(IEnumerable<IInitialization> initializers,
IPublisher publisher) : IPublisher publisher) :
IHostedService IHostedService
{ {
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
foreach (IInitializer initializer in initializers) foreach (IInitialization initializer in initializers)
{ {
await initializer.Initialize(); await initializer.Initialize();
} }
+1 -1
View File
@@ -1,3 +1,3 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public record ChangedEventArgs<TSender>(TSender? Sender = default); public record ChangedEventArgs<TSender>(TSender? Sender = default);
+1 -1
View File
@@ -6,7 +6,7 @@ public partial class CommandValueViewModel<TValue>(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer) : IDisposer disposer) :
ValueViewModel<TValue>(provider, factory, mediator, publisher, subscriber, disposer) ValueViewModel<TValue>(provider, factory, mediator, publisher, subscriber, disposer)
{ {
+1 -1
View File
@@ -6,7 +6,7 @@ public partial class CommandViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer) : IDisposer disposer) :
Observable(provider, factory, mediator, publisher, subscriber, disposer) Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {
+3 -4
View File
@@ -32,14 +32,13 @@ public class ComponentBuilder :
services.AddScoped<SubscriptionCollection>(); services.AddScoped<SubscriptionCollection>();
services.AddTransient<IHandlerProvider, HandlerProvider>(); services.AddTransient<IHandlerProvider, HandlerProvider>();
services.AddScoped<ISubscription, Subscription>(); services.AddScoped<ISubscriber, Subscriber>();
services.AddTransient<IPublisher, Publisher>(); services.AddTransient<IPublisher, Publisher>();
services.AddTransient<IMediator, Mediator>(); services.AddTransient<IMediator, Mediator>();
services.AddTransient<IContentFactory, ContentFactory>();
services.AddTransient<INavigationScope, NavigationScope>(); services.AddTransient<INavigationScope, NavigationScope>();
services.AddTransient<INavigationProvider, NavigationProvider>();
services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(); services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>();
services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>(); services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>();
@@ -12,7 +12,7 @@ public partial class ComponentConfigurationViewModel<TConfiguration, TValue, THe
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
THeader header, THeader header,
TDescription description, TDescription description,
@@ -30,7 +30,7 @@ public partial class ComponentConfigurationViewModel<TConfiguration, TValue, TAc
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TAction action, TAction action,
TConfiguration configuration, TConfiguration configuration,
@@ -72,7 +72,7 @@ public partial class ComponentConfigurationViewModel<TConfiguration, TValue, TDe
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TAction action, TAction action,
TDescription description, TDescription description,
+2 -2
View File
@@ -4,7 +4,7 @@ using Microsoft.Extensions.Hosting;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class ComponentHost(IServiceProvider services, public class ComponentHost(IServiceProvider services,
IEnumerable<IInitializer> initializers, IEnumerable<IInitialization> initializers,
IEnumerable<IHostedService> hostedServices) : IEnumerable<IHostedService> hostedServices) :
IComponentHost IComponentHost
{ {
@@ -21,7 +21,7 @@ public class ComponentHost(IServiceProvider services,
public async Task StartAsync(CancellationToken cancellationToken = default) public async Task StartAsync(CancellationToken cancellationToken = default)
{ {
foreach (IInitializer initializer in initializers) foreach (IInitialization initializer in initializers)
{ {
await initializer.Initialize(); await initializer.Initialize();
} }
+1 -1
View File
@@ -7,7 +7,7 @@ public class ComponentInitializer(IEnumerable<IComponent> components,
IComponentHostCollection hosts, IComponentHostCollection hosts,
IComponentScopeCollection scopes, IComponentScopeCollection scopes,
IServiceProvider provider) : IServiceProvider provider) :
IInitializer IInitialization
{ {
public async Task Initialize() public async Task Initialize()
{ {
@@ -5,7 +5,7 @@ public class ConfigurationInitializer<TConfiguration>(IConfigurationReader<TConf
IConfigurationFactory<TConfiguration> factory, IConfigurationFactory<TConfiguration> factory,
IPublisher publisher) : IPublisher publisher) :
IConfigurationInitializer<TConfiguration>, IConfigurationInitializer<TConfiguration>,
IInitializer IInitialization
where TConfiguration : where TConfiguration :
class class
{ {
+46
View File
@@ -0,0 +1,46 @@
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation;
public class ContentFactory(IMediator mediator,
IServiceProvider provider,
IServiceFactory factory) : IContentFactory
{
public async Task<object?> CreateAsync(IContentTemplateDescriptor descriptor,
object[] parameters)
{
Type createEventType = typeof(CreateEventArgs<>).MakeGenericType(descriptor.ContentType);
object? content = null;
if (Activator.CreateInstance(createEventType, [null, parameters]) is object createEvent)
{
content = await mediator.Handle(descriptor.ContentType, createEvent, descriptor.Key);
}
if (content is null)
{
if (parameters is { Length: > 0 })
{
content = factory.Create(descriptor.ContentType, args =>
{
if (args is IPostInitialization initialization)
{
initialization.PostInitialize();
}
}, parameters);
}
else
{
content = provider.GetRequiredKeyedService(descriptor.ContentType, args =>
{
if (args is IPostInitialization initialization)
{
initialization.PostInitialize();
}
}, descriptor.Key);
}
}
return content;
}
}
@@ -1,16 +0,0 @@
namespace Toolkit.Foundation;
public class ContentTemplateDescriptorProvider(IEnumerable<IContentTemplateDescriptor> contentTemplates) :
IContentTemplateDescriptorProvider
{
public IContentTemplateDescriptor? Get(object key)
{
if (contentTemplates.FirstOrDefault(x => x.Key.Equals(key) || x.Key.Equals($"{key}".Replace("ViewModel", "")))
is IContentTemplateDescriptor viewModelTemplate)
{
return viewModelTemplate;
}
return default;
}
}
+3 -5
View File
@@ -29,9 +29,9 @@ public class DefaultHostBuilder :
services.AddScoped<SubscriptionCollection>(); services.AddScoped<SubscriptionCollection>();
services.AddTransient<IHandlerProvider, HandlerProvider>(); services.AddTransient<IHandlerProvider, HandlerProvider>();
services.AddTransient<ISubscription, Subscription>(); services.AddTransient<ISubscriber, Subscriber>();
services.AddTransient<ISubscription, Subscription>(); services.AddTransient<ISubscriber, Subscriber>();
services.AddTransient<IPublisher, Publisher>(); services.AddTransient<IPublisher, Publisher>();
services.AddTransient<IMediator, Mediator>(); services.AddTransient<IMediator, Mediator>();
@@ -45,9 +45,7 @@ public class DefaultHostBuilder :
services.AddScoped<IProxyService<IComponentHostCollection>>(provider => services.AddScoped<IProxyService<IComponentHostCollection>>(provider =>
new ProxyService<IComponentHostCollection>(provider.GetRequiredService<IComponentHostCollection>())); new ProxyService<IComponentHostCollection>(provider.GetRequiredService<IComponentHostCollection>()));
services.AddTransient<IContentTemplateDescriptorProvider, ContentTemplateDescriptorProvider>(); services.AddTransient<IContentFactory, ContentFactory>();
services.AddTransient<INavigationProvider, NavigationProvider>();
services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(); services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>();
services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>(); services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>();
+1 -1
View File
@@ -1,4 +1,4 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public interface IConfigurationChanged<TConfiguration> : public interface IConfigurationChanged<TConfiguration> :
IInitializer; IInitialization;
+8
View File
@@ -0,0 +1,8 @@
namespace Toolkit.Foundation
{
public interface IContentFactory
{
Task<object?> CreateAsync(IContentTemplateDescriptor descriptor, object[] resolvedArguments);
}
}
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IContentTemplateDescriptorProvider
{
IContentTemplateDescriptor? Get(object key);
}
+1 -1
View File
@@ -156,7 +156,7 @@ public static class IHostBuilderExtension
services.TryAddKeyedTransient<IConfigurationFactory<TConfiguration>>(section, (provider, key) => services.TryAddKeyedTransient<IConfigurationFactory<TConfiguration>>(section, (provider, key) =>
new ConfigurationFactory<TConfiguration>(() => defaultConfiguration ?? provider.GetRequiredKeyedService<TConfiguration>(key))); new ConfigurationFactory<TConfiguration>(() => defaultConfiguration ?? provider.GetRequiredKeyedService<TConfiguration>(key)));
services.AddTransient<IInitializer, ConfigurationInitializer<TConfiguration>>(provider => services.AddTransient<IInitialization, ConfigurationInitializer<TConfiguration>>(provider =>
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),
@@ -1,11 +1,11 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public interface IInitializer public interface IInitialization
{ {
Task Initialize(); Task Initialize();
} }
public interface IInitializer<T> public interface IInitialization<T>
{ {
Task<T> Initialize(); Task<T> Initialize();
} }
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface INavigationProvider
{
INavigation? Get(Type type);
}
@@ -0,0 +1,6 @@
namespace Toolkit.Foundation;
public interface IPostInitialization
{
void PostInitialize();
}
@@ -1,8 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public static class IServiceCollectionExtensions public static class IServiceCollectionExtensions
{ {
public static IServiceCollection AddCache<TKey, TValue>(this IServiceCollection services) public static IServiceCollection AddCache<TKey, TValue>(this IServiceCollection services)
@@ -132,31 +130,9 @@ public static class IServiceCollectionExtensions
public static IServiceCollection AddInitializer<TInitializer>(this IServiceCollection services) public static IServiceCollection AddInitializer<TInitializer>(this IServiceCollection services)
where TInitializer : class, where TInitializer : class,
IInitializer IInitialization
{ {
services.AddTransient<IInitializer, TInitializer>(); services.AddTransient<IInitialization, TInitializer>();
return services;
}
public static IServiceCollection AddNavigateHandler<THandler>(this IServiceCollection services)
where THandler : INavigateHandler,
IHandler
{
IEnumerable<Type> contracts = typeof(THandler).GetInterfaces()
.Where(x => x.Name == typeof(INavigateHandler<>).Name || x.Name == typeof(INavigateBackHandler<>).Name);
foreach (Type contract in contracts)
{
if (contract.GetGenericArguments() is { Length: 1 } arguments)
{
services.AddTransient<INavigation>(provider => new Navigation
{
Type = arguments[0]
});
}
}
services.AddHandler<THandler>();
return services; return services;
} }
@@ -205,7 +181,7 @@ public static class IServiceCollectionExtensions
services.Add(new ServiceDescriptor(viewType, key, viewType, serviceLifetime)); services.Add(new ServiceDescriptor(viewType, key, viewType, serviceLifetime));
services.AddTransient<IContentTemplateDescriptor>(provider => services.AddKeyedTransient<IContentTemplateDescriptor>(key, (provider, _) =>
new ContentTemplateDescriptor(key, viewModelType, viewType, parameters)); new ContentTemplateDescriptor(key, viewModelType, viewType, parameters));
return services; return services;
+3
View File
@@ -7,5 +7,8 @@ public interface IServiceFactory
TService Create<TService>(Action<TService> serviceDelegate, TService Create<TService>(Action<TService> serviceDelegate,
params object?[]? parameters); params object?[]? parameters);
object Create(Type type, Action<object> serviceDelegate,
params object?[]? parameters);
TService Create<TService>(params object?[]? parameters); TService Create<TService>(params object?[]? parameters);
} }
@@ -0,0 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation;
public static class IServiceProviderExtensions
{
public static object GetRequiredKeyedService(this IServiceProvider provider,
Type serviceType,
Action<object> serviceDelegate,
object? serviceKey)
{
object service = provider.GetRequiredKeyedService(serviceType, serviceKey);
serviceDelegate.Invoke(service);
return service;
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Toolkit.Foundation;
public interface ISubscriber
{
void Subscribe(object subscriber);
void Unsubscribe(object subscriber);
}
@@ -0,0 +1,6 @@
namespace Toolkit.Foundation;
public interface ISubscriberRequired
{
ISubscriber Subscription { get; }
}
-8
View File
@@ -1,8 +0,0 @@
namespace Toolkit.Foundation;
public interface ISubscription
{
void Add(object subscriber);
void Remove(object subscriber);
}
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface ISubscriptionRequired
{
ISubscription Subscription { get; }
}
-16
View File
@@ -1,16 +0,0 @@
namespace Toolkit.Foundation;
public class NavigationProvider(IEnumerable<INavigation> navigations) :
INavigationProvider
{
public INavigation? Get(Type type)
{
if (navigations.FirstOrDefault(x => type == x.Type ||
type.BaseType == x.Type) is INavigation navigation)
{
return navigation;
}
return default;
}
}
@@ -1,15 +1,15 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class NavigationRegionProvider(INavigationRegionCollection contexts) : public class NavigationRegionProvider(INavigationRegionCollection collection) :
INavigationRegionProvider INavigationRegionProvider
{ {
public object? Get(object key) => public object? Get(object key) =>
contexts.TryGetValue(key, out object? target) ? target : default; collection.TryGetValue(key, out object? target) ? target : default;
public bool TryGet(object name, public bool TryGet(object name,
out object? value) out object? value)
{ {
if (contexts.TryGetValue(name, if (collection.TryGetValue(name,
out object? target)) out object? target))
{ {
value = target; value = target;
+19 -44
View File
@@ -2,13 +2,10 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class NavigationScope(IPublisher publisher, public class NavigationScope(IServiceProvider provider,
IMediator mediator,
IServiceProvider provider,
IServiceFactory factory,
INavigationProvider navigationProvider,
INavigationRegionProvider navigationRegionProvider, INavigationRegionProvider navigationRegionProvider,
IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : IContentFactory contentFactory,
IPublisher publisher) :
INavigationScope INavigationScope
{ {
public async void Navigate(string route, public async void Navigate(string route,
@@ -30,7 +27,7 @@ public class NavigationScope(IPublisher publisher,
{ {
currentSegmentIndex++; currentSegmentIndex++;
if (contentTemplateDescriptorProvider.Get(segment) if (provider.GetKeyedService<IContentTemplateDescriptor>(segment)
is IContentTemplateDescriptor descriptor) is IContentTemplateDescriptor descriptor)
{ {
Dictionary<string, object>? arguments = parameters?.ToDictionary(x => x.Key, x => x.Value, StringComparer.InvariantCultureIgnoreCase) ?? []; Dictionary<string, object>? arguments = parameters?.ToDictionary(x => x.Key, x => x.Value, StringComparer.InvariantCultureIgnoreCase) ?? [];
@@ -42,14 +39,15 @@ public class NavigationScope(IPublisher publisher,
.TryGetValue(x.Name, out object? argument) ? argument : default) .TryGetValue(x.Name, out object? argument) ? argument : default)
.Where(argument => argument is not null)] : []; .Where(argument => argument is not null)] : [];
if (provider.GetRequiredKeyedService(descriptor.TemplateType, segment) is object view) if (provider.GetRequiredKeyedService(descriptor.TemplateType, descriptor.Key)
is object template)
{ {
if (region is not null) if (region is not null)
{ {
switch (region) switch (region)
{ {
case "self": case "self":
region = view; region = template;
break; break;
default: default:
@@ -64,38 +62,18 @@ public class NavigationScope(IPublisher publisher,
if (region is not null) if (region is not null)
{ {
Type createEventType = typeof(CreateEventArgs<>).MakeGenericType(descriptor.ContentType); object? content = await contentFactory.CreateAsync(descriptor, resolvedArguments);
object? content = null;
if (Activator.CreateInstance(createEventType, [null, resolvedArguments]) is object createEvent)
{
content = await mediator.Handle(descriptor.ContentType, createEvent, descriptor.Key);
}
if (content is null)
{
if (resolvedArguments is { Length: > 0 })
{
content = factory.Create(descriptor.ContentType, resolvedArguments);
}
else
{
content = provider.GetRequiredKeyedService(descriptor.ContentType, segment);
}
}
if (content is not null) if (content is not null)
{ {
if (navigationProvider.Get(region is Type type ? type : region.GetType()) is INavigation navigation) Type navigationType = region is Type type ? type : region.GetType();
Type navigateEventType = typeof(NavigateEventArgs<>).MakeGenericType(navigationType);
if (Activator.CreateInstance(navigateEventType, [region, template, content, sender, parameters])
is object navigateEvent)
{ {
Type navigateEventType = typeof(NavigateEventArgs<>).MakeGenericType(navigation.Type); publisher.Publish(navigateEvent, navigationType.Name);
if (Activator.CreateInstance(navigateEventType, [region, view, content, sender, parameters]) is object navigateEvent) if (currentSegmentIndex == segmentCount)
{ {
publisher.Publish(navigateEvent); navigated?.Invoke(this, EventArgs.Empty);
if (currentSegmentIndex == segmentCount)
{
navigated?.Invoke(this, EventArgs.Empty);
}
} }
} }
} }
@@ -114,14 +92,11 @@ public class NavigationScope(IPublisher publisher,
if (region is not null) if (region is not null)
{ {
if (navigationProvider.Get(region is Type type ? type : region.GetType()) Type navigationType = region is Type type ? type : region.GetType();
is INavigation navigation) Type navigateType = typeof(NavigateBackEventArgs<>).MakeGenericType(navigationType);
if (Activator.CreateInstance(navigateType, [region]) is object navigate)
{ {
Type navigateType = typeof(NavigateBackEventArgs<>).MakeGenericType(navigation.Type); publisher.Publish(navigate);
if (Activator.CreateInstance(navigateType, [region]) is object navigate)
{
publisher.Publish(navigate);
}
} }
} }
} }
@@ -1,8 +0,0 @@
namespace Toolkit.Foundation;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class NavigationTargetAttribute(string name) :
Attribute
{
public string Name => name;
}
+31 -28
View File
@@ -2,11 +2,18 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class Observable : public partial class Observable(IServiceProvider
provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) :
ObservableObject, ObservableObject,
IObservableViewModel, IObservableViewModel,
IActivityIndicator, IActivityIndicator,
IInitializer, IPostInitialization,
IInitialization,
IActivated, IActivated,
IDeactivating, IDeactivating,
IDeactivated, IDeactivated,
@@ -20,40 +27,25 @@ public partial class Observable :
{ {
private readonly Dictionary<string, object> trackedProperties = []; private readonly Dictionary<string, object> trackedProperties = [];
[ObservableProperty]
private bool initialized;
[ObservableProperty] [ObservableProperty]
private bool active; private bool active;
public Observable(IServiceProvider [ObservableProperty]
provider, private bool initialized;
IServiceFactory factory, private bool postInitialized;
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer)
{
Provider = provider;
Factory = factory;
Mediator = mediator;
Publisher = publisher;
Disposer = disposer;
subscriber.Add(this);
}
public event EventHandler? DeactivateHandler; public event EventHandler? DeactivateHandler;
public IDisposer Disposer { get; } public IDisposer Disposer { get; } = disposer;
public IServiceFactory Factory { get; } public IServiceFactory Factory { get; } = factory;
public IMediator Mediator { get; } public IMediator Mediator { get; } = mediator;
public IServiceProvider Provider { get; } public IServiceProvider Provider { get; } = provider;
public IPublisher Publisher { get; } public IPublisher Publisher { get; } = publisher;
public ISubscriber Subscriber { get; } = subscriber;
public void Commit() public void Commit()
{ {
@@ -95,6 +87,17 @@ public partial class Observable :
public virtual Task OnDeactivating() => public virtual Task OnDeactivating() =>
Task.CompletedTask; Task.CompletedTask;
public virtual void PostInitialize()
{
if (postInitialized)
{
return;
}
postInitialized = true;
Subscriber.Subscribe(this);
}
public void Revert() public void Revert()
{ {
foreach (object trackedProperty in trackedProperties.Values) foreach (object trackedProperty in trackedProperties.Values)
@@ -124,7 +127,7 @@ public partial class Observable<TValue> :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TValue value) : base(provider, factory, mediator, publisher, subscriber, disposer) TValue value) : base(provider, factory, mediator, publisher, subscriber, disposer)
{ {
@@ -154,7 +157,7 @@ public partial class Observable<TKey, TValue> :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TKey key, TKey key,
TValue value) : base(provider, factory, mediator, publisher, subscriber, disposer) TValue value) : base(provider, factory, mediator, publisher, subscriber, disposer)
+60 -34
View File
@@ -9,7 +9,8 @@ namespace Toolkit.Foundation;
public partial class ObservableCollection<TItem> : public partial class ObservableCollection<TItem> :
ObservableObject, ObservableObject,
IObservableCollectionViewModel<TItem>, IObservableCollectionViewModel<TItem>,
IInitializer, IPostInitialization,
IInitialization,
IActivated, IActivated,
IDeactivating, IDeactivating,
IDeactivated, IDeactivated,
@@ -32,7 +33,7 @@ public partial class ObservableCollection<TItem> :
INotificationHandler<MoveToEventArgs<TItem>>, INotificationHandler<MoveToEventArgs<TItem>>,
INotificationHandler<ReplaceEventArgs<TItem>>, INotificationHandler<ReplaceEventArgs<TItem>>,
INotificationHandler<SelectionEventArgs<TItem>> INotificationHandler<SelectionEventArgs<TItem>>
where TItem : notnull, where TItem : notnull,
IDisposable IDisposable
{ {
private readonly System.Collections.ObjectModel.ObservableCollection<TItem> collection = []; private readonly System.Collections.ObjectModel.ObservableCollection<TItem> collection = [];
@@ -52,6 +53,8 @@ public partial class ObservableCollection<TItem> :
[ObservableProperty] [ObservableProperty]
private bool initialized; private bool initialized;
private bool postInitialized;
[ObservableProperty] [ObservableProperty]
private TItem? selectedItem; private TItem? selectedItem;
@@ -59,17 +62,16 @@ public partial class ObservableCollection<TItem> :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer) IDisposer disposer)
{ {
Provider = provider; Provider = provider;
Factory = factory; Factory = factory;
Mediator = mediator; Mediator = mediator;
Publisher = publisher; Publisher = publisher;
Subscriber = subscriber;
Disposer = disposer; Disposer = disposer;
subscriber.Add(this);
dispatcher = Provider.GetRequiredService<IDispatcher>(); dispatcher = Provider.GetRequiredService<IDispatcher>();
collection.CollectionChanged += OnCollectionChanged; collection.CollectionChanged += OnCollectionChanged;
} }
@@ -78,7 +80,7 @@ public partial class ObservableCollection<TItem> :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
IEnumerable<TItem> items) IEnumerable<TItem> items)
{ {
@@ -86,10 +88,9 @@ public partial class ObservableCollection<TItem> :
Factory = factory; Factory = factory;
Mediator = mediator; Mediator = mediator;
Publisher = publisher; Publisher = publisher;
Subscriber = subscriber;
Disposer = disposer; Disposer = disposer;
subscriber.Add(this);
dispatcher = Provider.GetRequiredService<IDispatcher>(); dispatcher = Provider.GetRequiredService<IDispatcher>();
collection.CollectionChanged += OnCollectionChanged; collection.CollectionChanged += OnCollectionChanged;
@@ -117,6 +118,7 @@ public partial class ObservableCollection<TItem> :
public IServiceProvider Provider { get; private set; } public IServiceProvider Provider { get; private set; }
public IPublisher Publisher { get; private set; } public IPublisher Publisher { get; private set; }
public ISubscriber Subscriber { get; }
object ICollection.SyncRoot => this; object ICollection.SyncRoot => this;
@@ -149,18 +151,19 @@ public partial class ObservableCollection<TItem> :
where T : where T :
TItem TItem
{ {
T? item = Factory.Create<T>(parameters); T? item = Factory.Create<T>(args =>
{
if (args is IPostInitialization initialization)
{
initialization.PostInitialize();
}
}, parameters);
Add(item); Add(item);
return item; return item;
} }
public void Clear(Action<ObservableCollection<TItem>> factory)
{
Clear();
factory.Invoke(this);
}
public void Add(TItem item) public void Add(TItem item)
{ {
int index = collection.Count; int index = collection.Count;
@@ -200,6 +203,11 @@ public partial class ObservableCollection<TItem> :
} }
} }
public void Clear(Action<ObservableCollection<TItem>> factory)
{
Clear();
factory.Invoke(this);
}
public void Clear() public void Clear()
{ {
clearing = true; clearing = true;
@@ -246,17 +254,6 @@ public partial class ObservableCollection<TItem> :
Disposer.Dispose(this); Disposer.Dispose(this);
} }
public void Fetch(bool reset = false)
{
if (reset)
{
Clear();
}
SynchronizeExpression expression = BuildAggregateExpression();
Publisher.PublishUI(expression.Value, expression.Key);
}
public void Fetch(Func<SynchronizeExpression> aggregateDelegate, public void Fetch(Func<SynchronizeExpression> aggregateDelegate,
bool reset = false) bool reset = false)
{ {
@@ -268,6 +265,7 @@ public partial class ObservableCollection<TItem> :
SynchronizeExpression expression = aggregateDelegate.Invoke(); SynchronizeExpression expression = aggregateDelegate.Invoke();
Publisher.PublishUI(expression.Value, expression.Key); Publisher.PublishUI(expression.Value, expression.Key);
} }
public IEnumerator<TItem> GetEnumerator() => public IEnumerator<TItem> GetEnumerator() =>
collection.GetEnumerator(); collection.GetEnumerator();
@@ -413,7 +411,7 @@ public partial class ObservableCollection<TItem> :
} }
Initialized = true; Initialized = true;
Fetch(); Synchronize();
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -423,14 +421,21 @@ public partial class ObservableCollection<TItem> :
where T : where T :
TItem TItem
{ {
T? item = Factory.Create<T>(parameters); T? item = Factory.Create<T>(args =>
{
if (args is IPostInitialization initialization)
{
initialization.PostInitialize();
}
}, parameters);
InsertItem(index, item); InsertItem(index, item);
UpdateSelection(item); UpdateSelection(item);
return item; return item;
} }
public void Insert(int index, public void Insert(int index,
TItem item) TItem item)
{ {
InsertItem(index, item); InsertItem(index, item);
@@ -516,6 +521,17 @@ public partial class ObservableCollection<TItem> :
public virtual Task OnDeactivating() => public virtual Task OnDeactivating() =>
Task.CompletedTask; Task.CompletedTask;
public virtual void PostInitialize()
{
if (postInitialized)
{
return;
}
postInitialized = true;
Subscriber.Subscribe(this);
}
public bool Remove(TItem item) public bool Remove(TItem item)
{ {
int index = collection.IndexOf(item); int index = collection.IndexOf(item);
@@ -574,6 +590,16 @@ public partial class ObservableCollection<TItem> :
} }
} }
public void Synchronize(bool reset = false)
{
if (reset)
{
Clear();
}
SynchronizeExpression expression = BuildAggregateExpression();
Publisher.PublishUI(expression.Value, expression.Key);
}
public void Track<T>(string propertyName, Func<T> getter, Action<T> setter) public void Track<T>(string propertyName, Func<T> getter, Action<T> setter)
{ {
if (!trackedProperties.ContainsKey(propertyName)) if (!trackedProperties.ContainsKey(propertyName))
@@ -657,7 +683,7 @@ public partial class ObservableCollection<TValue, TViewModel>(IServiceProvider p
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TValue value) : ObservableCollection<TViewModel>(provider, factory, mediator, publisher, subscriber, disposer) TValue value) : ObservableCollection<TViewModel>(provider, factory, mediator, publisher, subscriber, disposer)
where TViewModel : notnull, IDisposable where TViewModel : notnull, IDisposable
@@ -690,7 +716,7 @@ public partial class ObservableCollection<TViewModel, TKey, TValue> :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
TKey key, TKey key,
TValue value) : base(provider, factory, mediator, publisher, subscriber, disposer) TValue value) : base(provider, factory, mediator, publisher, subscriber, disposer)
@@ -703,7 +729,7 @@ public partial class ObservableCollection<TViewModel, TKey, TValue> :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
IEnumerable<TViewModel> items, IEnumerable<TViewModel> items,
TKey key, TKey key,
@@ -728,7 +754,7 @@ public class ObservableCollection :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer) : base(provider, factory, mediator, publisher, subscriber, disposer) IDisposer disposer) : base(provider, factory, mediator, publisher, subscriber, disposer)
{ {
} }
@@ -737,7 +763,7 @@ public class ObservableCollection :
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
IEnumerable<IDisposable> items) : base(provider, factory, mediator, publisher, subscriber, disposer, items) IEnumerable<IDisposable> items) : base(provider, factory, mediator, publisher, subscriber, disposer, items)
{ {
+9
View File
@@ -17,4 +17,13 @@ public class ServiceFactory(Func<Type, object?[]?, object> factory) :
public object Create(Type type, params object?[]? parameters) => public object Create(Type type, params object?[]? parameters) =>
factory(type, parameters); factory(type, parameters);
public object Create(Type type, Action<object> serviceDelegate,
params object?[]? parameters)
{
object service = factory(type, parameters);
serviceDelegate.Invoke(service);
return service;
}
} }
@@ -2,11 +2,11 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class Subscription(SubscriptionCollection subscriptions, public class Subscriber(SubscriptionCollection subscriptions,
IDisposer disposer) : IDisposer disposer) :
ISubscription ISubscriber
{ {
public void Add(object subscriber) public void Subscribe(object subscriber)
{ {
Type handlerType = subscriber.GetType(); Type handlerType = subscriber.GetType();
@@ -45,7 +45,7 @@ public class Subscription(SubscriptionCollection subscriptions,
} }
} }
public void Remove(object subscriber) public void Unsubscribe(object subscriber)
{ {
Type handlerType = subscriber.GetType(); Type handlerType = subscriber.GetType();
IDictionary<Type, List<object>> subscribers = GetSubscriptionKeys(subscriber); IDictionary<Type, List<object>> subscribers = GetSubscriptionKeys(subscriber);
+1 -1
View File
@@ -6,7 +6,7 @@ public partial class ValueViewModel<TValue>(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscriber subscriber,
IDisposer disposer) : IDisposer disposer) :
Observable(provider, factory, mediator, publisher, subscriber, disposer) Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {