Change how root elements are created and how app content is parented
- Renderers no longer create the root control. All they do it *add* a given component to a given parent. The platform-specific code is in charge of creating the root component. - This now avoids creating unnecessary wrappers (e.g. BlazorContentViewWrapper) that muddy the native control hierarchy
This commit is contained in:
Родитель
fe3413a55f
Коммит
2c6cd47653
|
@ -23,13 +23,6 @@ namespace Blaxamarin.Framework
|
|||
//MessageBox.Show(exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
protected override IFormsControlHandler CreateRootControl()
|
||||
{
|
||||
var rootContent = new BlazorContentViewWrapper();
|
||||
ContentPage.Content = (View)rootContent.Element;
|
||||
return rootContent;
|
||||
}
|
||||
|
||||
protected override NativeControlManager<IFormsControlHandler> CreateNativeControlManager()
|
||||
{
|
||||
return new BlaxamarinNativeControlManager();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Blaxamarin.Framework.Elements;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
|
@ -8,16 +9,40 @@ namespace Blaxamarin.Framework
|
|||
{
|
||||
public static class BlaxamarinServiceProviderExtensions
|
||||
{
|
||||
public static ContentPage GetComponentContentPage<TComponent>(this IServiceProvider services) where TComponent : IComponent
|
||||
/// <summary>
|
||||
/// Creates a component of type <typeparamref name="TComponent"/> and adds it as a child of <paramref name="parent"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TComponent"></typeparam>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="parent"></param>
|
||||
public static void AddComponent<TComponent>(this IServiceProvider services, Element parent) where TComponent : IComponent
|
||||
{
|
||||
var renderer = new BlaxamarinRenderer(services, services.GetRequiredService<ILoggerFactory>());
|
||||
var result = renderer.Dispatcher.InvokeAsync(async () =>
|
||||
if (parent is null)
|
||||
{
|
||||
await renderer.AddComponent<TComponent>();
|
||||
throw new ArgumentNullException(nameof(parent));
|
||||
}
|
||||
|
||||
return renderer.ContentPage;
|
||||
var renderer = new BlaxamarinRenderer(services, services.GetRequiredService<ILoggerFactory>());
|
||||
renderer.Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
await renderer.AddComponent<TComponent>(new ElementWrapper(parent));
|
||||
});
|
||||
return result.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private sealed class ElementWrapper : IFormsControlHandler
|
||||
{
|
||||
public ElementWrapper(Element element)
|
||||
{
|
||||
Element = element ?? throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
public Element Element { get; }
|
||||
public object NativeControl => Element;
|
||||
|
||||
public void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
|
||||
{
|
||||
FormsComponentBase.ApplyAttribute(Element, attributeEventHandlerId, attributeName, attributeValue, attributeEventUpdatesAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
using Xamarin.Forms;
|
||||
|
||||
namespace Blaxamarin.Framework
|
||||
{
|
||||
internal class BlazorContentViewWrapper : ContentView, IFormsControlHandler
|
||||
{
|
||||
public object NativeControl => this;
|
||||
public Element Element => this;
|
||||
|
||||
// TODO: Need to think about whether this method is needed. There's no component for this element, so when
|
||||
// would this get called?
|
||||
public void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
|
||||
{
|
||||
Elements.FormsComponentBase.ApplyAttribute(this, attributeEventHandlerId, attributeName, attributeValue, attributeEventUpdatesAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ namespace BlaxamarinSample
|
|||
})
|
||||
.Build();
|
||||
|
||||
MainPage = host.Services.GetComponentContentPage<TodoApp>();
|
||||
host.Services.AddComponent<TodoApp>(parent: this);
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using BlinForms.Framework.Controls;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -12,21 +13,21 @@ 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)"/>.
|
||||
/// <see cref="BlinFormsServiceCollectionExtensions.AddRootFormContent{TComponent}(Microsoft.Extensions.DependencyInjection.IServiceCollection)"/>.
|
||||
/// The service will request that the application stops when the main form is closed.
|
||||
/// This service will invoke all instances of <see cref="IBlinFormsStartup"/> that are registered in the
|
||||
/// container. The order of the startup instances is not guaranteed.
|
||||
/// </summary>
|
||||
public class BlinFormsHostedService : IHostedService
|
||||
{
|
||||
private readonly IBlinFormsMainFormType _blinFormsMainForm;
|
||||
private readonly IBlinFormsRootFormContent _blinFormsMainForm;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||
private readonly IEnumerable<IBlinFormsStartup> _blinFormsStartups;
|
||||
|
||||
public BlinFormsHostedService(
|
||||
IBlinFormsMainFormType blinFormsMainForm,
|
||||
IBlinFormsRootFormContent blinFormsMainForm,
|
||||
ILoggerFactory loggerFactory,
|
||||
IServiceProvider serviceProvider,
|
||||
IHostApplicationLifetime hostApplicationLifetime,
|
||||
|
@ -50,11 +51,13 @@ namespace BlinForms.Framework
|
|||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
var renderer = new BlinFormsRenderer(_serviceProvider, _loggerFactory);
|
||||
await renderer.Dispatcher.InvokeAsync(() =>
|
||||
await renderer.Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
renderer.AddComponent(_blinFormsMainForm.MainFormType);
|
||||
var rootForm = renderer.RootForm;
|
||||
var rootForm = new RootForm();
|
||||
rootForm.FormClosed += OnRootFormFormClosed;
|
||||
|
||||
await renderer.AddComponent(_blinFormsMainForm.RootFormContentType, new ControlWrapper(rootForm));
|
||||
|
||||
Application.Run(rootForm);
|
||||
});
|
||||
}
|
||||
|
@ -69,5 +72,22 @@ namespace BlinForms.Framework
|
|||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private sealed class ControlWrapper : IWindowsFormsControlHandler
|
||||
{
|
||||
public ControlWrapper(Control control)
|
||||
{
|
||||
Control = control ?? throw new ArgumentNullException(nameof(control));
|
||||
}
|
||||
|
||||
public Control Control { get; }
|
||||
public object NativeControl => Control;
|
||||
|
||||
public void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
|
||||
{
|
||||
FormsComponentBase.ApplyAttribute(Control, attributeEventHandlerId, attributeName, attributeValue, attributeEventUpdatesAttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
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; }
|
||||
}
|
||||
}
|
|
@ -12,18 +12,11 @@ namespace BlinForms.Framework
|
|||
{
|
||||
}
|
||||
|
||||
public RootForm RootForm { get; } = new RootForm();
|
||||
|
||||
protected override void HandleException(Exception exception)
|
||||
{
|
||||
MessageBox.Show(exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
protected override IWindowsFormsControlHandler CreateRootControl()
|
||||
{
|
||||
return RootForm;
|
||||
}
|
||||
|
||||
protected override NativeControlManager<IWindowsFormsControlHandler> CreateNativeControlManager()
|
||||
{
|
||||
return new BlinFormsNativeControlManager();
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public class BlinFormsRootFormContent<TComponent> : IBlinFormsRootFormContent
|
||||
where TComponent : IComponent
|
||||
{
|
||||
public BlinFormsRootFormContent()
|
||||
{
|
||||
RootFormContentType = typeof(TComponent);
|
||||
}
|
||||
|
||||
public Type RootFormContentType { get; }
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ namespace BlinForms.Framework
|
|||
/// <typeparam name="TComponent"></typeparam>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddBlinFormsMainForm<TComponent>(this IServiceCollection services)
|
||||
public static IServiceCollection AddRootFormContent<TComponent>(this IServiceCollection services)
|
||||
where TComponent : class, IComponent
|
||||
{
|
||||
if (services is null)
|
||||
|
@ -21,7 +21,7 @@ namespace BlinForms.Framework
|
|||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
services.AddSingleton<IBlinFormsMainFormType, BlinFormsMainFormType<TComponent>>();
|
||||
services.AddSingleton<IBlinFormsRootFormContent, BlinFormsRootFormContent<TComponent>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public interface IBlinFormsMainFormType
|
||||
{
|
||||
Type MainFormType { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace BlinForms.Framework
|
||||
{
|
||||
public interface IBlinFormsRootFormContent
|
||||
{
|
||||
Type RootFormContentType { get; }
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@ namespace BlinFormsSample
|
|||
services.AddSingleton<AppState>();
|
||||
services.AddSingleton<IBlinFormsStartup, TodoAppStartup>();
|
||||
|
||||
// Configure main form to load at startup
|
||||
services.AddBlinFormsMainForm<TodoApp>();
|
||||
// Register root form content
|
||||
services.AddRootFormContent<TodoApp>();
|
||||
})
|
||||
.Build()
|
||||
.RunAsync();
|
||||
|
|
|
@ -27,18 +27,29 @@ namespace Emblazon
|
|||
public override Dispatcher Dispatcher { get; }
|
||||
= Dispatcher.CreateDefault();
|
||||
|
||||
public async Task AddComponent<TComponent>() where TComponent : IComponent
|
||||
/// <summary>
|
||||
/// Creates a component of type <typeparamref name="TComponent"/> and adds it as a child of <paramref name="parent"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TComponent"></typeparam>
|
||||
/// <param name="parent"></param>
|
||||
/// <returns></returns>
|
||||
public async Task AddComponent<TComponent>(TComponentHandler parent) where TComponent : IComponent
|
||||
{
|
||||
await AddComponent(typeof(TComponent));
|
||||
await AddComponent(typeof(TComponent), parent);
|
||||
}
|
||||
|
||||
public async Task AddComponent(Type componentType)
|
||||
/// <summary>
|
||||
/// Creates a component of type <paramref name="componentType"/> and adds it as a child of <paramref name="parent"/>.
|
||||
/// </summary>
|
||||
/// <param name="componentType"></param>
|
||||
/// <param name="parent"></param>
|
||||
/// <returns></returns>
|
||||
public async Task AddComponent(Type componentType, TComponentHandler parent)
|
||||
{
|
||||
var component = InstantiateComponent(componentType);
|
||||
var componentId = AssignRootComponentId(component);
|
||||
var rootControl = CreateRootControl();
|
||||
|
||||
var rootAdapter = new EmblazonAdapter<TComponentHandler>(this, closestPhysicalParent: rootControl, knownTargetControl: rootControl)
|
||||
var rootAdapter = new EmblazonAdapter<TComponentHandler>(this, closestPhysicalParent: parent, knownTargetControl: parent)
|
||||
{
|
||||
Name = "RootAdapter"
|
||||
};
|
||||
|
@ -106,7 +117,5 @@ namespace Emblazon
|
|||
_componentIdToAdapter[componentId] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract TComponentHandler CreateRootControl();
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче