原生內嵌
一般而言,.NET 多平臺應用程式 UI (.NET MAUI) 應用程式包含包含版面配置的頁面,例如 Grid,以及包含檢視的版面配置,例如 Button。 頁面、版面配置和檢視全都衍生自 Element。 原生內嵌可讓衍生自 Element 的任何 .NET MAUI 控件在適用於 Android 的 .NET、適用於 iOS 的 .NET、適用於 Mac Catalyst 的 .NET 和 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 應用程式專案新增至方案之後,請執行下列步驟:
從專案刪除 Properties 資料夾。
從項目刪除 [平臺] 資料夾。
從項目刪除 Resources/AppIcon 資料夾。
從項目刪除 Resources/raw 資料夾。
從項目刪除 Resources/Splash 資料夾。
從項目刪除類別
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>
CreateMauiApp
修改 類別中的MauiProgram
方法,使其接受在方法傳回之前叫用的選擇性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 支援
若要使用衍生自 Element .NET for Android 的 .NET、適用於 iOS 的 .NET、適用於 Mac Catalyst 的 .NET for Mac Catalyst 或 WinUI 應用程式,您應該將原生應用程式專案新增至與您先前建立之 .NET MAUI 類別庫專案相同的解決方案。 然後,您應該在原生應用程式的項目檔中啟用 .NET MAUI 支援,方法是在項目檔的第一個<PropertyGroup>
節點中,將 和 $(MauiEnablePlatformUsings)
建置屬性設定$(UseMaui)
為 true
:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
</PropertyGroup>
針對適用於 Mac Catalyst 應用程式的 .NET,您也必須將 build 屬性設定 $(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.Compatiblity
NuGet 套件的項目檔Microsoft.Maui.Controls
:
<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上, OnCreate
類別中的 MainActivity
覆寫通常是執行應用程式啟動相關工作的位置。 下列程式代碼範例顯示類別中正在初始化的 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 上, FinishedLaunching
應該修改 類別中的 AppDelegate
覆寫,以建立主要檢視控制器:
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 使用延遲初始化來建立 物件。 擴充 UseMauiEmbedding
方法會在物件上 MauiAppBuilder 叫用。 因此,您的原生應用程式項目應該包含您建立的 .NET MAUI 類別庫項目的參考,其中包含這個擴充方法。 MauiContext然後,會從 MauiApp 物件建立 物件,並bool
決定內容的範圍。 將 MauiContext .NET MAUI 控件轉換成原生類型時,將會使用 物件。
取用 .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);
nativeView.WidthAnchor.ConstraintEqualTo(View.Frame.Width).Active = true;
nativeView.HeightAnchor.ConstraintEqualTo(500).Active = true;
在此範例中, ContentView衍生物件會轉換成 UIView 物件,然後設定其寬度和高度條件約束以允許互動。
注意
擴充 ToPlatformEmbedded
方法位於您稍早建立的 .NET MAUI 類別庫中。 因此,您的原生應用程式項目應該包含該專案的參考。
UIView然後,物件可以新增至檢視控制器中的檢視:
stackView.AddArrangedSubView(nativeView);
此外, ToUIViewController
.NET MAUI 中的擴充方法可用來嘗試將 .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 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應