feat: Update Maui Embedding for .NET 8

This commit is contained in:
Dan Siegel 2024-01-23 09:38:22 -06:00
Родитель 4ae0aa8886
Коммит 5f3daa95db
12 изменённых файлов: 202 добавлений и 328 удалений

Просмотреть файл

@ -1,9 +1,8 @@
using MauiEmbedding.MauiControls;
using Uno.Extensions.Maui.Platform;
namespace MauiEmbedding;
public class App : EmbeddingApplication
public class App : Application
{
protected Window? MainWindow { get; private set; }
protected IHost? Host { get; private set; }

Просмотреть файл

@ -34,15 +34,10 @@ partial class MauiEmbedding
rootContext.InitializeScopedServices();
var iApp = mauiApp.Services.GetRequiredService<IApplication>();
if(app is not EmbeddingApplication embeddingApp)
{
throw new MauiEmbeddingException(Properties.Resources.TheApplicationMustInheritFromEmbeddingApplication);
}
embeddingApp.InitializeApplication(scope.ServiceProvider, iApp);
// Initializing with the Activity to set the current activity.
// The Bundle is not actually used by Maui
_ = new EmbeddedApplication(mauiApp.Services, iApp);
// Initializing with the Activity to set the current activity.
// The Bundle is not actually used by Maui
Microsoft.Maui.ApplicationModel.Platform.Init(activity, null);
androidApp.SetApplicationHandler(iApp, rootContext);

Просмотреть файл

@ -34,13 +34,9 @@ partial class MauiEmbedding
rootContext.InitializeScopedServices();
var iApp = mauiApp.Services.GetRequiredService<IApplication>();
if (app is not EmbeddingApplication embeddingApp)
{
throw new MauiEmbeddingException(Properties.Resources.TheApplicationMustInheritFromEmbeddingApplication);
}
Microsoft.Maui.ApplicationModel.Platform.Init(() => mauiApp.Services.GetRequiredService<UIWindow>().RootViewController!);
embeddingApp.InitializeApplication(mauiApp.Services, iApp);
_ = new EmbeddedApplication(mauiApp.Services, iApp);
app.SetApplicationHandler(iApp, rootContext);
InitializeApplicationMainPage(iApp);
}

Просмотреть файл

