原生嵌入
通常,.NET Multi-platform App UI (.NET MAUI) 应用包括包含布局的页面,例如 Grid,以及包含视图的布局,例如 Button。 页面、布局和视图全部派生自 Element。 本机嵌入使派生自 Element 的任何 .NET MAUI 控件都可以在 .NET Android、.NET iOS、.NET Mac Catalyst 和 WinUI 本机应用中使用。
在本机应用中使用 .NET MAUI 控件的过程如下所示:
- 创建扩展方法,以启动本机嵌入式应用。 有关详细信息,请参阅创建扩展方法。
- 创建包含 .NET MAUI UI 和任何依赖项的 .NET MAUI 单一项目。 有关详细信息,请参阅创建 .NET MAUI 单一项目。
- 创建本机应用,并在其中启用 .NET MAUI 支持。 有关详细信息,请参阅 启用 .NET MAUI 支持。
- 通过调用
UseMauiEmbedding
扩展方法初始化 .NET MAUI。 有关详细信息,请参阅启用 .NET MAUI。 - 创建 .NET MAUI UI,并使用
ToPlatformEmbedding
扩展方法将其转换为适当的本机类型。 有关详细信息,请参阅使用 .NET MAUI 控件。
注意
使用本机嵌入时,.NET MAUI 的数据绑定引擎仍然有效。 但是,必须使用本机导航 API 执行页面导航。
创建扩展方法
在创建使用 .NET MAUI 控件的本机应用之前,应首先创建一个 .NET MAUI 类库项目,并从中删除平台文件夹和 Class1
类。 然后,向其添加一个名为 EmbeddedExtensions
的类,其中包含以下代码:
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Maui.Platform;
#if ANDROID
using PlatformView = Android.Views.View;
using PlatformWindow = Android.App.Activity;
using PlatformApplication = Android.App.Application;
#elif IOS || MACCATALYST
using PlatformView = UIKit.UIView;
using PlatformWindow = UIKit.UIWindow;
using PlatformApplication = UIKit.IUIApplicationDelegate;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.FrameworkElement;
using PlatformWindow = Microsoft.UI.Xaml.Window;
using PlatformApplication = Microsoft.UI.Xaml.Application;
#endif
namespace Microsoft.Maui.Controls;
public static class EmbeddedExtensions
{
public static MauiAppBuilder UseMauiEmbedding(this MauiAppBuilder builder, PlatformApplication? platformApplication = null)
{
#if ANDROID
platformApplication ??= (Android.App.Application)Android.App.Application.Context;
#elif IOS || MACCATALYST
platformApplication ??= UIKit.UIApplication.SharedApplication.Delegate;
#elif WINDOWS
platformApplication ??= Microsoft.UI.Xaml.Application.Current;
#endif
builder.Services.AddSingleton(platformApplication);
builder.Services.AddSingleton<EmbeddedPlatformApplication>();
builder.Services.AddScoped<EmbeddedWindowProvider>();
// Returning null is acceptable here as the platform window is optional - but we don't know until we resolve it
builder.Services.AddScoped<PlatformWindow>(svc => svc.GetRequiredService<EmbeddedWindowProvider>().PlatformWindow!);
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IMauiInitializeService, EmbeddedInitializeService>());
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(Window), typeof(EmbeddedWindowHandler));
});
return builder;
}
public static IMauiContext CreateEmbeddedWindowContext(this MauiApp mauiApp, PlatformWindow platformWindow, Window? window = null)
{
var windowScope = mauiApp.Services.CreateScope();
#if ANDROID
var windowContext = new MauiContext(windowScope.ServiceProvider, platformWindow);
#else
var windowContext = new MauiContext(windowScope.ServiceProvider);
#endif
window ??= new Window();
var wndProvider = windowContext.Services.GetRequiredService<EmbeddedWindowProvider>();
wndProvider.SetWindow(platformWindow, window);
window.ToHandler(windowContext);
return windowContext;
}
public static PlatformView ToPlatformEmbedded(this IElement element, IMauiContext context)
{
var wndProvider = context.Services.GetService<EmbeddedWindowProvider>();
if (wndProvider is not null && wndProvider.Window is Window wnd && element is VisualElement visual)
wnd.AddLogicalChild(visual);
return element.ToPlatform(context);
}
private class EmbeddedInitializeService : IMauiInitializeService
{
public void Initialize(IServiceProvider services) =>
services.GetRequiredService<EmbeddedPlatformApplication>();
}
}
这些扩展方法位于 Microsoft.Maui.Controls
命名空间中,用于在每个平台上启动本机嵌入式应用。 扩展方法引用 EmbeddedPlatformApplication
、EmbeddedWindowHandler
和 EmbeddedWindowProvider
类型,还必须将其添加到 .NET MAUI 库项目。
以下代码显示了 EmbeddedPlatformApplication
类,该类应添加到与 EmbeddedExtensions
类相同的 .NET MAUI 库项目中:
#if ANDROID
using PlatformApplication = Android.App.Application;
#elif IOS || MACCATALYST
using PlatformApplication = UIKit.IUIApplicationDelegate;
#elif WINDOWS
using PlatformApplication = Microsoft.UI.Xaml.Application;
#endif
namespace Microsoft.Maui.Controls;
internal class EmbeddedPlatformApplication : IPlatformApplication
{
private readonly MauiContext rootContext;
private readonly IMauiContext applicationContext;
public IServiceProvider Services { get; }
public IApplication Application { get; }
public EmbeddedPlatformApplication(IServiceProvider services)
{
IPlatformApplication.Current = this;
#if ANDROID
var platformApp = services.GetRequiredService<PlatformApplication>();
rootContext = new MauiContext(services, platformApp);
#else
rootContext = new MauiContext(services);
#endif
applicationContext = MakeApplicationScope(rootContext);
Services = applicationContext.Services;
Application = Services.GetRequiredService<IApplication>();
}
private static IMauiContext MakeApplicationScope(IMauiContext rootContext)
{
var scopedContext = new MauiContext(rootContext.Services);
InitializeScopedServices(scopedContext);
return scopedContext;
}
private static void InitializeScopedServices(IMauiContext scopedContext)
{
var scopedServices = scopedContext.Services.GetServices<IMauiInitializeScopedService>();
foreach (var service in scopedServices)
service.Initialize(scopedContext.Services);
}
}
以下代码显示了 EmbeddedWindowHandler
类,该类应添加到与 EmbeddedExtensions
类相同的 .NET MAUI 库项目中:
using Microsoft.Maui.Handlers;
#if ANDROID
using PlatformWindow = Android.App.Activity;
#elif IOS || MACCATALYST
using PlatformWindow = UIKit.UIWindow;
#elif WINDOWS
using PlatformWindow = Microsoft.UI.Xaml.Window;
#endif
namespace Microsoft.Maui.Controls;
internal class EmbeddedWindowHandler : ElementHandler<IWindow, PlatformWindow>, IWindowHandler
{
public static IPropertyMapper<IWindow, IWindowHandler> Mapper =
new PropertyMapper<IWindow, IWindowHandler>(ElementHandler.ElementMapper)
{
};
public static CommandMapper<IWindow, IWindowHandler> CommandMapper =
new CommandMapper<IWindow, IWindowHandler>(ElementHandler.ElementCommandMapper)
{
};
public EmbeddedWindowHandler() : base(Mapper)
{
}
protected override PlatformWindow CreatePlatformElement() =>
MauiContext!.Services.GetRequiredService<PlatformWindow>() ??
throw new InvalidOperationException("EmbeddedWindowHandler could not locate a platform window.");
}
以下代码显示了 EmbeddedWindowProvider
类,该类应添加到与 EmbeddedExtensions
类相同的 .NET MAUI 库项目中:
#if ANDROID
using PlatformWindow = Android.App.Activity;
#elif IOS || MACCATALYST
using PlatformWindow = UIKit.UIWindow;
#elif WINDOWS
using PlatformWindow = Microsoft.UI.Xaml.Window;
#endif
namespace Microsoft.Maui.Controls;
public class EmbeddedWindowProvider
{
WeakReference<PlatformWindow?>? platformWindow;
WeakReference<Window?>? window;
public PlatformWindow? PlatformWindow => Get(platformWindow);
public Window? Window => Get(window);
public void SetWindow(PlatformWindow? platformWindow, Window? window)
{
this.platformWindow = new WeakReference<PlatformWindow?>(platformWindow);
this.window = new WeakReference<Window?>(window);
}
private static T? Get<T>(WeakReference<T?>? weak) where T : class =>
weak is not null && weak.TryGetTarget(out var target) ? target : null;
}
创建 .NET MAUI 单一项目
在创建使用 .NET MAUI 控件的本机应用之前,应将 .NET MAUI 应用项目添加到与之前创建的 .NET MAUI 类库项目相同的解决方案中。 .NET MAUI 应用项目将存储你打算在本机嵌入式应用中重复使用的 UI。 将新的 .NET MAUI 应用项目添加到解决方案后,请执行以下步骤:
从项目中删除属性文件夹。
从项目中删除平台文件夹。
从项目中删除资源/应用图标文件夹。
从项目中删除资源/原始文件夹。
从项目中删除资源/初始文件夹。
从项目中删除
AppShell
类。修改
App
类,使其不设置MainPage
属性:public partial class App : Application { public App() { InitializeComponent(); } }
从项目中删除
MainPage
类。修改项目文件,使
$(TargetFrameworks)
生成属性设置为net8.0
,并删除$(OutputType)
生成属性:<PropertyGroup> <TargetFrameworks>net8.0</TargetFrameworks> <RootNamespace>MyMauiApp</RootNamespace> <UseMaui>true</UseMaui> <SingleProject>true</SingleProject> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> ... </PropertyGroup>
修改
MauiProgram
类中的CreateMauiApp
方法,使其接受方法返回前调用的可选Action<MauiAppBuilder>
参数:public static MauiApp CreateMauiApp(Action<MauiAppBuilder>? additional = null) { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); #if DEBUG builder.Logging.AddDebug(); #endif additional?.Invoke(builder); return builder.Build(); }
此时,应将所需的 .NET MAUI UI 添加到项目,包括任何依赖项和资源,并确保项目正确生成。
启用 .NET MAUI 支持
若要使用派生自 .NET Android、.NET iOS、.NET Mac Catalyst 或 WinUI 应用中的 Element 的 .NET MAUI 控件,应将本机应用项目添加到之前创建的 .NET MAUI 类库项目所在的同一解决方案中。 然后,应在本机应用的项目文件中启用 .NET MAUI 支持,方法是将 $(UseMaui)
和 $(MauiEnablePlatformUsings)
生成属性设置为在项目文件的第一个 <PropertyGroup>
节点中的 true
:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
</PropertyGroup>
对于 .NET Mac Catalyst 应用,还需要将 $(SupportedOSPlatformVersion)
生成属性设置为至少 14.0:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
</PropertyGroup>
对于 WinUI 应用,还需要将 $(EnableDefaultXamlItems)
生成属性设置为 false
:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
<EnableDefaultXamlItems>false</EnableDefaultXamlItems>
</PropertyGroup>
这样你将不会收到有关已定义 InitializeComponent
方法的生成错误。
然后,将 $(PackageReference)
生成项添加到 Microsoft.Maui.Controls
和 Microsoft.Maui.Controls.Compatiblity
NuGet 包的项目文件中:
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
</ItemGroup>
初始化 .NET MAUI
必须先初始化 .NET MAUI,然后本机应用项目才能构造 .NET MAUI 控件。 选择何时初始化它主要取决于在应用流中最方便的时候 - 它可以在启动时执行,也可以在构造 .NET MAUI 控件之前执行。 此处概述的方法是在创建应用的初始 UI 时初始化 .NET MAUI。
通常,在本机应用项目中初始化 .NET MAUI 的模式如下所示:
- 创建 MauiApp 对象。
- 从 MauiApp 对象中创建 MauiContext 对象。
在 Android 上,MainActivity
类中的 OnCreate
替代通常是执行应用启动相关任务的位置。 下面的代码示例展示 MainActivity
类中正在初始化的 .NET MAUI:
namespace MyNativeEmbeddedApp.Droid
{
[Activity(Label = "@string/app_name", MainLauncher = true, Theme = "@style/AppTheme")]
public class MainActivity : Activity
{
public static readonly Lazy<MauiApp> MauiApp = new(() =>
{
var mauiApp = MauiProgram.CreateMauiApp(builder =>
{
builder.UseMauiEmbedding();
});
return mauiApp;
});
public static bool UseWindowContext = true;
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Ensure .NET MAUI app is built before creating .NET MAUI views
var mauiApp = MainActivity.MauiApp.Value;
// Create .NET MAUI context
var mauiContext = UseWindowContext
? mauiApp.CreateEmbeddedWindowContext(this) // Create window context
: new MauiContext(mauiApp.Services, this); // Create app context
...
}
}
}
在 iOS 和 Mac Catalyst 上,应修改 AppDelegate
类中的 FinishedLaunching
替代,以创建主视图控制器:
namespace MyNativeEmbeddedApp.iOS
{
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow? Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var vc = new MainViewController();
Window.RootViewController = vc;
Window.MakeKeyAndVisible();
return true;
}
}
}
然后,可以在主视图控制器的 ViewDidLoad
方法中初始化 .NET MAUI:
using Microsoft.Maui.Platform;
namespace MyNativeEmbeddedApp.iOS
{
public class MainViewController : UIViewController
{
UIWindow GetWindow() =>
View?.Window ??
ParentViewController?.View?.Window ??
MainViewController.MauiApp.Value.Services.GetRequiredService<IUIApplicationDelegate>().GetWindow() ??
UIApplication.SharedApplication.Delegate.GetWindow();
public static readonly Lazy<MauiApp> MauiApp = new(() =>
{
var mauiApp = MauiProgram.CreateMauiApp(builder =>
{
builder.UseMauiEmbedding();
});
return mauiApp;
});
public static bool UseWindowContext = true;
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Ensure app is built before creating .NET MAUI views
var mauiApp = MainViewController.MauiApp.Value;
// Create .NET MAUI context
var mauiContext = UseWindowContext
? mauiApp.CreateEmbeddedWindowContext(GetWindow()) // Create window context
: new MauiContext(mauiApp.Services); // Create app context
...
}
}
}
在 Windows 上,MainWindow
类通常是执行 UI 相关应用启动任务的位置:
namespace MyNativeEmbeddedApp.WinUI
{
public sealed partial class MainWindow : Microsoft.UI.Xaml.Window
{
public static readonly Lazy<MauiApp> MauiApp = new(() =>
{
var mauiApp = MauiProgram.CreateMauiApp(builder =>
{
builder.UseMauiEmbedding();
});
return mauiApp;
});
public static bool UseWindowContext = true;
public MainWindow()
{
this.InitializeComponent();
// Ensure .NET MAUI app is built before creating .NET MAUI views
var mauiApp = MainWindow.MauiApp.Value;
// Create .NET MAUI context
var mauiContext = UseWindowContext
? mauiApp.CreateEmbeddedWindowContext(this) // Create window context
: new MauiContext(mauiApp.Services); // Create app context
...
}
}
}
在此示例中,使用迟缓初始化创建 MauiApp 对象。 在 MauiAppBuilder 对象上调用 UseMauiEmbedding
扩展方法。 因此,本机应用项目应包含对你创建的包含此扩展方法的 .NET MAUI 类库项目的引用。 然后,从 MauiApp 对象创建 MauiContext 对象,利用 bool
确定上下文的范围。 将 .NET MAUI 控件转换为本机类型时,将使用 MauiContext 对象。
使用 .NET MAUI 控件
在本机应用中初始化 .NET MAUI 后,可以将 .NET MAUI UI 添加到本机应用的布局。 这可以通过创建 UI 的实例并使用 ToPlatformEmbedded
扩展方法将其转换为适当的本机类型来实现。
在 Android 上,ToPlatformEmbedded
扩展方法将 .NET MAUI 控件转换为 Android View 对象:
var mauiView = new MyMauiContent();
Android.Views.View nativeView = mauiView.ToPlatformEmbedded(mauiContext);
在此示例中,派生自 ContentView 的对象转换为 Android View 对象。
注意
ToPlatformEmbedded
扩展方法位于前面创建的 .NET MAUI 类库中。 因此,本机应用项目应包含对该项目的引用。
然后,可以将 View 对象添加到本机应用中的布局:
rootLayout.AddView(nativeView, new LinearLayout.LayoutParams(MatchParent, WrapContent));
在 iOS 和 Mac Catalyst 上,ToPlatformEmbedded
扩展方法将 .NET MAUI 控件转换为 UIView 对象:
var mauiView = new MyMauiContent();
UIView nativeView = mauiView.ToPlatformEmbedded(mauiContext);
在此示例中,派生自 ContentView 的对象转换为 UIView 对象。
注意
ToPlatformEmbedded
扩展方法位于前面创建的 .NET MAUI 类库中。 因此,本机应用项目应包含对该项目的引用。
然后,可以将 UIView 对象添加到视图控制器中的视图:
stackView.AddArrangedSubView(nativeView);
警告
目前无法与 iOS 和 Mac Catalyst 上的 .NET MAUI UI 进行交互。 有关详细信息,请参阅 GitHub 问题 #19340。
此外,可以使用 .NET MAUI 中的 ToUIViewController
扩展方法尝试将 .NET MAUI 页转换为 UIViewController:
MyMauiPage myMauiPage = new MyMauiPage();
UIViewController myPageController = myMauiPage.ToUIViewController(mauiContext);
在此示例中,派生自 ContentPage 的对象转换为 UIViewController。
在 Windows 上,ToPlatformEmbedded
扩展方法将 .NET MAUI 控件转换为 FrameworkElement
对象:
var mauiView = new MyMauiContent();
FrameworkElement nativeView = myMauiPage.ToPlatformEmbedded(mauiContext);
在此示例中,派生自 ContentView 的对象转换为 FrameworkElement
对象。 然后,可以将 FrameworkElement
对象设置为 WinUI 页的内容。
然后,可以将 FrameworkElement
对象添加到本机应用中的布局:
stackPanel.Children.Add(nativeView);
重要
为了避免发生错误,应在调试配置中运行本机嵌入式应用之前禁用 XAML 热重载。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