Use Generic Host to enable DI for BlinForms apps
- Wrap BlinForms startup logic in an IHostedService - Update BlinForms TodoApp sample to use DI to store the counter state
This commit is contained in:
Коммит
9f6430bd77
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Blaxamarin.Framework
|
||||
|
@ -11,7 +12,7 @@ namespace Blaxamarin.Framework
|
|||
var serviceCollection = new ServiceCollection();
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var renderer = new BlaxamarinRenderer(serviceProvider);
|
||||
var renderer = new BlaxamarinRenderer(serviceProvider, new LoggerFactory());
|
||||
var result = renderer.Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
await renderer.AddComponent<T>();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Emblazon;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Xamarin.Forms;
|
||||
|
@ -7,8 +8,8 @@ namespace Blaxamarin.Framework
|
|||
{
|
||||
public class BlaxamarinRenderer : EmblazonRenderer<IFormsControlHandler>
|
||||
{
|
||||
public BlaxamarinRenderer(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
public BlaxamarinRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
|
||||
: base(serviceProvider, loggerFactory)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0-preview9.19423.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public static class BlinForms
|
||||
{
|
||||
public static void Run<T>() where T : IComponent
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var renderer = new BlinFormsRenderer(serviceProvider);
|
||||
renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
renderer.AddComponent<T>();
|
||||
Application.Run(renderer.RootForm);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public static class BlinFormsHostBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers <see cref="BlinFormsHostedService"/> in the DI container. Call this as part of configuring the
|
||||
/// host to enable BlinForms.
|
||||
/// </summary>
|
||||
/// <param name="hostBuilder"></param>
|
||||
/// <returns></returns>
|
||||
public static IHostBuilder AddBlinForms(this IHostBuilder hostBuilder)
|
||||
{
|
||||
if (hostBuilder is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostBuilder));
|
||||
}
|
||||
|
||||
hostBuilder.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
services.AddHostedService<BlinFormsHostedService>();
|
||||
});
|
||||
|
||||
return hostBuilder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="IHostedService"/> that controls the lifetime of a BlinForms application.
|
||||
/// When this service starts, it loads the main form registered by
|
||||
/// <see cref="BlinFormsServiceCollectionExtensions.AddBlinFormsMainForm{TComponent}(Microsoft.Extensions.DependencyInjection.IServiceCollection)"/>.
|
||||
/// The service will request that the application stops when the main form is closed.
|
||||
/// </summary>
|
||||
public class BlinFormsHostedService : IHostedService
|
||||
{
|
||||
private readonly IBlinFormsMainFormType _blinFormsMainForm;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||
|
||||
public BlinFormsHostedService(IBlinFormsMainFormType blinFormsMainForm, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IHostApplicationLifetime hostApplicationLifetime)
|
||||
{
|
||||
_blinFormsMainForm = blinFormsMainForm;
|
||||
_loggerFactory = loggerFactory;
|
||||
_serviceProvider = serviceProvider;
|
||||
_hostApplicationLifetime = hostApplicationLifetime;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
var renderer = new BlinFormsRenderer(_serviceProvider, _loggerFactory);
|
||||
await renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
renderer.AddComponent(_blinFormsMainForm.MainFormType);
|
||||
var rootForm = renderer.RootForm;
|
||||
rootForm.FormClosed += OnRootFormFormClosed;
|
||||
Application.Run(rootForm);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnRootFormFormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
// When the main form closes, request for the application to stop
|
||||
_hostApplicationLifetime.StopApplication();
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public class BlinFormsMainFormType<TComponent> : IBlinFormsMainFormType
|
||||
where TComponent : IComponent
|
||||
{
|
||||
public BlinFormsMainFormType()
|
||||
{
|
||||
MainFormType = typeof(TComponent);
|
||||
}
|
||||
|
||||
public Type MainFormType { get; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using Emblazon;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
|
@ -6,8 +7,8 @@ namespace BlinForms.Framework
|
|||
{
|
||||
public class BlinFormsRenderer : EmblazonRenderer<IWindowsFormsControlHandler>
|
||||
{
|
||||
public BlinFormsRenderer(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
public BlinFormsRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
|
||||
: base(serviceProvider, loggerFactory)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public static class BlinFormsServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers a BlinForms component (typically from a .razor file) as the initial form to load when the
|
||||
/// application start.
|
||||
/// </summary>
|
||||
/// <typeparam name="TComponent"></typeparam>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddBlinFormsMainForm<TComponent>(this IServiceCollection services)
|
||||
where TComponent : class, IComponent
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
services.AddSingleton<IBlinFormsMainFormType, BlinFormsMainFormType<TComponent>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public interface IBlinFormsMainFormType
|
||||
{
|
||||
Type MainFormType { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BlinFormsSample
|
||||
{
|
||||
public class AppState
|
||||
{
|
||||
public List<TodoItem> items = new List<TodoItem>();
|
||||
|
||||
public int Counter { get; set; }
|
||||
|
||||
public void ResetAppState()
|
||||
{
|
||||
items.AddRange(
|
||||
new[]
|
||||
{
|
||||
new TodoItem { Text = "sell dog", IsDone = true },
|
||||
new TodoItem { Text = "buy cat" },
|
||||
new TodoItem { Text = "buy cat food" },
|
||||
});
|
||||
|
||||
Counter = 0;
|
||||
}
|
||||
|
||||
public bool IsEmptyAppState()
|
||||
{
|
||||
return
|
||||
!items.Any() &&
|
||||
Counter == 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
<Panel Width="200" Height="100" BackColor="SystemColors.Info">
|
||||
@inject AppState AppState
|
||||
|
||||
<Panel Width="200" Height="100" BackColor="SystemColors.Info">
|
||||
<Button Width="40" Text="+1" OnClick="@HandleClick" />
|
||||
<Label Left="50" Width="200" Text="@("You have pressed " + clickCount + " times")" />
|
||||
<Label Left="50" Width="200" Text="@("You have pressed " + AppState.Counter + " times")" />
|
||||
</Panel>
|
||||
|
||||
@code {
|
||||
int clickCount = 0;
|
||||
|
||||
void HandleClick()
|
||||
{
|
||||
clickCount++;
|
||||
AppState.Counter++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
using System;
|
||||
using BlinForms.Framework;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlinFormsSample
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static void Main()
|
||||
static async Task Main()
|
||||
{
|
||||
BlinForms.Framework.BlinForms.Run<TodoApp>();
|
||||
await Host.CreateDefaultBuilder()
|
||||
.AddBlinForms()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
// Register app-specific services
|
||||
services.AddSingleton<AppState>();
|
||||
|
||||
// Configure main form to load at startup
|
||||
services.AddBlinFormsMainForm<TodoApp>();
|
||||
})
|
||||
.Build()
|
||||
.RunAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ namespace Emblazon
|
|||
private readonly Dictionary<ulong, Action> _eventRegistrations = new Dictionary<ulong, Action>();
|
||||
|
||||
|
||||
public EmblazonRenderer(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider, new LoggerFactory())
|
||||
public EmblazonRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
|
||||
: base(serviceProvider, loggerFactory)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,14 @@ namespace Emblazon
|
|||
public override Dispatcher Dispatcher { get; }
|
||||
= Dispatcher.CreateDefault();
|
||||
|
||||
public Task AddComponent<T>() where T : IComponent
|
||||
public async Task AddComponent<TComponent>() where TComponent : IComponent
|
||||
{
|
||||
var component = InstantiateComponent(typeof(T));
|
||||
await AddComponent(typeof(TComponent));
|
||||
}
|
||||
|
||||
public async Task AddComponent(Type componentType)
|
||||
{
|
||||
var component = InstantiateComponent(componentType);
|
||||
var componentId = AssignRootComponentId(component);
|
||||
var rootControl = CreateRootControl();
|
||||
|
||||
|
@ -39,7 +44,7 @@ namespace Emblazon
|
|||
};
|
||||
|
||||
_componentIdToAdapter[componentId] = rootAdapter;
|
||||
return RenderRootComponentAsync(componentId);
|
||||
await RenderRootComponentAsync(componentId);
|
||||
}
|
||||
|
||||
protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
|
||||
|
|
Загрузка…
Ссылка в новой задаче