@ -1,169 +1,185 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Maui.ApplicationModel;
using Uno.Extensions.Hosting;
namespace Uno.Extensions.Maui;
/// <summary>
/// Embedding support for Microsoft.Maui controls in Uno Platform app hosts.
/// </summary>
public static partial class MauiEmbedding
{
/// <summary>
/// Registers Maui embedding in the Uno Platform app builder.
/// </summary>
/// <returns>The updated app builder.</returns>
/// <param name="builder">The IHost builder.</param>
/// <param name="configure">Optional lambda to configure the Maui app builder.</param>
public static IApplicationBuilder UseMauiEmbedding<TApp>(this IApplicationBuilder builder, Action<MauiAppBuilder>? configure = null)
where TApp : MauiApplication
=> builder.Configure(hostBuilder => hostBuilder.UseMauiEmbedding<TApp>(builder.App, builder.Window, configure));
/// <summary>
/// Registers Maui embedding in the Uno Platform app builder.
/// </summary>
/// <returns>The updated app builder.</returns>
/// <param name="builder">The IHost builder.</param>
/// <param name="app">The Uno app.</param>
/// <param name="window">The Main Application Window.</param>
/// <param name="configure">Optional lambda to configure the Maui app builder.</param>
public static IHostBuilder UseMauiEmbedding<TApp>(this IHostBuilder builder, Microsoft.UI.Xaml.Application app, Microsoft.UI.Xaml.Window window, Action<MauiAppBuilder>? configure = null)
where TApp : MauiApplication
{
#if MAUI_EMBEDDING
var mauiAppBuilder = ConfigureMauiAppBuilder<TApp>(app, window, configure);
builder.UseServiceProviderFactory(new UnoServiceProviderFactory(mauiAppBuilder, () => BuildMauiApp(mauiAppBuilder, app, window)));
#endif
return builder;
}
/// <summary>
/// Registers Maui embedding with WinUI3 and WPF application builder.
/// </summary>
/// <param name="app">The Uno app.</param>
/// <param name="window">The Main Application Window.</param>
/// <param name="configure">Optional lambda to configure the Maui app builder.</param>
public static MauiApp UseMauiEmbedding<TApp>(this Microsoft.UI.Xaml.Application app, Microsoft.UI.Xaml.Window window, Action<MauiAppBuilder>? configure = null)
where TApp : MauiApplication
{
#if MAUI_EMBEDDING
var mauiAppBuilder = ConfigureMauiAppBuilder<TApp>(app, window, configure);
return BuildMauiApp(mauiAppBuilder, app, window);
#else
return default!;
#endif
}
#if MAUI_EMBEDDING
private static MauiAppBuilder ConfigureMauiAppBuilder<TApp>(Application app, Microsoft.UI.Xaml.Window window, Action<MauiAppBuilder>? configure)
where TApp : MauiApplication
{
var mauiAppBuilder = MauiApp.CreateBuilder()
.UseMauiEmbedding<TApp>()
.RegisterPlatformServices(app);
mauiAppBuilder.Services.AddSingleton(app)
.AddSingleton(window)
.AddSingleton<IMauiInitializeService, MauiEmbeddingInitializer>();
// HACK: https://github.com/dotnet/maui/pull/16758
mauiAppBuilder.Services.RemoveAll<IApplication>()
.AddSingleton<IApplication, TApp>();
configure?.Invoke(mauiAppBuilder);
return mauiAppBuilder;
}
private static MauiApp BuildMauiApp(MauiAppBuilder builder, Application app, Microsoft.UI.Xaml.Window window)
{
var mauiApp = builder.Build();
mauiApp.InitializeMauiEmbeddingApp(app);
#if WINDOWS
window.Activated += (s, args) =>
{
WindowStateManager.Default.OnActivated(window, args);
};
#endif
return mauiApp;
}
private static void InitializeScopedServices(this IMauiContext scopedContext)
{
var scopedServices = scopedContext.Services.GetServices<IMauiInitializeScopedService>();
foreach (var service in scopedServices)
{
service.Initialize(scopedContext.Services);
}
}
private static void InitializeApplicationMainPage(IApplication iApp)
{
if (iApp is not MauiApplication app || app.Handler?.MauiContext is null)
{
// NOTE: This method is supposed to be called immediately after we initialize the Application Handler
// This should never actually happen but is required due to nullability
return;
}
#if ANDROID
var services = app.Handler.MauiContext.Services;
var context = new MauiContext(services, services.GetRequiredService<Android.App.Activity>());
#else
var context = app.Handler.MauiContext;
#endif
// Create an Application Main Page and initialize a Handler with the Maui Context
var page = new ContentPage();
app.MainPage = page;
_ = page.ToPlatform(context);
// Create a Maui Window and initialize a Handler shim. This will expose the actual Application Window
var virtualWindow = new Microsoft.Maui.Controls.Window();
virtualWindow.Handler = new EmbeddedWindowHandler
{
#if IOS || MACCATALYST
PlatformView = context.Services.GetRequiredService<UIKit.UIWindow>(),
#elif ANDROID
PlatformView = context.Services.GetRequiredService<Android.App.Activity>(),
#elif WINDOWS
PlatformView = context.Services.GetRequiredService<Microsoft.UI.Xaml.Window>(),
#endif
VirtualView = virtualWindow,
MauiContext = context
};
virtualWindow.Page = page;
app.SetCoreWindow(virtualWindow);
}
private static void SetCoreWindow(this IApplication app, Microsoft.Maui.Controls.Window window)
{
if (app.Windows is List<Microsoft.Maui.Controls.Window> windows)
{
windows.Add(window);
}
}
#endif
// NOTE: This was part of the POC and is out of scope for the MVP. Keeping it in case we want to add it back later.
/*
public static MauiAppBuilder MapControl<TWinUI, TMaui>(this MauiAppBuilder builder)
where TWinUI : FrameworkElement
where TMaui : Microsoft.Maui.Controls.View
{
Interop.MauiInterop.MapControl<TWinUI, TMaui>();
return builder;
}
public static MauiAppBuilder MapStyleHandler<THandler>(this MauiAppBuilder builder)
where THandler : Interop.IWinUIToMauiStyleHandler, new()
{
Interop.MauiInterop.MapStyleHandler<THandler>();
return builder;
}
*/
}
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Maui.ApplicationModel;
using Uno.Extensions.Hosting;
namespace Uno.Extensions.Maui;
/// <summary>
/// Embedding support for Microsoft.Maui controls in Uno Platform app hosts.
/// </summary>
public static partial class MauiEmbedding
{
/// <summary>
/// Registers Maui embedding in the Uno Platform app builder.
/// </summary>
/// <returns>The updated app builder.</returns>
/// <param name="builder">The IHost builder.</param>
/// <param name="configure">Optional lambda to configure the Maui app builder.</param>
public static IApplicationBuilder UseMauiEmbedding<TApp>(this IApplicationBuilder builder, Action<MauiAppBuilder>? configure = null)
where TApp : MauiApplication
=> builder.Configure(hostBuilder => hostBuilder.UseMauiEmbedding<TApp>(builder.App, builder.Window, configure));
/// <summary>
/// Registers Maui embedding in the Uno Platform app builder.
/// </summary>
/// <returns>The updated app builder.</returns>
/// <param name="builder">The IHost builder.</param>
/// <param name="app">The Uno app.</param>
/// <param name="window">The Main Application Window.</param>
/// <param name="configure">Optional lambda to configure the Maui app builder.</param>
public static IHostBuilder UseMauiEmbedding<TApp>(this IHostBuilder builder, Microsoft.UI.Xaml.Application app, Microsoft.UI.Xaml.Window window, Action<MauiAppBuilder>? configure = null)
where TApp : MauiApplication
{
#if MAUI_EMBEDDING
var mauiAppBuilder = ConfigureMauiAppBuilder<TApp>(app, window, configure);
builder.UseServiceProviderFactory(new UnoServiceProviderFactory(mauiAppBuilder, () => BuildMauiApp(mauiAppBuilder, app, window)));
#endif
return builder;
}
/// <summary>
/// Registers Maui embedding with WinUI3 and WPF application builder.
/// </summary>
/// <param name="app">The Uno app.</param>
/// <param name="window">The Main Application Window.</param>
/// <param name="configure">Optional lambda to configure the Maui app builder.</param>
public static MauiApp UseMauiEmbedding<TApp>(this Microsoft.UI.Xaml.Application app, Microsoft.UI.Xaml.Window window, Action<MauiAppBuilder>? configure = null)
where TApp : MauiApplication
{
#if MAUI_EMBEDDING
var mauiAppBuilder = ConfigureMauiAppBuilder<TApp>(app, window, configure);
return BuildMauiApp(mauiAppBuilder, app, window);
#else
return default!;
#endif
}
#if MAUI_EMBEDDING
private static MauiAppBuilder ConfigureMauiAppBuilder<TApp>(Application app, Microsoft.UI.Xaml.Window window, Action<MauiAppBuilder>? configure)
where TApp : MauiApplication
{
// Forcing hot reload to false to prevent exceptions being raised
Microsoft.Maui.HotReload.MauiHotReloadHelper.IsEnabled = false;
var mauiAppBuilder = MauiApp.CreateBuilder()
.UseMauiEmbedding<TApp>()
.RegisterPlatformServices(app);
mauiAppBuilder.Services.AddSingleton(app)
.AddSingleton(window)
.AddSingleton<IMauiInitializeService, MauiEmbeddingInitializer>();
// HACK: https://github.com/dotnet/maui/pull/16758
mauiAppBuilder.Services.RemoveAll<IApplication>()
.AddSingleton<IApplication, TApp>();
configure?.Invoke(mauiAppBuilder);
return mauiAppBuilder;
}
private static MauiApp BuildMauiApp(MauiAppBuilder builder, Application app, Microsoft.UI.Xaml.Window window)
{
var mauiApp = builder.Build();
mauiApp.InitializeMauiEmbeddingApp(app);
#if WINDOWS
window.Activated += (s, args) =>
{
WindowStateManager.Default.OnActivated(window, args);
};
#endif
return mauiApp;
}
private static void InitializeScopedServices(this IMauiContext scopedContext)
{
var scopedServices = scopedContext.Services.GetServices<IMauiInitializeScopedService>();
foreach (var service in scopedServices)
{
service.Initialize(scopedContext.Services);
}
}
private static void InitializeApplicationMainPage(IApplication iApp)
{
if (iApp is not MauiApplication app || app.Handler?.MauiContext is null)
{
// NOTE: This method is supposed to be called immediately after we initialize the Application Handler
// This should never actually happen but is required due to nullability
return;
}
#if ANDROID
var services = app.Handler.MauiContext.Services;
var context = new MauiContext(services, services.GetRequiredService<Android.App.Activity>());
#else
var context = app.Handler.MauiContext;
#endif
// Create an Application Main Page and initialize a Handler with the Maui Context
var page = new ContentPage();
app.MainPage = page;
_ = page.ToPlatform(context);
// Create a Maui Window and initialize a Handler shim. This will expose the actual Application Window
var virtualWindow = new Microsoft.Maui.Controls.Window();
virtualWindow.Handler = new EmbeddedWindowHandler
{
#if IOS || MACCATALYST
PlatformView = context.Services.GetRequiredService<UIKit.UIWindow>(),
#elif ANDROID
PlatformView = context.Services.GetRequiredService<Android.App.Activity>(),
#elif WINDOWS
PlatformView = context.Services.GetRequiredService<Microsoft.UI.Xaml.Window>(),
#endif
VirtualView = virtualWindow,
MauiContext = context
};
virtualWindow.Page = page;
app.SetCoreWindow(virtualWindow);
}
private static void SetCoreWindow(this IApplication app, Microsoft.Maui.Controls.Window window)
{
if (app.Windows is List<Microsoft.Maui.Controls.Window> windows)
{
windows.Add(window);
}
}
#endif
internal record EmbeddedApplication : IPlatformApplication
{
public EmbeddedApplication(IServiceProvider services, IApplication application)
{
Services = services;
Application = application;
IPlatformApplication.Current = this;
}
public IServiceProvider Services { get; }
public IApplication Application { get; }
}
// NOTE: This was part of the POC and is out of scope for the MVP. Keeping it in case we want to add it back later.
/*
public static MauiAppBuilder MapControl<TWinUI, TMaui>(this MauiAppBuilder builder)
where TWinUI : FrameworkElement
where TMaui : Microsoft.Maui.Controls.View
{
Interop.MauiInterop.MapControl<TWinUI, TMaui>();
return builder;
}
public static MauiAppBuilder MapStyleHandler<THandler>(this MauiAppBuilder builder)
where THandler : Interop.IWinUIToMauiStyleHandler, new()
{
Interop.MauiInterop.MapStyleHandler<THandler>();
return builder;
}
*/
}

