Add project files.
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>TheXamlGuy.Framework.Avalonia</RootNamespace>
|
||||
<AssemblyName>TheXamlGuy.Framework.Avalonia</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.0.0-preview3" />
|
||||
<PackageReference Include="MicroCom.Runtime" Version="0.10.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\UI\Avalonia\Avalonia.csproj" />
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="FluentAvalonia">
|
||||
<HintPath>References\FluentAvalonia.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public static class IHostBuilderExtensions
|
||||
{
|
||||
public static IHostBuilder ConfigureTemplates(this IHostBuilder hostBuilder, Action<ITemplateBuilder> builderDelegate)
|
||||
{
|
||||
hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) =>
|
||||
{
|
||||
TemplateBuilder? builder = new();
|
||||
builderDelegate?.Invoke(builder);
|
||||
|
||||
serviceCollection
|
||||
.AddSingleton(builder.Descriptors)
|
||||
.AddSingleton<ITemplateDescriptorProvider, TemplateDescriptorProvider>()
|
||||
.AddSingleton<ITemplateFactory, TemplateFactory>()
|
||||
.AddSingleton<INamedTemplateFactory, NamedTemplateFactory>()
|
||||
.AddSingleton<ITypedDataTemplateFactory, TypedDataTemplateFactory>()
|
||||
.AddSingleton<INamedDataTemplateFactory, NamedDataTemplateFactory>()
|
||||
.AddSingleton<ITemplateSelector, TemplateSelector>();
|
||||
|
||||
foreach (ITemplateDescriptor? descriptor in builder.Descriptors)
|
||||
{
|
||||
serviceCollection.Add(new ServiceDescriptor(descriptor.TemplateType, descriptor.TemplateType, descriptor.Lifetime));
|
||||
serviceCollection.Add(new ServiceDescriptor(descriptor.DataType, descriptor.DataType, descriptor.Lifetime));
|
||||
}
|
||||
});
|
||||
|
||||
return hostBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public static class IServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddRequiredAvalonia(this IServiceCollection serviceCollection)
|
||||
{
|
||||
return serviceCollection
|
||||
.AddSingleton<IRouter, Router>()
|
||||
.AddSingleton<IRouteDescriptorCollection, RouteDescriptorCollection>()
|
||||
.AddSingleton<IRouterContext, RouterContext>()
|
||||
.AddTransient<IMediatorHandler<Navigate>, NavigateHandler>()
|
||||
.RegisterHandlers();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
using TheXamlGuy.UI.Avalonia;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class NavigateExtension : TriggerExtension
|
||||
{
|
||||
private static readonly AttachedProperty<IEventAggregator> EventAggregatorProperty =
|
||||
AvaloniaProperty.RegisterAttached<NavigateExtension, Control, IEventAggregator>("EventAggregator");
|
||||
|
||||
private static readonly AttachedProperty<object> ParameterProperty =
|
||||
AvaloniaProperty.RegisterAttached<NavigateExtension, Control, object>("Parameter");
|
||||
|
||||
private static readonly AvaloniaProperty RouteProperty =
|
||||
AvaloniaProperty.RegisterAttached<NavigateExtension, Control, object>("Route");
|
||||
|
||||
private static readonly AvaloniaProperty ToProperty =
|
||||
AvaloniaProperty.RegisterAttached<NavigateExtension, Control, object>("To");
|
||||
|
||||
private readonly Binding eventAggregatorBinding;
|
||||
private readonly List<object> parameters = new();
|
||||
private readonly Binding toBinding;
|
||||
private object? route;
|
||||
private Binding? routeBinding;
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8,
|
||||
object args9)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8,
|
||||
object args9,
|
||||
object args10)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
|
||||
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8,
|
||||
object args9,
|
||||
object args10,
|
||||
object args11)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
|
||||
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
|
||||
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8,
|
||||
object args9,
|
||||
object args10,
|
||||
object args11,
|
||||
object args12)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
|
||||
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
|
||||
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
|
||||
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8,
|
||||
object args9,
|
||||
object args10,
|
||||
object args11,
|
||||
object args12,
|
||||
object args13)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
|
||||
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
|
||||
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
|
||||
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
|
||||
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8,
|
||||
object args9,
|
||||
object args10,
|
||||
object args11,
|
||||
object args12,
|
||||
object args13,
|
||||
object args14)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
|
||||
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
|
||||
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
|
||||
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
|
||||
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
|
||||
parameters.Add(args14 is MarkupExtension ? args14 : args14.ToBinding());
|
||||
}
|
||||
|
||||
public NavigateExtension(object eventAggregator,
|
||||
object to,
|
||||
object args1,
|
||||
object args2,
|
||||
object args3,
|
||||
object args4,
|
||||
object args5,
|
||||
object args6,
|
||||
object args7,
|
||||
object args8,
|
||||
object args9,
|
||||
object args10,
|
||||
object args11,
|
||||
object args12,
|
||||
object args13,
|
||||
object args14,
|
||||
object args15)
|
||||
{
|
||||
eventAggregatorBinding = eventAggregator.ToBinding();
|
||||
this.toBinding = to is Binding toBinding ? toBinding : to.ToBinding();
|
||||
|
||||
parameters.Add(args1 is MarkupExtension ? args1 : args1.ToBinding());
|
||||
parameters.Add(args2 is MarkupExtension ? args2 : args2.ToBinding());
|
||||
parameters.Add(args3 is MarkupExtension ? args3 : args3.ToBinding());
|
||||
parameters.Add(args4 is MarkupExtension ? args4 : args4.ToBinding());
|
||||
parameters.Add(args5 is MarkupExtension ? args5 : args5.ToBinding());
|
||||
parameters.Add(args6 is MarkupExtension ? args6 : args6.ToBinding());
|
||||
parameters.Add(args7 is MarkupExtension ? args7 : args7.ToBinding());
|
||||
parameters.Add(args8 is MarkupExtension ? args8 : args8.ToBinding());
|
||||
parameters.Add(args9 is MarkupExtension ? args9 : args9.ToBinding());
|
||||
parameters.Add(args10 is MarkupExtension ? args10 : args10.ToBinding());
|
||||
parameters.Add(args11 is MarkupExtension ? args11 : args11.ToBinding());
|
||||
parameters.Add(args12 is MarkupExtension ? args12 : args12.ToBinding());
|
||||
parameters.Add(args13 is MarkupExtension ? args13 : args13.ToBinding());
|
||||
parameters.Add(args14 is MarkupExtension ? args14 : args14.ToBinding());
|
||||
parameters.Add(args15 is MarkupExtension ? args15 : args15.ToBinding());
|
||||
}
|
||||
|
||||
public object? Route
|
||||
{
|
||||
get
|
||||
{
|
||||
return route;
|
||||
}
|
||||
set
|
||||
{
|
||||
route = value;
|
||||
if (route is not null)
|
||||
{
|
||||
routeBinding = route.ToBinding();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override void OnInvoked(object sender, EventArgs args)
|
||||
{
|
||||
if (TargetObject is not null)
|
||||
{
|
||||
TargetObject.Bind(EventAggregatorProperty, eventAggregatorBinding);
|
||||
if (TargetObject.GetValue(EventAggregatorProperty) is IEventAggregator eventAggregator)
|
||||
{
|
||||
TargetObject.Bind(ToProperty, toBinding);
|
||||
if (TargetObject.GetValue(ToProperty) is { } to)
|
||||
{
|
||||
object? route = null;
|
||||
if (routeBinding is not null)
|
||||
{
|
||||
TargetObject.Bind(RouteProperty, routeBinding);
|
||||
route = TargetObject.GetValue(RouteProperty);
|
||||
}
|
||||
|
||||
if (to is string name)
|
||||
{
|
||||
if (toBinding?.StringFormat is string format)
|
||||
{
|
||||
name = string.Format(format, name);
|
||||
}
|
||||
|
||||
eventAggregator.Publish(new Navigate(name, parameters.ToArray()) { Route = route });
|
||||
}
|
||||
|
||||
if (to is Type type)
|
||||
{
|
||||
eventAggregator.Publish(new Navigate(type, parameters.ToArray()) { Route = route });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
base.OnInvoked(sender, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using TheXamlGuy.UI.Avalonia;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class RouteExtension : MarkupExtension
|
||||
{
|
||||
private static readonly AttachedProperty<object> RouteProperty =
|
||||
AvaloniaProperty.RegisterAttached<RouteExtension, Control, object>("Route");
|
||||
|
||||
private readonly string name;
|
||||
private readonly Binding routeBinding;
|
||||
|
||||
public RouteExtension(object route, string name)
|
||||
{
|
||||
routeBinding = route.ToBinding();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private bool TryGetBinding(AvaloniaObject sender, out object? binding)
|
||||
{
|
||||
binding = sender.GetValue(StyledElement.DataContextProperty);
|
||||
return binding is not null;
|
||||
}
|
||||
|
||||
public override object? ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget target)
|
||||
{
|
||||
if (target.TargetObject is TemplatedControl control)
|
||||
{
|
||||
if (!TryGetBinding(control, out object? binding))
|
||||
{
|
||||
void HandleDataContextChanged(object? sender, EventArgs args)
|
||||
{
|
||||
if (TryGetBinding(control, out binding))
|
||||
{
|
||||
control.Loaded -= HandleLoaded;
|
||||
control.Bind(RouteProperty, routeBinding);
|
||||
|
||||
if (control?.GetValue(RouteProperty) is IRouter route)
|
||||
{
|
||||
route.Add(name, control);
|
||||
control.ClearValue(RouteProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control.DataContextChanged += HandleDataContextChanged;
|
||||
|
||||
void HandleLoaded(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
control.Loaded -= HandleLoaded;
|
||||
if (TryGetBinding(control, out binding))
|
||||
{
|
||||
control.Bind(RouteProperty, routeBinding);
|
||||
if (control?.GetValue(RouteProperty) is IRouter route)
|
||||
{
|
||||
route.Add(name, control);
|
||||
control.ClearValue(RouteProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control.Loaded += HandleLoaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
control.Bind(RouteProperty, routeBinding);
|
||||
if (control?.GetValue(RouteProperty) is IRouter route)
|
||||
{
|
||||
route.Add(name, control);
|
||||
control.ClearValue(RouteProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
using Avalonia.Metadata;
|
||||
|
||||
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "TheXamlGuy.Framework.Avalonia")]
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class ContentDialogRouteHandler : RouteHandler<ContentDialog>
|
||||
{
|
||||
public override async void Handle(Route<ContentDialog> request)
|
||||
{
|
||||
if (request.Template is ContentDialog contentDialog)
|
||||
{
|
||||
contentDialog.DataContext = request.Data;
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ContentControlRouteHandler : RouteHandler<ContentControl>
|
||||
{
|
||||
public override void Handle(Route<ContentControl> request)
|
||||
{
|
||||
if (request.Template is TemplatedControl control)
|
||||
{
|
||||
control.DataContext = request.Data;
|
||||
request.Target.Content = control;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Avalonia.Controls.Primitives;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class FrameRouteHandler : RouteHandler<Frame>
|
||||
{
|
||||
public override void Handle(Route<Frame> request)
|
||||
{
|
||||
if (request.Template is Type type)
|
||||
{
|
||||
void HandleNavigated(object sender, NavigationEventArgs args)
|
||||
{
|
||||
request.Target.Navigated -= HandleNavigated;
|
||||
if (request.Target.Content is TemplatedControl control)
|
||||
{
|
||||
control.DataContext = request.Data;
|
||||
}
|
||||
}
|
||||
|
||||
request.Target.Navigated += HandleNavigated;
|
||||
request.Target.Navigate(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public interface IRouteDescriptor
|
||||
{
|
||||
object Route { get; }
|
||||
|
||||
string? Name { get; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public interface IRouteDescriptorCollection : IList<IRouteDescriptor>
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public interface IRouter
|
||||
{
|
||||
void Add(string name, object route);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia
|
||||
{
|
||||
public class NavigateHandler : IMediatorHandler<Navigate>
|
||||
{
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
|
||||
public NavigateHandler(IEventAggregator eventAggregator)
|
||||
{
|
||||
this.eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
public void Handle(Navigate request)
|
||||
{
|
||||
eventAggregator.Publish(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class Navigated<TContent, TDataContext> where TContent : class where TDataContext : class
|
||||
{
|
||||
public Navigated()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Navigated(TContent content, TDataContext dataContext, IDictionary<string, object>? parameters = null)
|
||||
{
|
||||
Content = content;
|
||||
DataContext = dataContext;
|
||||
Parameters = parameters;
|
||||
}
|
||||
|
||||
public TContent? Content { get; }
|
||||
|
||||
public TDataContext? DataContext { get; }
|
||||
|
||||
public IDictionary<string, object>? Parameters { get; }
|
||||
}
|
||||
|
||||
public class Navigated
|
||||
{
|
||||
public static Navigated<TContent, TDataTemplate> Create<TContent, TDataTemplate>(TContent content, TDataTemplate dataContext, IDictionary<string, object>? parameters = null) where TContent : class where TDataTemplate : class
|
||||
{
|
||||
return new Navigated<TContent, TDataTemplate>(content, dataContext, parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using Avalonia.Controls.Primitives;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public record Route<TTarget>(TTarget Target, object? Data, object? Template) where TTarget : TemplatedControl;
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public record RouteDescriptor : IRouteDescriptor
|
||||
{
|
||||
public RouteDescriptor(string name, object route)
|
||||
{
|
||||
Name = name;
|
||||
Route = route;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public object Route { get; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class RouteDescriptorCollection : List<IRouteDescriptor>, IRouteDescriptorCollection
|
||||
{
|
||||
public RouteDescriptorCollection(IEnumerable<IRouteDescriptor> collection) : base(collection)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Avalonia.Controls.Primitives;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public abstract class RouteHandler<TTarget> : IMediatorHandler<Route<TTarget>> where TTarget : TemplatedControl
|
||||
{
|
||||
public abstract void Handle(Route<TTarget> request);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class Router : IRouter
|
||||
{
|
||||
private readonly IRouteDescriptorCollection routes;
|
||||
|
||||
public Router(IRouteDescriptorCollection routes)
|
||||
{
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
public void Add(string name, object route)
|
||||
{
|
||||
if (route is TemplatedControl control)
|
||||
{
|
||||
void HandleUnloaded(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
if (routes.FirstOrDefault(x => x.Route == sender) is IRouteDescriptor descriptor)
|
||||
{
|
||||
routes.Remove(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
control.Unloaded += HandleUnloaded;
|
||||
}
|
||||
|
||||
if (routes.FirstOrDefault(x => x.Name == name) is IRouteDescriptor descriptor)
|
||||
{
|
||||
routes.Remove(descriptor);
|
||||
}
|
||||
|
||||
routes.Add(new RouteDescriptor(name, route));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class RouterContext : IRouterContext
|
||||
{
|
||||
private readonly IRouteDescriptorCollection descriptors;
|
||||
private readonly IDisposer disposer;
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
private readonly IMediator mediator;
|
||||
private readonly INamedDataTemplateFactory namedDataTemplateFactory;
|
||||
private readonly INamedTemplateFactory namedTemplateFactory;
|
||||
private readonly ITemplateDescriptorProvider templateDescriptorProvider;
|
||||
private readonly ITemplateFactory templateFactory;
|
||||
private readonly ITypedDataTemplateFactory typedDataTemplateFactory;
|
||||
|
||||
public RouterContext(ITemplateDescriptorProvider templateDescriptorProvider,
|
||||
ITemplateFactory templateFactory,
|
||||
INamedTemplateFactory namedTemplateFactory,
|
||||
INamedDataTemplateFactory namedDataTemplateFactory,
|
||||
ITypedDataTemplateFactory typedDataTemplateFactory,
|
||||
IEventAggregator eventAggregator,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
IRouteDescriptorCollection descriptors)
|
||||
{
|
||||
this.templateDescriptorProvider = templateDescriptorProvider;
|
||||
this.templateFactory = templateFactory;
|
||||
this.namedTemplateFactory = namedTemplateFactory;
|
||||
this.namedDataTemplateFactory = namedDataTemplateFactory;
|
||||
this.typedDataTemplateFactory = typedDataTemplateFactory;
|
||||
this.eventAggregator = eventAggregator;
|
||||
this.mediator = mediator;
|
||||
this.disposer = disposer;
|
||||
this.descriptors = descriptors;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
disposer.Add(this, eventAggregator.Current.SubscribeUI<Navigate>(OnNavigate));
|
||||
disposer.Add(this, eventAggregator.Current.SubscribeUI<NavigateBack>(OnNavigateBack));
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void OnNavigate(Navigate args)
|
||||
{
|
||||
object? data = null;
|
||||
object? template = null;
|
||||
|
||||
Dictionary<string, object> keyedParameters = new();
|
||||
List<object> parameters = new();
|
||||
|
||||
foreach (object? parameter in args.Parameters)
|
||||
{
|
||||
if (parameter is KeyValuePair<string, object> keyed)
|
||||
{
|
||||
keyedParameters.Add(keyed.Key, keyed.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.Add(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Name is { Length: > 0 } name)
|
||||
{
|
||||
data = namedDataTemplateFactory.Create(name, parameters.ToArray());
|
||||
template = descriptors.FirstOrDefault(x => args.Route is string { } name && name == x.Name) is { Route: Frame } ? templateDescriptorProvider.Get(name)?.TemplateType : namedTemplateFactory.Create(name);
|
||||
}
|
||||
|
||||
if (args.Type is Type type)
|
||||
{
|
||||
data = typedDataTemplateFactory.Create(type, parameters.ToArray());
|
||||
template = descriptors.FirstOrDefault(x => args.Route is string { } name && name == x.Name) is { Route: Frame } ? templateDescriptorProvider.Get(type)?.TemplateType : templateFactory.Create(data);
|
||||
}
|
||||
|
||||
if (template is not null)
|
||||
{
|
||||
if (template is ContentDialog contentDialog)
|
||||
{
|
||||
mediator.Handle(new Route<ContentDialog>(contentDialog, data, template));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (descriptors.FirstOrDefault(x => args.Route is string { } name && name == x.Name) is RouteDescriptor descriptor)
|
||||
{
|
||||
switch (descriptor.Route)
|
||||
{
|
||||
case Frame frame:
|
||||
mediator.Handle(new Route<Frame>(frame, data, template));
|
||||
break;
|
||||
case ContentControl contentControl:
|
||||
mediator.Handle(new Route<ContentControl>(contentControl, data, template));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (descriptors.FirstOrDefault(x => args.Route is string { } name && name == x.Name) is RouteDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.Route is ContentControl contentControl)
|
||||
{
|
||||
contentControl.Content = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNavigateBack(NavigateBack args)
|
||||
{
|
||||
if (descriptors.FirstOrDefault(x => args.Route is string { } name && name == x.Name) is RouteDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.Route is ContentControl { Content: TemplatedControl content })
|
||||
{
|
||||
if (content.DataContext is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor.Route is Frame frame)
|
||||
{
|
||||
frame.GoBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Markup.Xaml.Templates;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Avalonia;
|
||||
|
||||
public class TemplateSelector : DataTemplateSelector, ITemplateSelector
|
||||
{
|
||||
private readonly Dictionary<object, DataTemplate> dataTracking = new();
|
||||
|
||||
private readonly ITemplateFactory templateFactory;
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
|
||||
public TemplateSelector(ITemplateFactory templateFactory,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
this.templateFactory = templateFactory;
|
||||
this.eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
protected override IDataTemplate SelectTemplateCore(object item, IControl container)
|
||||
{
|
||||
if (item is not null)
|
||||
{
|
||||
if (dataTracking.TryGetValue(item, out DataTemplate? cachedDataTemplate))
|
||||
{
|
||||
return cachedDataTemplate;
|
||||
}
|
||||
|
||||
return new FuncDataTemplate(item.GetType(), (value, namescope) =>
|
||||
{
|
||||
if (templateFactory.Create(item) is TemplatedControl template)
|
||||
{
|
||||
return template;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return base.SelectTemplateCore(item, container);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0-rc.2.22472.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Media\Capture\Capture.csproj" />
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public class CameraBuilder : ICameraBuilder
|
||||
{
|
||||
private readonly List<ICameraBuilderConfiguration> configurations = new();
|
||||
|
||||
public IReadOnlyCollection<ICameraBuilderConfiguration> Configurations => new ReadOnlyCollection<ICameraBuilderConfiguration>(configurations);
|
||||
|
||||
public ICameraBuilderConfiguration<TConfiguration> Add<TConfiguration>(IConfiguration configuration) where TConfiguration : IRemoteCameraConfiguration, new()
|
||||
{
|
||||
CameraBuilderConfiguration<TConfiguration>? builderConfiguration = new(configuration);
|
||||
configurations.Add(builderConfiguration);
|
||||
|
||||
return builderConfiguration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public class CameraBuilderConfiguration<TConfiguration> : ICameraBuilderConfiguration<TConfiguration> where TConfiguration : INamedCameraConfiguration, new()
|
||||
{
|
||||
private readonly IConfiguration configuration;
|
||||
|
||||
public CameraBuilderConfiguration(IConfiguration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public Func<IServiceProvider, ICameraContext> Factory => (IServiceProvider provider) => provider.GetService<ICameraFactory>()!.Create(configuration.Get<TConfiguration>());
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using TheXamlGuy.Framework.Core;
|
||||
using TheXamlGuy.Media.Capture;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public class CameraContext : ICameraContext
|
||||
{
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
private ILowLagPhotoCapture? lowLagPhotoCapture;
|
||||
private IMediaCapture? mediaCapture;
|
||||
|
||||
public CameraContext(IEventAggregator eventAggregator)
|
||||
{
|
||||
this.eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
IReadOnlyList<IMediaFrameSource> sourceGroups = await MediaFrameSource.FindAllAsync();
|
||||
if (sourceGroups.FirstOrDefault(x => x.DisplayName.Contains("USB", StringComparison.InvariantCultureIgnoreCase)) is IMediaFrameSource source)
|
||||
{
|
||||
if (source.SupportedFormats.OrderByDescending(x => x.Size.Width & x.Size.Height).FirstOrDefault() is MediaFrameFormat bestSupportedFormat)
|
||||
{
|
||||
source.SetFormat(bestSupportedFormat);
|
||||
}
|
||||
|
||||
MediaCaptureInitializationSettings settings = new()
|
||||
{
|
||||
Source = source
|
||||
};
|
||||
|
||||
mediaCapture = new MediaCapture();
|
||||
mediaCapture.Initialize(settings);
|
||||
|
||||
lowLagPhotoCapture = await mediaCapture.PrepareLowLagPhotoCaptureAsync();
|
||||
eventAggregator.SubscribeUI<Capture>(OnCapture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void OnCapture(Capture args)
|
||||
{
|
||||
if (lowLagPhotoCapture is not null)
|
||||
{
|
||||
await lowLagPhotoCapture.CaptureAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public class CameraFactory : ICameraFactory
|
||||
{
|
||||
private readonly Dictionary<INamedCameraConfiguration, ICameraContext> cache = new();
|
||||
private readonly IServiceFactory factory;
|
||||
|
||||
public CameraFactory(IServiceFactory factory)
|
||||
{
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public ICameraContext Create(INamedCameraConfiguration configuration)
|
||||
{
|
||||
if (cache.TryGetValue(configuration, out ICameraContext? context))
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
if (configuration is IRemoteCameraConfiguration)
|
||||
{
|
||||
context = factory.Create<RemoteCameraContext>();
|
||||
}
|
||||
else
|
||||
{
|
||||
context = factory.Create<CameraContext>();
|
||||
}
|
||||
|
||||
cache.Add(configuration, context);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public record Capture;
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public record Captured
|
||||
{
|
||||
public Bitmap? Photo { get; init; }
|
||||
|
||||
public int Width { get; init; }
|
||||
|
||||
public int Height { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public interface ICameraBuilder
|
||||
{
|
||||
IReadOnlyCollection<ICameraBuilderConfiguration> Configurations { get; }
|
||||
|
||||
ICameraBuilderConfiguration<TConfiguration> Add<TConfiguration>(IConfiguration configuration) where TConfiguration : IRemoteCameraConfiguration, new();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public interface ICameraBuilderConfiguration
|
||||
{
|
||||
Func<IServiceProvider, ICameraContext> Factory { get; }
|
||||
}
|
||||
|
||||
public interface ICameraBuilderConfiguration<TConfiguration> : ICameraBuilderConfiguration where TConfiguration : INamedCameraConfiguration, new()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public interface ICameraConfiguration : INamedCameraConfiguration
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public interface ICameraContext : IInitializer
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public interface ICameraFactory
|
||||
{
|
||||
ICameraContext Create(INamedCameraConfiguration configuration);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public static class IHostBuilderExtensions
|
||||
{
|
||||
public static IHostBuilder ConfigureCamera(this IHostBuilder hostBuilder, Action<HostBuilderContext, ICameraBuilder> builderDelegate)
|
||||
{
|
||||
hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) =>
|
||||
{
|
||||
CameraBuilder? builder = new();
|
||||
|
||||
builderDelegate.Invoke(hostBuilderContext, builder);
|
||||
serviceCollection.TryAddSingleton<ICameraFactory, CameraFactory>();
|
||||
|
||||
foreach (ICameraBuilderConfiguration configuration in builder.Configurations)
|
||||
{
|
||||
serviceCollection.AddSingleton(provider => configuration.Factory.Invoke(provider));
|
||||
}
|
||||
});
|
||||
|
||||
return hostBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public interface INamedCameraConfiguration
|
||||
{
|
||||
string Name { get; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public interface IRemoteCameraConfiguration : INamedCameraConfiguration
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public class RemoteCameraConfiguration : IRemoteCameraConfiguration
|
||||
{
|
||||
[NotNull]
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using TheXamlGuy.Framework.Core;
|
||||
using TheXamlGuy.Media.Capture;
|
||||
|
||||
namespace TheXamlGuy.Framework.Camera;
|
||||
|
||||
public class RemoteCameraContext : ICameraContext
|
||||
{
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
private ILowLagPhotoCapture? lowLagPhotoCapture;
|
||||
private IRemoteMediaCapture? mediaCapture;
|
||||
|
||||
public RemoteCameraContext(IEventAggregator eventAggregator)
|
||||
{
|
||||
this.eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
IReadOnlyList<IRemoteMediaFrameSource> sourceGroups = await RemoteMediaFrameSource.FindAllAsync();
|
||||
if (sourceGroups.FirstOrDefault(x => x.DisplayName.Contains("DSC-HX60", StringComparison.InvariantCultureIgnoreCase)) is IRemoteMediaFrameSource source)
|
||||
{
|
||||
RemoteMediaCaptureInitializationSettings settings = new()
|
||||
{
|
||||
Source = source
|
||||
};
|
||||
|
||||
mediaCapture = new RemoteMediaCapture();
|
||||
mediaCapture.Initialize(settings);
|
||||
|
||||
lowLagPhotoCapture = await mediaCapture.PrepareLowLagPhotoCaptureAsync();
|
||||
eventAggregator.SubscribeUI<Capture>(OnCapture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void OnCapture(Capture args)
|
||||
{
|
||||
if (lowLagPhotoCapture is not null)
|
||||
{
|
||||
if (await lowLagPhotoCapture.CaptureAsync() is CapturedPhoto capturedPhoto)
|
||||
{
|
||||
eventAggregator.Publish(new Captured
|
||||
{
|
||||
Photo = capturedPhoto.Photo,
|
||||
Width = capturedPhoto.Width,
|
||||
Height = capturedPhoto.Height
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public record ConfigurationChanged<TConfiguration>(TConfiguration Configuration) where TConfiguration : class;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class ConfigurationInitializer<TConfiguration> : IInitializer where TConfiguration : class, new()
|
||||
{
|
||||
private readonly TConfiguration configuration;
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
|
||||
public ConfigurationInitializer(TConfiguration configuration, IEventAggregator eventAggregator)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
this.eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
eventAggregator.Publish(configuration);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class ConfigurationWriter<TConfiguration> : IConfigurationWriter<TConfiguration> where TConfiguration : class, new()
|
||||
{
|
||||
private readonly IConfiguration rootConfiguration;
|
||||
|
||||
public ConfigurationWriter(IConfiguration rootConfiguration)
|
||||
{
|
||||
this.rootConfiguration = rootConfiguration;
|
||||
}
|
||||
|
||||
public void Write(string section, TConfiguration configuration)
|
||||
{
|
||||
if (rootConfiguration is IConfigurationRoot root)
|
||||
{
|
||||
foreach (IConfigurationProvider? provider in root.Providers)
|
||||
{
|
||||
if (provider is IWritableConfigurationProvider writableConfigurationProvider)
|
||||
{
|
||||
writableConfigurationProvider.Write(section, configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IConfigurationWriter<TConfiguration> where TConfiguration : class
|
||||
{
|
||||
void Write(string section, TConfiguration args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IWritableConfigurationProvider
|
||||
{
|
||||
void Write<TValue>(string section, TValue value) where TValue : class, new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IWritableJsonConfigurationDescriptor
|
||||
{
|
||||
Type ConfigurationType { get; }
|
||||
|
||||
string Key { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.IO;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IWritableJsonConfigurationBuilder
|
||||
{
|
||||
Stream? DefaultFileStream { get; }
|
||||
|
||||
IWritableJsonConfigurationBuilder AddDefaultConfiguration<TConfiguration>(string Key) where TConfiguration : class;
|
||||
|
||||
IWritableJsonConfigurationBuilder AddDefaultFileStream(Stream stream);
|
||||
|
||||
void Build(string path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using Json.Patch;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class WritableJsonConfigurationBuilder : IWritableJsonConfigurationBuilder
|
||||
{
|
||||
private readonly List<IWritableJsonConfigurationDescriptor> descriptors = new();
|
||||
|
||||
public Stream? DefaultFileStream { get; private set; }
|
||||
|
||||
public IReadOnlyCollection<IWritableJsonConfigurationDescriptor> Descriptors => new ReadOnlyCollection<IWritableJsonConfigurationDescriptor>(descriptors);
|
||||
|
||||
public IWritableJsonConfigurationBuilder AddDefaultConfiguration<TConfiguration>(string Key) where TConfiguration : class
|
||||
{
|
||||
descriptors.Add(new WritableJsonConfigurationDescriptor(typeof(TConfiguration), Key));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWritableJsonConfigurationBuilder AddDefaultFileStream(Stream stream)
|
||||
{
|
||||
DefaultFileStream = stream;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Build(string path)
|
||||
{
|
||||
JObject? sourceDocument = new();
|
||||
if (TryLoadSource(out string? defaultContent))
|
||||
{
|
||||
sourceDocument = JObject.Parse(defaultContent!);
|
||||
}
|
||||
|
||||
JObject? targetDocument = new();
|
||||
if (TryLoadTarget(path, out string? targetContent))
|
||||
{
|
||||
targetDocument = JObject.Parse(targetContent!);
|
||||
}
|
||||
|
||||
foreach (IWritableJsonConfigurationDescriptor? descriptor in descriptors)
|
||||
{
|
||||
if (sourceDocument.SelectToken($"$.{descriptor.Key}") is JToken sourceSection)
|
||||
{
|
||||
if (targetDocument.SelectToken($"$.{descriptor.Key}") is JToken targetSection)
|
||||
{
|
||||
object? source = JsonSerializer.Deserialize(JsonConvert.SerializeObject(sourceSection), descriptor.ConfigurationType);
|
||||
object? target = JsonSerializer.Deserialize(JsonConvert.SerializeObject(targetSection), descriptor.ConfigurationType);
|
||||
|
||||
JsonPatch? patch = source.CreatePatch(target);
|
||||
|
||||
object? sourcePatched = patch.Apply(source);
|
||||
targetSection.Replace(JToken.Parse(JsonSerializer.Serialize(sourcePatched, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })));
|
||||
}
|
||||
else
|
||||
{
|
||||
object? source = JsonSerializer.Deserialize(JsonConvert.SerializeObject(sourceSection), descriptor.ConfigurationType);
|
||||
targetDocument.Add(descriptor.Key, JToken.Parse(JsonSerializer.Serialize(source, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
object? configuration = Activator.CreateInstance(descriptor.ConfigurationType);
|
||||
targetDocument.Add(descriptor.Key, JToken.Parse(JsonSerializer.Serialize(configuration, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })));
|
||||
}
|
||||
}
|
||||
|
||||
using FileStream? fileStream = new(path, FileMode.Create, FileAccess.Write);
|
||||
using StreamWriter streamWriter = new(fileStream);
|
||||
using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented };
|
||||
targetDocument.WriteTo(writer);
|
||||
}
|
||||
|
||||
private bool TryLoadTarget(string path, [MaybeNull] out string? content)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
using FileStream? fileStream = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using StreamReader? streamReader = new(fileStream);
|
||||
content = streamReader.ReadToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
content = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryLoadSource([MaybeNull] out string? content)
|
||||
{
|
||||
if (DefaultFileStream is Stream fileStream)
|
||||
{
|
||||
using StreamReader? streamReader = new(fileStream);
|
||||
content = streamReader.ReadToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
content = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public record WritableJsonConfigurationDescriptor(Type ConfigurationType, string Key) : IWritableJsonConfigurationDescriptor;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class WritableJsonConfigurationExtensions
|
||||
{
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path)
|
||||
{
|
||||
return builder.AddWritableJsonFile(null, path, false, false, null);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
Action<IWritableJsonConfigurationBuilder>? factoryDelegate)
|
||||
{
|
||||
return builder.AddWritableJsonFile(null, path, false, false, factoryDelegate);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional)
|
||||
{
|
||||
return builder.AddWritableJsonFile(null, path, optional, false, null);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional,
|
||||
Action<IWritableJsonConfigurationBuilder>? factoryDelegate)
|
||||
{
|
||||
return builder.AddWritableJsonFile(null, path, optional, false, factoryDelegate);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional,
|
||||
bool reloadOnChange)
|
||||
{
|
||||
return builder.AddWritableJsonFile(null, path, optional, reloadOnChange, null);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional,
|
||||
bool reloadOnChange,
|
||||
Action<IWritableJsonConfigurationBuilder>? factoryDelegate)
|
||||
{
|
||||
return builder.AddWritableJsonFile(null, path, optional, reloadOnChange, factoryDelegate);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
IFileProvider? provider,
|
||||
string path,
|
||||
bool optional,
|
||||
bool reloadOnChange, Action<IWritableJsonConfigurationBuilder>? writableJsonConfigurationDelegate)
|
||||
{
|
||||
IWritableJsonConfigurationBuilder writableJsonConfigurationBuilder = new WritableJsonConfigurationBuilder();
|
||||
writableJsonConfigurationDelegate?.Invoke(writableJsonConfigurationBuilder);
|
||||
|
||||
return builder.AddWritableJsonFile(configuration =>
|
||||
{
|
||||
configuration.FileProvider = provider;
|
||||
configuration.Path = path;
|
||||
configuration.Optional = optional;
|
||||
configuration.ReloadOnChange = reloadOnChange;
|
||||
configuration.Factory = writableJsonConfigurationBuilder;
|
||||
configuration.ResolveFileProvider();
|
||||
});
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, Action<WritableJsonConfigurationSource> configureSource) => builder.Add(configureSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
internal class WritableJsonConfigurationFile
|
||||
{
|
||||
private readonly Dictionary<string, (JsonValueKind?, string?)> data = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Stack<string> paths = new();
|
||||
private JObject tokenCache = new();
|
||||
private bool isParsing;
|
||||
|
||||
public IDictionary<string, string?> Parse(Stream input)
|
||||
{
|
||||
return ParseStream(input);
|
||||
}
|
||||
|
||||
public void Write(string key, string value, Stream output)
|
||||
{
|
||||
if (isParsing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (key[^1] == ':')
|
||||
{
|
||||
key = key[0..^1];
|
||||
}
|
||||
|
||||
string? tokenPath = $"$.{key.Replace(":", ".")}";
|
||||
key = key.Replace("[", "").Replace("]", "");
|
||||
|
||||
if (tokenCache.SelectToken(tokenPath) is JToken token && data.ContainsKey(key))
|
||||
{
|
||||
(JsonValueKind? kind, string _) = data[key];
|
||||
object? newValue = ConvertValue(kind, value);
|
||||
|
||||
data[key] = new(kind, value);
|
||||
token.Replace(JToken.FromObject(newValue));
|
||||
}
|
||||
|
||||
using StreamWriter streamWriter = new(output);
|
||||
using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented };
|
||||
tokenCache.WriteTo(writer);
|
||||
|
||||
output.SetLength(output.Position);
|
||||
}
|
||||
|
||||
private static object ConvertValue(JsonValueKind? kind, string value)
|
||||
{
|
||||
return kind is JsonValueKind.True or JsonValueKind.False ? bool.Parse(value) : value;
|
||||
}
|
||||
|
||||
private void EnterContext(string context)
|
||||
{
|
||||
paths.Push(paths.Count > 0 ? paths.Peek() + ConfigurationPath.KeyDelimiter + context : context);
|
||||
}
|
||||
|
||||
private void ExitContext()
|
||||
{
|
||||
paths.Pop();
|
||||
}
|
||||
|
||||
private IDictionary<string, string?> ParseStream(Stream input)
|
||||
{
|
||||
data.Clear();
|
||||
|
||||
JsonDocumentOptions jsonDocumentOptions = new()
|
||||
{
|
||||
CommentHandling = JsonCommentHandling.Skip,
|
||||
AllowTrailingCommas = true,
|
||||
};
|
||||
|
||||
isParsing = true;
|
||||
using (StreamReader? reader = new(input))
|
||||
{
|
||||
string? content = reader.ReadToEnd();
|
||||
tokenCache = JObject.Parse(content);
|
||||
|
||||
using JsonDocument? doc = JsonDocument.Parse(content, jsonDocumentOptions);
|
||||
VisitElement(doc.RootElement);
|
||||
}
|
||||
isParsing = false;
|
||||
|
||||
return data.ToDictionary(k => k.Key, v => v.Value.Item2?.ToString() ?? null);
|
||||
}
|
||||
|
||||
private void VisitElement(JsonElement element)
|
||||
{
|
||||
bool isEmpty = true;
|
||||
|
||||
foreach (JsonProperty property in element.EnumerateObject())
|
||||
{
|
||||
isEmpty = false;
|
||||
|
||||
EnterContext(property.Name);
|
||||
VisitValue(property.Value);
|
||||
ExitContext();
|
||||
}
|
||||
|
||||
if (isEmpty && paths.Count > 0)
|
||||
{
|
||||
data[paths.Peek()] = (JsonValueKind.Null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void VisitValue(JsonElement value)
|
||||
{
|
||||
switch (value.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
VisitElement(value);
|
||||
break;
|
||||
|
||||
case JsonValueKind.Array:
|
||||
int index = 0;
|
||||
foreach (JsonElement arrayElement in value.EnumerateArray())
|
||||
{
|
||||
EnterContext(index.ToString());
|
||||
VisitValue(arrayElement);
|
||||
ExitContext();
|
||||
index++;
|
||||
}
|
||||
break;
|
||||
|
||||
case JsonValueKind.Number:
|
||||
case JsonValueKind.String:
|
||||
case JsonValueKind.True:
|
||||
case JsonValueKind.False:
|
||||
case JsonValueKind.Null:
|
||||
string key = paths.Peek();
|
||||
if (data.ContainsKey(key))
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
data[key] = new(value.ValueKind, value.ToString());
|
||||
break;
|
||||
|
||||
case JsonValueKind.Undefined:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Microsoft.Extensions.Configuration.Json;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.IO;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class WritableJsonConfigurationProvider : JsonConfigurationProvider, IWritableConfigurationProvider
|
||||
{
|
||||
public WritableJsonConfigurationProvider(JsonConfigurationSource source) : base(source)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Write<TValue>(string section, TValue value) where TValue : class, new()
|
||||
{
|
||||
IFileInfo? file = Source.FileProvider?.GetFileInfo(Source.Path ?? string.Empty);
|
||||
static Stream OpenRead(IFileInfo fileInfo)
|
||||
{
|
||||
return fileInfo.PhysicalPath is not null
|
||||
? new FileStream(fileInfo.PhysicalPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)
|
||||
: fileInfo.CreateReadStream();
|
||||
}
|
||||
|
||||
if (file is null)
|
||||
{
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
using Stream stream = OpenRead(file);
|
||||
using StreamReader? reader = new(stream);
|
||||
|
||||
string? content = reader.ReadToEnd();
|
||||
JObject? document = JObject.Parse(content);
|
||||
|
||||
if (document.SelectToken($"$.{section}") is JToken sectionToken)
|
||||
{
|
||||
sectionToken.Replace(JToken.Parse(JsonConvert.SerializeObject(value)));
|
||||
stream.SetLength(0);
|
||||
|
||||
using StreamWriter streamWriter = new(stream);
|
||||
using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented };
|
||||
document.WriteTo(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration.Json;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class WritableJsonConfigurationSource : JsonConfigurationSource
|
||||
{
|
||||
public IWritableJsonConfigurationBuilder? Factory { get; set; }
|
||||
|
||||
public override IConfigurationProvider Build(IConfigurationBuilder builder)
|
||||
{
|
||||
EnsureDefaultsWithSteam(builder);
|
||||
return new WritableJsonConfigurationProvider(this);
|
||||
}
|
||||
|
||||
private void EnsureDefaultsWithSteam(IConfigurationBuilder builder)
|
||||
{
|
||||
EnsureDefaults(builder);
|
||||
|
||||
if (FileProvider is PhysicalFileProvider physicalFileProvider)
|
||||
{
|
||||
string? outputFile = System.IO.Path.Combine(physicalFileProvider.Root, Path);
|
||||
Factory?.Build(outputFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public record Write<TConfiguration>(string Section, Action<TConfiguration> UpdateDelegate) where TConfiguration : class;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class WriteHandler<TConfiguration> : IMediatorHandler<Write<TConfiguration>> where TConfiguration : class
|
||||
{
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
private readonly TConfiguration configuration;
|
||||
private readonly IConfigurationWriter<TConfiguration> writer;
|
||||
|
||||
public WriteHandler(TConfiguration configuration,
|
||||
IConfigurationWriter<TConfiguration> writer,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
this.eventAggregator = eventAggregator;
|
||||
this.configuration = configuration;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public void Handle(Write<TConfiguration> request)
|
||||
{
|
||||
request.UpdateDelegate.Invoke(configuration);
|
||||
writer.Write(request.Section, configuration);
|
||||
|
||||
eventAggregator.Publish(new ConfigurationChanged<TConfiguration>(configuration));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonPatch.Net" Version="2.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventBuilder : IEventBuilder
|
||||
{
|
||||
private readonly List<IEventBuilderConfiguration> configurations = new();
|
||||
|
||||
public IReadOnlyCollection<IEventBuilderConfiguration> Configurations => new ReadOnlyCollection<IEventBuilderConfiguration>(configurations);
|
||||
|
||||
public IEventBuilderConfiguration<TEvent> Add<TEvent>() where TEvent : class
|
||||
{
|
||||
EventConfiguration<TEvent>? configuration = new();
|
||||
configurations.Add(configuration);
|
||||
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventConfiguration<TEvent> : IEventBuilderConfiguration<TEvent> where TEvent : class
|
||||
{
|
||||
private readonly List<IEventDescriptor> descriptors = new();
|
||||
|
||||
public EventConfiguration()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<IEventDescriptor> Descriptors => new ReadOnlyCollection<IEventDescriptor>(descriptors);
|
||||
|
||||
public Action<IServiceProvider, TEvent>? Factory { get; }
|
||||
|
||||
public Action<TEvent>? Next { get; }
|
||||
|
||||
public IEventBuilderConfiguration<TEvent> WithHandler<THandlerEvent>() where THandlerEvent : class
|
||||
{
|
||||
descriptors.Add(new EventDescriptor<TEvent, THandlerEvent>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEventBuilderConfiguration<TEvent> WithHandler<THandlerEvent>(Func<TEvent, THandlerEvent> factoryDelegate) where THandlerEvent : class
|
||||
{
|
||||
descriptors.Add(new EventDescriptor<TEvent, THandlerEvent>(factoryDelegate));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEventBuilderConfiguration<TEvent> WithHandler<THandlerEvent>(Func<IServiceProvider, TEvent, THandlerEvent> factoryDelegate) where THandlerEvent : class
|
||||
{
|
||||
descriptors.Add(new EventDescriptor<TEvent, THandlerEvent>(factoryDelegate));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public record EventDescriptor<TEvent, THandlerEvent> : IEventDescriptor
|
||||
{
|
||||
public EventDescriptor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EventDescriptor(Func<IServiceProvider, TEvent, THandlerEvent>? factoryDelegate)
|
||||
{
|
||||
FactoryDelegate = factoryDelegate;
|
||||
}
|
||||
|
||||
public EventDescriptor(Func<TEvent, THandlerEvent>? nextDelegate)
|
||||
{
|
||||
NextDelegate = nextDelegate;
|
||||
}
|
||||
|
||||
public Func<IServiceProvider, TEvent, THandlerEvent>? FactoryDelegate { get; }
|
||||
|
||||
public Func<TEvent, THandlerEvent>? NextDelegate { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventHandler<TTEvent> : IEventHandler<TTEvent> where TTEvent : class
|
||||
{
|
||||
private readonly IEventBuilderConfiguration<TTEvent> configuration;
|
||||
private readonly IDisposer disposer;
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
private readonly IMediator mediator;
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public EventHandler(IEventBuilderConfiguration<TTEvent> configuration,
|
||||
IServiceProvider serviceProvider,
|
||||
IServiceFactory serviceFactory,
|
||||
IEventAggregator eventAggregator,
|
||||
IMediator mediator,
|
||||
IDisposer disposer)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
this.serviceProvider = serviceProvider;
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.eventAggregator = eventAggregator;
|
||||
this.mediator = mediator;
|
||||
this.disposer = disposer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
disposer.Dispose(this);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
disposer.Add(this, eventAggregator.SubscribeUI<TTEvent>(OnEvent));
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void OnEvent(TTEvent args)
|
||||
{
|
||||
foreach (IEventDescriptor? descriptor in configuration.Descriptors)
|
||||
{
|
||||
mediator.Handle((descriptor as dynamic).NextDelegate?.Invoke(args) ?? (descriptor as dynamic).FactoryDelegate?.Invoke(serviceProvider, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public interface IEventBuilder
|
||||
{
|
||||
IReadOnlyCollection<IEventBuilderConfiguration> Configurations { get; }
|
||||
|
||||
IEventBuilderConfiguration<TEvent> Add<TEvent>() where TEvent : class;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IEventBuilderConfiguration
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IEventBuilderConfiguration<TEvent> : IEventBuilderConfiguration where TEvent : class
|
||||
{
|
||||
IReadOnlyCollection<IEventDescriptor> Descriptors { get; }
|
||||
|
||||
Action<IServiceProvider, TEvent>? Factory { get; }
|
||||
|
||||
Action<TEvent>? Next { get; }
|
||||
|
||||
IEventBuilderConfiguration<TEvent> WithHandler<THandlerEvent>() where THandlerEvent : class;
|
||||
|
||||
IEventBuilderConfiguration<TEvent> WithHandler<THandlerEvent>(Func<TEvent, THandlerEvent> factoryDelegate) where THandlerEvent : class;
|
||||
|
||||
IEventBuilderConfiguration<TEvent> WithHandler<THandlerEvent>(Func<IServiceProvider, TEvent, THandlerEvent> factoryDelegate) where THandlerEvent : class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IEventDescriptor
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IEventHandler<TTEvent> : IInitializer, IDisposable where TTEvent : class
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class AssemblyExtensions
|
||||
{
|
||||
public static Stream? ExtractResource(this Assembly assembly, string filename)
|
||||
{
|
||||
string? resourceName = $"{assembly.GetName()?.Name?.Replace("-", "_")}.{filename}";
|
||||
return assembly.GetManifestResourceStream(resourceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class ICollectionExtensions
|
||||
{
|
||||
public static void AddRange<T>(this ICollection<T> target, IEnumerable<T> source)
|
||||
{
|
||||
foreach (T item in source)
|
||||
{
|
||||
target.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class IDisposableExtensions
|
||||
{
|
||||
public static T Dispose<T>(this T target) where T : IDisposable
|
||||
{
|
||||
target.Dispose();
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace TheXamlGuy.Framework.Core.Extensions
|
||||
{
|
||||
public static class IDisposerExtensions
|
||||
{
|
||||
public static void Add(this IDisposer disposer, object subject, IEnumerable<IDisposable> disposers)
|
||||
{
|
||||
disposer.Add(subject, disposers.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public static class IEventAggregatorExtensions
|
||||
{
|
||||
public static IDisposable Subscribe<TEvent>(this IEventAggregator eventAggregator, Action<TEvent> onNext, IScheduler? scheduler = null, Func<TEvent, bool>? where = null)
|
||||
{
|
||||
scheduler ??= Scheduler.Default;
|
||||
where ??= x => true;
|
||||
|
||||
return eventAggregator.GetEvent<TEvent>().Where(where).ObserveOn(scheduler).WeakSubscribe(eventAggregator.Invoker, onNext);
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeUI<TEvent>(this IEventAggregator eventAggregator, Action<TEvent> onNext, Func<TEvent, bool>? where = null)
|
||||
{
|
||||
return eventAggregator.Subscribe(onNext, eventAggregator.Dispatcher, where);
|
||||
}
|
||||
|
||||
public static void Publish<TEvent>(this IEventAggregator eventAggregator) where TEvent : new()
|
||||
{
|
||||
eventAggregator.Publish(new TEvent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class IHostBuilderExtensions
|
||||
{
|
||||
public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder, string contentRoot, bool createDirectory)
|
||||
{
|
||||
if (!Directory.Exists(contentRoot) && createDirectory)
|
||||
{
|
||||
Directory.CreateDirectory(contentRoot);
|
||||
}
|
||||
|
||||
return hostBuilder.UseContentRoot(contentRoot);
|
||||
}
|
||||
|
||||
public static IHostBuilder ConfigureEvents(this IHostBuilder hostBuilder, Action<IEventBuilder> builderDelegate)
|
||||
{
|
||||
hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) =>
|
||||
{
|
||||
EventBuilder? builder = new();
|
||||
builderDelegate?.Invoke(builder);
|
||||
|
||||
foreach (IEventBuilderConfiguration? configuration in builder.Configurations)
|
||||
{
|
||||
Type? type = configuration.GetType().GetGenericArguments()[0];
|
||||
|
||||
serviceCollection.AddSingleton(typeof(IEventBuilderConfiguration<>).MakeGenericType(type), configuration);
|
||||
serviceCollection.AddSingleton(typeof(EventHandler<>).MakeGenericType(type));
|
||||
}
|
||||
});
|
||||
|
||||
return hostBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IMediatorAsyncHandler<TRequest>
|
||||
{
|
||||
Task Handle(TRequest request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public interface IMediatorAsyncHandler<TResponse, TRequest>
|
||||
{
|
||||
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class IMediatorExtensions
|
||||
{
|
||||
public static void Handle<TEvent>(this IMediator mediator) where TEvent : new()
|
||||
{
|
||||
mediator.Handle(new TEvent());
|
||||
}
|
||||
|
||||
public static TResponse? Handle<TResponse, TRequest>(this IMediator mediator, params object[] parameters) where TRequest : new()
|
||||
{
|
||||
return mediator.Handle<TResponse?>(new TRequest(), parameters);
|
||||
}
|
||||
|
||||
public static Task HandleAsync<TEvent>(this IMediator mediator) where TEvent : new()
|
||||
{
|
||||
return mediator.HandleAsync(new TEvent());
|
||||
}
|
||||
|
||||
public static Task<TResponse?> HandleAsync<TResponse, TRequest>(this IMediator mediator, params object[] parameters) where TRequest : new()
|
||||
{
|
||||
return mediator.HandleAsync<TResponse?>(new TRequest(), parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IMediatorHandler<TRequest>
|
||||
{
|
||||
void Handle(TRequest request);
|
||||
}
|
||||
|
||||
public interface IMediatorHandler<TResponse, TRequest>
|
||||
{
|
||||
TResponse Handle(TRequest request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class IObservableExtensions
|
||||
{
|
||||
public static IDisposable WeakSubscribe<TEvent>(this IObservable<TEvent> observable, IEventAggregatorInvoker invoker, Action<TEvent> onNext)
|
||||
{
|
||||
MethodInfo methodInfo = onNext.Method;
|
||||
WeakReference weakReference = new(onNext.Target);
|
||||
|
||||
IDisposable? subscription = null;
|
||||
subscription = observable.Subscribe(item =>
|
||||
{
|
||||
if (weakReference.Target is object target)
|
||||
{
|
||||
invoker.Invoke(target, item, methodInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
subscription?.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class IServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection serviceCollection, IConfiguration configuration) where TConfiguration : class, new()
|
||||
{
|
||||
serviceCollection.Configure<TConfiguration>(configuration);
|
||||
serviceCollection.AddTransient(provider => provider.GetService<IOptionsMonitor<TConfiguration>>()!.CurrentValue);
|
||||
serviceCollection.AddTransient<ConfigurationInitializer<TConfiguration>>();
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddCreatable<TService, TImplementation>(this IServiceCollection services)
|
||||
{
|
||||
return services.AddSingleton(typeof(IServiceCreator<>).MakeGenericType(typeof(TService)), typeof(ServiceCreator<,>).MakeGenericType(typeof(TService), typeof(TImplementation)));
|
||||
}
|
||||
|
||||
public static IServiceCollection AddCreatable(this IServiceCollection services, Type serviceType, Type implementationType)
|
||||
{
|
||||
return services.AddSingleton(typeof(IServiceCreator<>).MakeGenericType(serviceType), typeof(ServiceCreator<,>).MakeGenericType(serviceType, implementationType));
|
||||
}
|
||||
|
||||
public static IServiceCollection AddCreatableSingleton(this IServiceCollection services, Type serviceType, Type implementationType)
|
||||
{
|
||||
services.AddSingleton(serviceType, implementationType);
|
||||
services.AddCreatable(serviceType, implementationType);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddCreatableTransient(this IServiceCollection services, Type serviceType, Type implementationType)
|
||||
{
|
||||
services.AddTransient(serviceType, implementationType);
|
||||
services.AddCreatable(serviceType, implementationType);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddReqiredCore(this IServiceCollection services)
|
||||
{
|
||||
return services
|
||||
.AddSingleton<IServiceFactory>(provider => new ServiceFactory(provider.GetService, (type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)))
|
||||
.AddSingleton<IDisposer, Disposer>()
|
||||
.AddSingleton<IEventAggregator, EventAggregator>()
|
||||
.AddSingleton<IEventAggregatorInvoker, EventAggregatorInvoker>()
|
||||
.AddSingleton<IMediator, Mediator>()
|
||||
.AddSingleton<IScope, Scope>()
|
||||
.AddTransient<IPropertyBinderCollection, PropertyBinderCollection>()
|
||||
.AddTransient<IPropertyBuilder, PropertyBuilder>()
|
||||
.AddSingleton<IInitialization, Initialization>(provider => new Initialization(() =>
|
||||
{
|
||||
return services.Where(x => x.ServiceType.GetInterfaces()
|
||||
.Contains(typeof(IInitializer)) || x.ServiceType == typeof(IInitializer))
|
||||
.GroupBy(x => x.ServiceType)
|
||||
.Select(x => x.First())
|
||||
.SelectMany(x => provider.GetServices(x.ServiceType)
|
||||
.Select(x => (IInitializer?)x)).ToList();
|
||||
}))
|
||||
.RegisterHandlers();
|
||||
}
|
||||
|
||||
public static IServiceCollection AddWritableConfiguration<TConfiguration>(this IServiceCollection serviceCollection, IConfiguration configuration) where TConfiguration : class, new()
|
||||
{
|
||||
serviceCollection.Configure<TConfiguration>(configuration);
|
||||
serviceCollection.AddTransient<IConfigurationWriter<TConfiguration>, ConfigurationWriter<TConfiguration>>();
|
||||
serviceCollection.AddTransient(provider => provider.GetService<IOptionsMonitor<TConfiguration>>()!.CurrentValue);
|
||||
serviceCollection.AddTransient<IMediatorHandler<Write<TConfiguration>>, WriteHandler<TConfiguration>>();
|
||||
serviceCollection.AddTransient<ConfigurationInitializer<TConfiguration>>();
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
public static IServiceCollection RegisterHandlers(this IServiceCollection services, params Assembly[] assemblies)
|
||||
{
|
||||
foreach (Tuple<Type, Type> item in GetImplementations(assemblies.Append(Assembly.GetCallingAssembly()), "Handler", typeof(IMediatorHandler<>), typeof(IMediatorHandler<,>), typeof(IMediatorAsyncHandler<>), typeof(IMediatorAsyncHandler<,>)))
|
||||
{
|
||||
if (!item.Item2.GetConstructors().Any(x => x.IsPublic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
services.AddCreatableTransient(item.Item1, item.Item2);
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IEnumerable<Tuple<Type, Type>> GetImplementations(IEnumerable<Assembly> assemblies, string endsWith, params Type[] interfaces)
|
||||
{
|
||||
return assemblies.Distinct().SelectMany(a => a.GetTypes())
|
||||
.Where(impl => impl.IsClass && impl.IsPublic && !impl.IsAbstract && impl.Name.EndsWith(endsWith))
|
||||
.SelectMany(impl => impl.GetInterfaces(), (impl, iface) => new { impl, iface })
|
||||
.Where(i => i.iface.IsGenericType && interfaces.Contains(i.iface.GetGenericTypeDefinition()))
|
||||
.Select(x => Tuple.Create(x.iface, x.impl));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class IServiceFactoryExtensions
|
||||
{
|
||||
public static T? Get<T>(this IServiceFactory serviceFactory)
|
||||
{
|
||||
return serviceFactory.Get<T>(typeof(T));
|
||||
}
|
||||
|
||||
public static T Create<T>(this IServiceFactory serviceFactory, params object?[] parameters)
|
||||
{
|
||||
return serviceFactory.Create<T>(typeof(T), parameters);
|
||||
}
|
||||
|
||||
public static object? Create(this IServiceFactory serviceFactory, Type type)
|
||||
{
|
||||
ServiceFactoryDescriptor? descriptor = new(serviceFactory);
|
||||
return descriptor.Create(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static int CountSubstring(this string text, string textToMatch, StringComparison stringComparison)
|
||||
{
|
||||
int count = 0;
|
||||
int minIndex = text.IndexOf(textToMatch, 0, stringComparison);
|
||||
|
||||
while (minIndex != -1)
|
||||
{
|
||||
minIndex = text.IndexOf(textToMatch, minIndex + textToMatch.Length, stringComparison);
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class AppServices : IHostedService
|
||||
{
|
||||
private readonly IMediator mediator;
|
||||
private readonly IDisposer disposer;
|
||||
private readonly IInitialization initialization;
|
||||
|
||||
public AppServices(IMediator mediator,
|
||||
IDisposer disposer,
|
||||
IInitialization initialization)
|
||||
{
|
||||
this.mediator = mediator;
|
||||
this.disposer = disposer;
|
||||
this.initialization = initialization;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
mediator.Handle<Started>();
|
||||
await initialization.InitializeAsync();
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
disposer.Dispose(this);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class Disposer : IDisposer
|
||||
{
|
||||
private readonly ConditionalWeakTable<object, CompositeDisposable> subjects = new();
|
||||
|
||||
public void Add(object subject, params object[] objects)
|
||||
{
|
||||
CompositeDisposable disposables = subjects.GetOrCreateValue(subject);
|
||||
foreach (IDisposable disposable in objects.OfType<IDisposable>())
|
||||
{
|
||||
disposables.Add(disposable);
|
||||
}
|
||||
|
||||
foreach (object notDisposable in objects.Where(x => x is not IDisposable))
|
||||
{
|
||||
disposables.Add(Disposable.Create(() => FromNotDisposable(notDisposable)));
|
||||
}
|
||||
}
|
||||
|
||||
private void FromNotDisposable(object target)
|
||||
{
|
||||
if (target is IEnumerable enumerableTarget)
|
||||
{
|
||||
foreach (object? item in enumerableTarget)
|
||||
{
|
||||
FromNotDisposable(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (target is IDisposable disposableTarget)
|
||||
{
|
||||
disposableTarget.Dispose();
|
||||
}
|
||||
|
||||
if (target is not IDisposable)
|
||||
{
|
||||
Dispose(target);
|
||||
}
|
||||
}
|
||||
|
||||
public TDisposable Replace<TDisposable>(object subject, IDisposable disposer, TDisposable replacement) where TDisposable : IDisposable
|
||||
{
|
||||
CompositeDisposable disposables = subjects.GetOrCreateValue(subject);
|
||||
if (disposer is not null)
|
||||
{
|
||||
disposables.Remove(disposer);
|
||||
}
|
||||
|
||||
disposables.Add(replacement);
|
||||
return replacement;
|
||||
}
|
||||
|
||||
public void Remove(object subject, IDisposable disposer)
|
||||
{
|
||||
CompositeDisposable disposables = subjects.GetOrCreateValue(subject);
|
||||
if (disposer is not null)
|
||||
{
|
||||
disposables.Remove(disposer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose(object subject)
|
||||
{
|
||||
if (subjects.TryGetValue(subject, out CompositeDisposable? disposables))
|
||||
{
|
||||
disposables?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventAggregator : IEventAggregator
|
||||
{
|
||||
private readonly ConcurrentDictionary<Type, object> subjects;
|
||||
|
||||
public EventAggregator(IEventAggregatorInvoker invoker)
|
||||
{
|
||||
subjects = new ConcurrentDictionary<Type, object>();
|
||||
Dispatcher = new SynchronizationContextScheduler(SynchronizationContext.Current!);
|
||||
Invoker = invoker;
|
||||
|
||||
Current = new EventAggregatorCurrent(this);
|
||||
}
|
||||
|
||||
protected EventAggregator(EventAggregator eventAggregator)
|
||||
{
|
||||
subjects = eventAggregator.subjects;
|
||||
Dispatcher = eventAggregator.Dispatcher;
|
||||
Invoker = eventAggregator.Invoker;
|
||||
Current = eventAggregator.Current;
|
||||
}
|
||||
|
||||
public IEventAggregator Current { get; }
|
||||
|
||||
public IScheduler Dispatcher { get; }
|
||||
|
||||
public IEventAggregatorInvoker Invoker { get; }
|
||||
|
||||
public virtual IObservable<TEvent> GetEvent<TEvent>()
|
||||
{
|
||||
return GetObservable<TEvent>().Skip(1);
|
||||
}
|
||||
|
||||
public virtual ISubject<TEvent> GetSubject<TEvent>()
|
||||
{
|
||||
return (ISubject<TEvent>)subjects.GetOrAdd(typeof(TEvent), x => new BehaviorSubject<TEvent>(default!));
|
||||
}
|
||||
|
||||
public virtual void Publish<TEvent>(TEvent domainEvent)
|
||||
{
|
||||
GetSubject<TEvent>().OnNext(domainEvent);
|
||||
}
|
||||
protected virtual IObservable<TEvent> GetObservable<TEvent>()
|
||||
{
|
||||
return GetSubject<TEvent>().AsObservable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventAggregatorCurrent : EventAggregator
|
||||
{
|
||||
public EventAggregatorCurrent(EventAggregator eventAggregator) : base(eventAggregator)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override IObservable<TEvent> GetEvent<TEvent>()
|
||||
{
|
||||
return GetObservable<TEvent>().Skip(0).Where(x => x != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventAggregatorInvoker : IEventAggregatorInvoker
|
||||
{
|
||||
private readonly IScope scope;
|
||||
|
||||
public EventAggregatorInvoker(IScope scope)
|
||||
{
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public void Invoke<TEvent>(object target, TEvent item, MethodInfo methodInfo)
|
||||
{
|
||||
using (scope.Enter<TEvent>(target))
|
||||
{
|
||||
methodInfo.Invoke(target, new object[] { item! });
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInvoking<TEvent>(object target)
|
||||
{
|
||||
return scope.IsActive<TEvent>(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface ICachable
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IDisposer
|
||||
{
|
||||
void Add(object subject, params object[] objects);
|
||||
|
||||
TDisposable Replace<TDisposable>(object subject, IDisposable disposer, TDisposable replacement) where TDisposable : IDisposable;
|
||||
|
||||
void Remove(object subject, IDisposable disposer);
|
||||
|
||||
void Dispose(object subject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IEventAggregator
|
||||
{
|
||||
IEventAggregator Current { get; }
|
||||
|
||||
IScheduler Dispatcher { get; }
|
||||
|
||||
IEventAggregatorInvoker Invoker { get; }
|
||||
|
||||
IObservable<TEvent> GetEvent<TEvent>();
|
||||
|
||||
ISubject<TEvent> GetSubject<TEvent>();
|
||||
|
||||
void Publish<TEvent>(TEvent args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IEventAggregatorInvoker
|
||||
{
|
||||
void Invoke<TEvent>(object target, TEvent item, MethodInfo methodInfo);
|
||||
|
||||
bool IsInvoking<TEvent>(object target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public interface IHasSensorPlacement
|
||||
{
|
||||
SensorPlacement Placement { get; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IInitialization
|
||||
{
|
||||
Task InitializeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IInitializer
|
||||
{
|
||||
Task InitializeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IKeepAlive
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IMediator
|
||||
{
|
||||
void Handle(object request, params object[] parameters);
|
||||
|
||||
TResponse? Handle<TResponse>(object request, params object[] parameters);
|
||||
|
||||
Task HandleAsync(object request, params object[] parameters);
|
||||
|
||||
Task HandleAsync(object request, CancellationToken cancellationToken, params object[] parameters);
|
||||
|
||||
Task<TResponse?> HandleAsync<TResponse>(object request, params object[] parameters);
|
||||
|
||||
Task<TResponse?> HandleAsync<TResponse>(object request, CancellationToken cancellationToken, params object[] parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IScope
|
||||
{
|
||||
IDisposable Enter<T>(object target);
|
||||
|
||||
bool IsActive<T>(object target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IServiceCreator<I>
|
||||
{
|
||||
object Create(Func<Type, object[], object> creator, params object[] parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IServiceFactory
|
||||
{
|
||||
T? Get<T>(Type type);
|
||||
|
||||
T Create<T>(Type type, params object?[] parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public interface ITwoStateSensor
|
||||
{
|
||||
SensorState State { get; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class Initialization : IInitialization
|
||||
{
|
||||
private readonly Func<IEnumerable<IInitializer?>> factory;
|
||||
|
||||
public Initialization(Func<IEnumerable<IInitializer?>> factory)
|
||||
{
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
foreach (IInitializer? initializer in factory())
|
||||
{
|
||||
if (initializer is not null)
|
||||
{
|
||||
await initializer.InitializeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public class Mediator : IMediator
|
||||
{
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
|
||||
public Mediator(IServiceFactory serviceFactory)
|
||||
{
|
||||
this.serviceFactory = serviceFactory;
|
||||
}
|
||||
|
||||
public void Handle(object request, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorHandler<>).MakeGenericType(request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
handler.Handle((dynamic)request);
|
||||
}
|
||||
}
|
||||
|
||||
public TResponse? Handle<TResponse>(object request, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorHandler<,>).MakeGenericType(typeof(TResponse), request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
return handler.Handle((dynamic)request);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task HandleAsync(object request, CancellationToken cancellationToken, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorAsyncHandler<>).MakeGenericType(request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
return handler.Handle((dynamic)request, cancellationToken);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task HandleAsync(object request, params object[] parameters)
|
||||
{
|
||||
return HandleAsync(request, CancellationToken.None, parameters);
|
||||
}
|
||||
|
||||
public Task<TResponse?> HandleAsync<TResponse>(object request, CancellationToken cancellationToken, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorAsyncHandler<,>).MakeGenericType(typeof(TResponse), request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
return handler.Handle((dynamic)request, cancellationToken);
|
||||
}
|
||||
|
||||
return Task.FromResult<TResponse?>(default);
|
||||
}
|
||||
|
||||
public Task<TResponse?> HandleAsync<TResponse>(object request, params object[] parameters)
|
||||
{
|
||||
return HandleAsync<TResponse?>(request, CancellationToken.None, parameters);
|
||||
}
|
||||
|
||||
private dynamic? GetHandler(Type type, params object[] parameters)
|
||||
{
|
||||
return parameters.Length == 0 ? serviceFactory.Get<object>(type) : serviceFactory.Create<object>(type, parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class Scope : IScope
|
||||
{
|
||||
private readonly ConcurrentDictionary<object, bool> scopes = new ConcurrentDictionary<object, bool>();
|
||||
|
||||
public IDisposable Enter<T>(object target)
|
||||
{
|
||||
scopes.TryAdd(Tuple.Create(target, typeof(T)), true);
|
||||
return Disposable.Create(() => scopes.TryRemove(Tuple.Create(target, typeof(T)), out bool value));
|
||||
}
|
||||
|
||||
public bool IsActive<T>(object target)
|
||||
{
|
||||
return scopes.ContainsKey(Tuple.Create(target, typeof(T)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public enum SensorPlacement
|
||||
{
|
||||
Left = 0,
|
||||
Top = 1,
|
||||
Right = 3,
|
||||
Bottom = 4,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public enum SensorState
|
||||
{
|
||||
Off = 0,
|
||||
On = 1
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class ServiceCreator<I, T> : IServiceCreator<I>
|
||||
{
|
||||
public virtual object Create(Func<Type, object[], object> creator, params object[] parameters)
|
||||
{
|
||||
return creator(typeof(T), parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public class ServiceFactory : IServiceFactory
|
||||
{
|
||||
private readonly Func<Type, object?> factory;
|
||||
private readonly Func<Type, object?[], object> creator;
|
||||
|
||||
public ServiceFactory(Func<Type, object?> factory, Func<Type, object?[], object> creator)
|
||||
{
|
||||
this.factory = factory;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public T? Get<T>(Type type)
|
||||
{
|
||||
T? value = (T?)factory(type);
|
||||
return value;
|
||||
}
|
||||
|
||||
public T Create<T>(Type type, params object?[] parameters)
|
||||
{
|
||||
dynamic? lookup = factory(typeof(IServiceCreator<>).MakeGenericType(type));
|
||||
return lookup is not null ? (T)lookup.Create(creator, parameters) : (T)creator(type, parameters);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user