threading fixes
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
namespace Hyperbar.Widget.Contextual;
|
|
||||||
|
namespace Hyperbar.Widget.Contextual;
|
||||||
|
|
||||||
public class ContextualWidgetViewModel :
|
public class ContextualWidgetViewModel :
|
||||||
ObservableCollectionViewModel<IWidgetComponentViewModel>,
|
ObservableCollectionViewModel<IWidgetComponentViewModel>,
|
||||||
@@ -6,8 +7,9 @@ public class ContextualWidgetViewModel :
|
|||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
public ContextualWidgetViewModel(ITemplateFactory templateFactory,
|
public ContextualWidgetViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, items)
|
IMediator mediator,
|
||||||
|
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, mediator, items)
|
||||||
{
|
{
|
||||||
TemplateFactory = templateFactory;
|
TemplateFactory = templateFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
|
public class PrimaryWidgetConfigurationChangedHandler :
|
||||||
|
INotificationHandler<ConfigurationChanged<PrimaryWidgetConfiguration>>
|
||||||
|
{
|
||||||
|
private readonly IMediator mediator;
|
||||||
|
private readonly IEnumerable<IWidgetComponentViewModel> items;
|
||||||
|
|
||||||
|
public PrimaryWidgetConfigurationChangedHandler(IMediator mediator,
|
||||||
|
IEnumerable<IWidgetComponentViewModel> items)
|
||||||
|
{
|
||||||
|
this.mediator = mediator;
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await mediator.PublishAsync(new CollectionChanged<IEnumerable<IWidgetComponentViewModel>>(items),
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,8 @@ public class PrimaryWidgetProvider :
|
|||||||
IWidgetProvider
|
IWidgetProvider
|
||||||
{
|
{
|
||||||
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
|
||||||
services.AddConfiguration(comtext.Configuration.GetSection(nameof(PrimaryWidgetConfiguration)),
|
services.AddConfiguration< PrimaryWidgetConfiguration>(comtext.Configuration.GetSection(nameof(PrimaryWidgetConfiguration)))
|
||||||
PrimaryWidgetConfiguration.Defaults)
|
.AddHandler<WidgetComponentMapping>()
|
||||||
.AddHandler<WidgetComponentMappingHandler>()
|
.AddHandler<PrimaryWidgetConfigurationChangedHandler>()
|
||||||
.AddWidgetTemplate<PrimaryWidgetViewModel>();
|
.AddWidgetTemplate<PrimaryWidgetViewModel>();
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
namespace Hyperbar.Windows.Primary;
|
|
||||||
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
public class PrimaryWidgetViewModel :
|
public class PrimaryWidgetViewModel :
|
||||||
ObservableCollectionViewModel<IWidgetComponentViewModel>,
|
ObservableCollectionViewModel<IWidgetComponentViewModel>,
|
||||||
@@ -7,7 +8,8 @@ public class PrimaryWidgetViewModel :
|
|||||||
{
|
{
|
||||||
public PrimaryWidgetViewModel(ITemplateFactory templateFactory,
|
public PrimaryWidgetViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, items)
|
IMediator mediator,
|
||||||
|
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, mediator, items)
|
||||||
{
|
{
|
||||||
TemplateFactory = templateFactory;
|
TemplateFactory = templateFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
namespace Hyperbar.Windows.Primary;
|
namespace Hyperbar.Windows.Primary;
|
||||||
|
|
||||||
public class WidgetComponentMappingHandler(PrimaryWidgetConfiguration configuration,
|
public class WidgetComponentMapping(PrimaryWidgetConfiguration configuration,
|
||||||
IServiceFactory service,
|
IServiceFactory service,
|
||||||
IMediator mediator) :
|
IMediator mediator) :
|
||||||
IMappingHandler<PrimaryWidgetConfiguration, IEnumerable<IWidgetComponentViewModel>>
|
IMappingHandler<PrimaryWidgetConfiguration, IEnumerable<IWidgetComponentViewModel>>
|
||||||
{
|
{
|
||||||
public IEnumerable<IWidgetComponentViewModel> Map()
|
public IEnumerable<IWidgetComponentViewModel> Handle()
|
||||||
{
|
{
|
||||||
foreach (IPrimaryCommandConfiguration item in configuration)
|
foreach (IPrimaryCommandConfiguration item in configuration)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Hyperbar.Windows.Primary;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
namespace Hyperbar.Windows;
|
namespace Hyperbar.Windows;
|
||||||
@@ -17,6 +18,10 @@ public partial class App :
|
|||||||
{
|
{
|
||||||
base.OnLaunched(args);
|
base.OnLaunched(args);
|
||||||
|
|
||||||
|
var context = new DispatcherQueueSynchronizationContext(
|
||||||
|
DispatcherQueue.GetForCurrentThread());
|
||||||
|
SynchronizationContext.SetSynchronizationContext(context);
|
||||||
|
|
||||||
IHost? host = Host.CreateDefaultBuilder()
|
IHost? host = Host.CreateDefaultBuilder()
|
||||||
.UseContentRoot(AppContext.BaseDirectory)
|
.UseContentRoot(AppContext.BaseDirectory)
|
||||||
.ConfigureAppConfiguration(config =>
|
.ConfigureAppConfiguration(config =>
|
||||||
@@ -31,10 +36,11 @@ public partial class App :
|
|||||||
services.AddSingleton<IServiceFactory>(provider =>
|
services.AddSingleton<IServiceFactory>(provider =>
|
||||||
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
|
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
|
||||||
|
|
||||||
|
services.AddSingleton<IMediator, Mediator>();
|
||||||
services.AddHostedService<AppService>();
|
services.AddHostedService<AppService>();
|
||||||
|
|
||||||
services.AddTransient<IInitializer, AppInitializer>();
|
services.AddTransient<IInitializer, AppInitializer>();
|
||||||
services.AddTransient<ITemplateFactory, TemplateFactory>();
|
services.AddTransient<ITemplateFactory, TemplateFactory>();
|
||||||
services.AddTransient<ITemplateGeneratorFactory, TemplateGeneratorFactory>();
|
|
||||||
|
|
||||||
services.AddTransient<DesktopFlyout>();
|
services.AddTransient<DesktopFlyout>();
|
||||||
services.AddContentTemplate<CommandViewModel, CommandView>();
|
services.AddContentTemplate<CommandViewModel, CommandView>();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
|
||||||
namespace Hyperbar.Windows
|
namespace Hyperbar.Windows
|
||||||
{
|
{
|
||||||
@@ -11,6 +12,9 @@ namespace Hyperbar.Windows
|
|||||||
where TWidgetProvider :
|
where TWidgetProvider :
|
||||||
IWidgetProvider, new()
|
IWidgetProvider, new()
|
||||||
{
|
{
|
||||||
|
DispatcherQueueSynchronizationContext context = new(DispatcherQueue.GetForCurrentThread());
|
||||||
|
SynchronizationContext.SetSynchronizationContext(context);
|
||||||
|
|
||||||
TWidgetProvider builder = new();
|
TWidgetProvider builder = new();
|
||||||
IHost? host = new HostBuilder()
|
IHost? host = new HostBuilder()
|
||||||
.UseContentRoot(AppContext.BaseDirectory)
|
.UseContentRoot(AppContext.BaseDirectory)
|
||||||
@@ -37,7 +41,6 @@ namespace Hyperbar.Windows
|
|||||||
isolatedServices.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>();
|
isolatedServices.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>();
|
||||||
|
|
||||||
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
|
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
|
||||||
isolatedServices.AddTransient<ITemplateGeneratorFactory, TemplateGeneratorFactory>();
|
|
||||||
|
|
||||||
builder.Create(context, isolatedServices);
|
builder.Create(context, isolatedServices);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Hyperbar.Windows;
|
||||||
|
|
||||||
|
public class DataTemplateConverter : ValueConverter<object, DataTemplateSelector>
|
||||||
|
{
|
||||||
|
protected override DataTemplateSelector? ConvertTo(object value, Type? targetType, object? parameter, string? language)
|
||||||
|
{
|
||||||
|
return new TemplateGenerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Microsoft.UI.Xaml;
|
|
||||||
|
|
||||||
namespace Hyperbar.Windows
|
|
||||||
{
|
|
||||||
public interface ITemplateGeneratorFactory
|
|
||||||
{
|
|
||||||
DataTemplate Create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,11 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
|
|
||||||
namespace Hyperbar.Windows;
|
namespace Hyperbar.Windows;
|
||||||
|
|
||||||
public class TemplateFactory(ITemplateGeneratorFactory factory,
|
public class TemplateFactory(IEnumerable<IContentTemplateDescriptor> descriptors,
|
||||||
IEnumerable<IContentTemplateDescriptor> descriptors,
|
|
||||||
IServiceProvider provider) :
|
IServiceProvider provider) :
|
||||||
DataTemplateSelector,
|
|
||||||
ITemplateFactory
|
ITemplateFactory
|
||||||
{
|
{
|
||||||
protected override DataTemplate SelectTemplateCore(object item) => factory.Create();
|
|
||||||
|
|
||||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) => factory.Create();
|
|
||||||
|
|
||||||
public object? Create(object key)
|
public object? Create(object key)
|
||||||
{
|
{
|
||||||
if (descriptors.FirstOrDefault(x => x.Key == key) is IContentTemplateDescriptor descriptor)
|
if (descriptors.FirstOrDefault(x => x.Key == key) is IContentTemplateDescriptor descriptor)
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Markup;
|
||||||
|
|
||||||
|
namespace Hyperbar.Windows;
|
||||||
|
|
||||||
|
public class TemplateGenerator : DataTemplateSelector
|
||||||
|
{
|
||||||
|
protected override DataTemplate SelectTemplateCore(object item)
|
||||||
|
{
|
||||||
|
string xamlString = @"
|
||||||
|
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
|
||||||
|
xmlns:desktop='using:Hyperbar.Windows'>
|
||||||
|
<desktop:TemplateGeneratorControl />
|
||||||
|
</DataTemplate>";
|
||||||
|
|
||||||
|
return (DataTemplate)XamlReader.Load(xamlString);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||||
|
{
|
||||||
|
string xamlString = @"
|
||||||
|
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
|
||||||
|
xmlns:desktop='using:Hyperbar.Windows'>
|
||||||
|
<desktop:TemplateGeneratorControl />
|
||||||
|
</DataTemplate>";
|
||||||
|
|
||||||
|
return (DataTemplate)XamlReader.Load(xamlString);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Markup;
|
|
||||||
|
|
||||||
namespace Hyperbar.Windows;
|
|
||||||
|
|
||||||
public class TemplateGeneratorFactory :
|
|
||||||
ITemplateGeneratorFactory
|
|
||||||
{
|
|
||||||
public DataTemplate Create()
|
|
||||||
{
|
|
||||||
string xamlString = @"
|
|
||||||
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
|
|
||||||
xmlns:desktop='using:Hyperbar.Windows'>
|
|
||||||
<desktop:TemplateGeneratorControl />
|
|
||||||
</DataTemplate>";
|
|
||||||
|
|
||||||
return (DataTemplate)XamlReader.Load(xamlString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Microsoft.UI.Xaml.Markup;
|
||||||
|
|
||||||
|
namespace Hyperbar.Windows;
|
||||||
|
|
||||||
|
public abstract class ValueConverter<TSource, TTarget> :
|
||||||
|
MarkupExtension,
|
||||||
|
IValueConverter
|
||||||
|
{
|
||||||
|
protected override object ProvideValue(IXamlServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
return ConvertTo((TSource)value, targetType, parameter, language);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
return ConvertBackTo((TTarget)value, targetType, parameter, language);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TTarget? Convert(TSource value)
|
||||||
|
{
|
||||||
|
return ConvertTo(value, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TSource? ConvertBack(TTarget value)
|
||||||
|
{
|
||||||
|
return ConvertBackTo(value, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual TTarget? ConvertTo(TSource value, Type? targetType, object? parameter, string? language)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual TSource? ConvertBackTo(TTarget value, Type? targetType, object? parameter, string? language)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,9 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Hyperbar.Windows.CommandView"
|
x:Class="Hyperbar.Windows.CommandView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
<ItemsControl ItemTemplateSelector="{Binding TemplateFactory}" ItemsSource="{Binding}">
|
xmlns:windows="using:Hyperbar.Windows">
|
||||||
|
<ItemsControl ItemTemplateSelector="{Binding Mode=TwoWay, Converter={windows:DataTemplateConverter}}" ItemsSource="{Binding}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<StackPanel Orientation="Horizontal" />
|
<StackPanel Orientation="Horizontal" />
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ public partial class CommandViewModel :
|
|||||||
ITemplatedViewModel
|
ITemplatedViewModel
|
||||||
{
|
{
|
||||||
public CommandViewModel(ITemplateFactory templateFactory,
|
public CommandViewModel(ITemplateFactory templateFactory,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
IEnumerable<IWidgetViewModel> items) : base(serviceFactory, items)
|
IMediator mediator,
|
||||||
|
IEnumerable<IWidgetViewModel> items) : base(serviceFactory, mediator, items)
|
||||||
{
|
{
|
||||||
TemplateFactory = templateFactory;
|
TemplateFactory = templateFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Hyperbar.Windows.WidgetView"
|
x:Class="Hyperbar.Windows.WidgetView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
<ItemsControl ItemTemplateSelector="{Binding TemplateFactory}" ItemsSource="{Binding}">
|
xmlns:windows="using:Hyperbar.Windows">
|
||||||
|
<ItemsControl ItemTemplateSelector="{Binding Mode=TwoWay, Converter={windows:DataTemplateConverter}}" ItemsSource="{Binding}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8" />
|
<StackPanel Orientation="Horizontal" Spacing="8" />
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
namespace Hyperbar;
|
||||||
|
|
||||||
|
public record ConfigurationChanged<TConfiguration>(TConfiguration Configuration) : INotification
|
||||||
|
where TConfiguration :
|
||||||
|
class;
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Hyperbar;
|
namespace Hyperbar;
|
||||||
|
|
||||||
public record ConfigurationChanged<TConfiguration>(TConfiguration Configuration) : INotification
|
|
||||||
where TConfiguration :
|
|
||||||
class;
|
|
||||||
|
|
||||||
public class ConfigurationInitializer<TConfiguration>(DefaultConfiguration<TConfiguration> defaults,
|
public class ConfigurationInitializer<TConfiguration>(DefaultConfiguration<TConfiguration> defaults,
|
||||||
IConfigurationWriter<TConfiguration> writer,
|
IConfigurationWriter<TConfiguration> writer,
|
||||||
IOptionsMonitor<TConfiguration> options,
|
IOptionsMonitor<TConfiguration> options,
|
||||||
@@ -17,12 +11,16 @@ public class ConfigurationInitializer<TConfiguration>(DefaultConfiguration<TConf
|
|||||||
{
|
{
|
||||||
public Task InitializeAsync()
|
public Task InitializeAsync()
|
||||||
{
|
{
|
||||||
options.OnChange(args =>
|
options.OnChange(async args =>
|
||||||
{
|
{
|
||||||
mediator.PublishAsync(new ConfigurationChanged<TConfiguration>(args));
|
await mediator.PublishAsync(new ConfigurationChanged<TConfiguration>(args));
|
||||||
});
|
});
|
||||||
|
|
||||||
writer.Write(defaults.Configuration);
|
if (defaults.Configuration is not null)
|
||||||
|
{
|
||||||
|
writer.Write(defaults.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
namespace Hyperbar;
|
namespace Hyperbar;
|
||||||
|
|
||||||
public class DefaultConfiguration<TConfiguration>(TConfiguration configuration)
|
public class DefaultConfiguration<TConfiguration>(TConfiguration? configuration = null)
|
||||||
|
where TConfiguration :
|
||||||
|
class
|
||||||
{
|
{
|
||||||
public TConfiguration Configuration => configuration;
|
public TConfiguration? Configuration => configuration;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public static class IServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
Type notificationType = arguments[0];
|
Type notificationType = arguments[0];
|
||||||
|
|
||||||
services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), ServiceLifetime.Singleton));
|
services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
|
||||||
services.Add(new ServiceDescriptor(typeof(INotificationHandler<>).MakeGenericType(notificationType),
|
services.Add(new ServiceDescriptor(typeof(INotificationHandler<>).MakeGenericType(notificationType),
|
||||||
provider => provider.GetRequiredService<THandler>(), lifetime));
|
provider => provider.GetRequiredService<THandler>(), lifetime));
|
||||||
}
|
}
|
||||||
@@ -53,13 +53,24 @@ public static class IServiceCollectionExtensions
|
|||||||
Type responseType = arguments[1];
|
Type responseType = arguments[1];
|
||||||
|
|
||||||
services.AddTransient(typeof(THandler));
|
services.AddTransient(typeof(THandler));
|
||||||
services.AddTransient(responseType, provider => ((dynamic)provider.GetRequiredService<THandler>()).Map());
|
services.AddTransient(responseType, provider =>
|
||||||
|
{
|
||||||
|
return ((dynamic)provider.GetRequiredService<THandler>()).Handle();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services,
|
||||||
|
IConfiguration configuration)
|
||||||
|
where TConfiguration :
|
||||||
|
class, new()
|
||||||
|
{
|
||||||
|
return services.AddConfiguration<TConfiguration>(configuration, typeof(TConfiguration).Name, "Settings.json", null);
|
||||||
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services,
|
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
TConfiguration? defaults = null)
|
TConfiguration? defaults = null)
|
||||||
@@ -107,10 +118,11 @@ public static class IServiceCollectionExtensions
|
|||||||
|
|
||||||
if (defaults is not null)
|
if (defaults is not null)
|
||||||
{
|
{
|
||||||
services.AddTransient(provider => new DefaultConfiguration<TConfiguration>(defaults));
|
|
||||||
services.AddTransient<IInitializer, ConfigurationInitializer<TConfiguration>>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
services.AddTransient(provider => new DefaultConfiguration<TConfiguration>(defaults));
|
||||||
|
services.AddTransient<IInitializer, ConfigurationInitializer<TConfiguration>>();
|
||||||
|
|
||||||
services.AddTransient<IWritableConfiguration<TConfiguration>, WritableConfiguration<TConfiguration>>();
|
services.AddTransient<IWritableConfiguration<TConfiguration>, WritableConfiguration<TConfiguration>>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
public interface IMappingHandler<TFrom, TTo> : IHandler
|
public interface IMappingHandler<TFrom, TTo> : IHandler
|
||||||
{
|
{
|
||||||
TTo Map();
|
TTo Handle();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Hyperbar;
|
namespace Hyperbar;
|
||||||
|
|
||||||
public interface INotificationHandler<in TNotification>
|
public interface INotificationHandler<in TNotification> :
|
||||||
|
IHandler
|
||||||
where TNotification :
|
where TNotification :
|
||||||
INotification
|
INotification
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ namespace Hyperbar;
|
|||||||
public class Mediator(IServiceProvider provider) :
|
public class Mediator(IServiceProvider provider) :
|
||||||
IMediator
|
IMediator
|
||||||
{
|
{
|
||||||
private readonly ConditionalWeakTable<Type, dynamic> handlers = [];
|
private readonly ConditionalWeakTable<Type, dynamic> addedHandlers = [];
|
||||||
|
|
||||||
public ValueTask PublishAsync<TNotification>(TNotification notification,
|
public async ValueTask PublishAsync<TNotification>(TNotification notification,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
where TNotification :
|
where TNotification :
|
||||||
INotification
|
INotification
|
||||||
@@ -16,7 +16,7 @@ public class Mediator(IServiceProvider provider) :
|
|||||||
List<INotificationHandler<TNotification>> handlers =
|
List<INotificationHandler<TNotification>> handlers =
|
||||||
provider.GetServices<INotificationHandler<TNotification>>().ToList();
|
provider.GetServices<INotificationHandler<TNotification>>().ToList();
|
||||||
|
|
||||||
foreach (KeyValuePair<Type, dynamic> handler in this.handlers)
|
foreach (KeyValuePair<Type, dynamic> handler in addedHandlers)
|
||||||
{
|
{
|
||||||
if (handler.Key == typeof(TNotification))
|
if (handler.Key == typeof(TNotification))
|
||||||
{
|
{
|
||||||
@@ -26,14 +26,12 @@ public class Mediator(IServiceProvider provider) :
|
|||||||
|
|
||||||
if (handlers.Count == 0)
|
if (handlers.Count == 0)
|
||||||
{
|
{
|
||||||
return default;
|
|
||||||
}
|
}
|
||||||
else if (handlers.Count == 1)
|
else if (handlers.Count == 1)
|
||||||
{
|
{
|
||||||
return handlers[0].Handle(notification, cancellationToken);
|
await handlers[0].Handle(notification, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> request,
|
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> request,
|
||||||
@@ -82,7 +80,7 @@ public class Mediator(IServiceProvider provider) :
|
|||||||
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
|
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
|
||||||
{
|
{
|
||||||
Type notificationType = arguments[0];
|
Type notificationType = arguments[0];
|
||||||
handlers.Add(notificationType, subject);
|
addedHandlers.Add(notificationType, subject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Hyperbar;
|
||||||
|
|
||||||
|
public record CollectionChanged<TCollection>(TCollection Items) : INotification where TCollection : IEnumerable;
|
||||||
@@ -3,19 +3,29 @@
|
|||||||
namespace Hyperbar;
|
namespace Hyperbar;
|
||||||
|
|
||||||
public class ObservableCollectionViewModel<TItem> :
|
public class ObservableCollectionViewModel<TItem> :
|
||||||
ObservableCollection<TItem>
|
ObservableCollection<TItem>,
|
||||||
|
INotificationHandler<CollectionChanged<IEnumerable<TItem>>>
|
||||||
{
|
{
|
||||||
private readonly IServiceFactory serviceFactory;
|
private readonly IServiceFactory serviceFactory;
|
||||||
|
private SynchronizationContext? context;
|
||||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory)
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||||
|
IMediator mediator)
|
||||||
{
|
{
|
||||||
|
context = SynchronizationContext.Current;
|
||||||
|
|
||||||
this.serviceFactory = serviceFactory;
|
this.serviceFactory = serviceFactory;
|
||||||
|
mediator.Subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
public ObservableCollectionViewModel(IServiceFactory serviceFactory,
|
||||||
|
IMediator mediator,
|
||||||
IEnumerable<TItem> items)
|
IEnumerable<TItem> items)
|
||||||
{
|
{
|
||||||
|
context = SynchronizationContext.Current;
|
||||||
|
|
||||||
this.serviceFactory = serviceFactory;
|
this.serviceFactory = serviceFactory;
|
||||||
|
mediator.Subscribe(this);
|
||||||
|
|
||||||
AddRange(items);
|
AddRange(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +41,7 @@ public class ObservableCollectionViewModel<TItem> :
|
|||||||
where T : TItem
|
where T : TItem
|
||||||
{
|
{
|
||||||
T? item = serviceFactory.Create<T>(parameters);
|
T? item = serviceFactory.Create<T>(parameters);
|
||||||
Add(item);
|
context?.Post(state => Add(item), null);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -41,7 +51,7 @@ public class ObservableCollectionViewModel<TItem> :
|
|||||||
TItem
|
TItem
|
||||||
{
|
{
|
||||||
T? item = serviceFactory.Create<T>();
|
T? item = serviceFactory.Create<T>();
|
||||||
Add(item);
|
context?.Post(state => Add(item), null);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -50,12 +60,19 @@ public class ObservableCollectionViewModel<TItem> :
|
|||||||
{
|
{
|
||||||
foreach (TItem? item in items)
|
foreach (TItem? item in items)
|
||||||
{
|
{
|
||||||
Add(item);
|
context?.Post(state => Add(item), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValueTask Handle(CollectionChanged<IEnumerable<TItem>> notification,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
context?.Post(state => Clear(), null);
|
||||||
|
AddRange(notification.Items);
|
||||||
|
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ObservableCollectionViewModel(IServiceFactory serviceFactory) :
|
public class ObservableCollectionViewModel(IServiceFactory serviceFactory, IMediator mediator) :
|
||||||
ObservableCollectionViewModel<object>(serviceFactory)
|
ObservableCollectionViewModel<object>(serviceFactory, mediator);
|
||||||
{
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user