Просмотреть файл

@ -1,6 +1,4 @@
using Uno.Extensions.Maui.Platform;
namespace Uno.Extensions.Maui;
namespace Uno.Extensions.Maui;
partial class MauiEmbedding
{
@ -26,12 +24,7 @@ partial class MauiEmbedding
rootContext.InitializeScopedServices();
var iApp = mauiApp.Services.GetRequiredService<IApplication>();
if (app is not EmbeddingApplication embeddingApp)
{
throw new MauiEmbeddingException(Properties.Resources.TheApplicationMustInheritFromEmbeddingApplication);
}
embeddingApp.InitializeApplication(mauiApp.Services, iApp);
_ = new EmbeddedApplication(mauiApp.Services, iApp);
app.SetApplicationHandler(iApp, rootContext);
InitializeApplicationMainPage(iApp);
}

Просмотреть файл

@ -23,11 +23,7 @@ public partial class MauiHost : ContentControl
if (IPlatformApplication.Current?.Application is null
&& MauiApplication.Current?.Handler.MauiContext is not null)
{
if (Application.Current is not EmbeddingApplication embeddingApp)
{
throw new MauiEmbeddingInitializationException();
}
embeddingApp.InitializeApplication(MauiApplication.Current.Handler.MauiContext.Services, MauiApplication.Current);
_ = new MauiEmbedding.EmbeddedApplication(MauiApplication.Current.Handler.MauiContext.Services, MauiApplication.Current);
}
if (args.NewValue is null ||

Просмотреть файл

@ -1,43 +0,0 @@
namespace Uno.Extensions.Maui.Platform;
/// <summary>
/// Provides a base Application class for Maui Embedding
/// </summary>
public partial class EmbeddingApplication : Application, IPlatformApplication
{
private IServiceProvider _serviceProvider = default!;
private IApplication _application = default!;
IServiceProvider IPlatformApplication.Services => _serviceProvider;
IApplication IPlatformApplication.Application => _application;
internal void InitializeApplication(IServiceProvider services, IApplication application)
{
_serviceProvider = services;
_application = application;
// Hack: This is a workaround for https://github.com/dotnet/maui/pull/16803
HackMauiApplication.Initialize(services, application);
IPlatformApplication.Current = this;
}
private class HackMauiApplication : Microsoft.Maui.MauiApplication
{
private HackMauiApplication(IServiceProvider services, IApplication application)
: base(0, default)
{
// TODO: Adjust code to remove this warning (https://github.com/unoplatform/uno.extensions/issues/2142)
#pragma warning disable CS0618 // Type or member is obsolete
Services = services;
Application = application;
#pragma warning restore CS0618 // Type or member is obsolete
}
protected override MauiApp CreateMauiApp() => throw new NotImplementedException();
public override void OnCreate() { }
public static HackMauiApplication Initialize(IServiceProvider services, IApplication application) =>
new (services, application);
}
}

Просмотреть файл

@ -1,47 +0,0 @@
using Foundation;
using UIKit;
namespace Uno.Extensions.Maui.Platform;
/// <summary>
/// Provides a base Application class for Maui Embedding
/// </summary>
public partial class EmbeddingApplication : Application, IPlatformApplication
{
private IServiceProvider _serviceProvider = default!;
private IApplication _application = default!;
IServiceProvider IPlatformApplication.Services => _serviceProvider;
IApplication IPlatformApplication.Application => _application;
internal void InitializeApplication(IServiceProvider services, IApplication application)
{
_serviceProvider = services;
_application = application;
// Hack: This is a workaround for https://github.com/dotnet/maui/pull/16803
HackMauiUIApplicationDelegate.Initialize(services, application);
IPlatformApplication.Current = this;
}
private class HackMauiUIApplicationDelegate : MauiUIApplicationDelegate
{
private HackMauiUIApplicationDelegate(IServiceProvider services, IApplication application)
{
// TODO: Adjust code to remove this warning (https://github.com/unoplatform/uno.extensions/issues/2142)
#pragma warning disable CS0618 // Type or member is obsolete
Services = services;
Application = application;
#pragma warning restore CS0618 // Type or member is obsolete
}
public static HackMauiUIApplicationDelegate Initialize(IServiceProvider services, IApplication application) =>
new(services, application);
protected override MauiApp CreateMauiApp() => throw new NotImplementedException();
public override bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) => true;
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) => true;
}
}

Просмотреть файл

@ -1,13 +1,10 @@
namespace Uno.Extensions.Maui.Platform;
namespace Uno.Extensions.Maui.Platform;
/// <summary>
/// Provides a base Application class for Maui Embedding
/// </summary>
public partial class EmbeddingApplication
[Obsolete("The EmbeddingApplication is obsolete in .NET 8. Please use Microsoft.UI.Xaml.Application", error: true)]
public partial class EmbeddingApplication : Application
{
static EmbeddingApplication()
{
// Forcing hot reload to false to prevent exceptions being raised
Microsoft.Maui.HotReload.MauiHotReloadHelper.IsEnabled = false;
}
}

Просмотреть файл

@ -1,8 +0,0 @@
namespace Uno.Extensions.Maui.Platform;
/// <summary>
/// Provides a base Application class for Maui Embedding
/// </summary>
public partial class EmbeddingApplication : Application
{
}

Просмотреть файл

@ -1,20 +0,0 @@
namespace Uno.Extensions.Maui.Platform;
/// <summary>
/// Provides a base Application class for Maui Embedding
/// </summary>
public partial class EmbeddingApplication : MauiWinUIApplication
{
/// <inheritdoc />
protected sealed override MauiApp CreateMauiApp() => throw new NotImplementedException();
/// <inheritdoc />
protected override void OnLaunched(LaunchActivatedEventArgs args) { }
internal void InitializeApplication(IServiceProvider services, IApplication application)
{
Services = services;
Application = application;
IPlatformApplication.Current = this;
}
}

Просмотреть файл

@ -1,8 +1,8 @@
using System.Runtime.CompilerServices;
using Uno.Extensions.Markup.Internals;
using Uno.Extensions.Markup.Internals;
namespace Uno.Extensions.Maui;
namespace Uno.Extensions.Maui;
public static class MauiHostMarkupExtensions
{
[MarkupExtension]