Встроенное внедрение
Как правило, приложение многоплатформенного пользовательского интерфейса приложений .NET (.NET MAUI) включает страницы, содержащие макеты, например макеты и макеты, содержащие представления, напримерGridButton. Страницы, макеты и представления являются производными от Element. Встроенное внедрение позволяет использовать любые элементы управления MAUI .NET, производные от Element использования в .NET для Android, .NET для iOS, .NET для Mac Catalyst и собственных приложений WinUI.
Процесс использования элемента управления MAUI .NET в собственном приложении выглядит следующим образом:
- Создайте методы расширения для загрузки собственного внедренного приложения. Дополнительные сведения см. в разделе "Создание методов расширения".
- Создайте единый проект .NET MAUI, содержащий пользовательский интерфейс .NET MAUI и все зависимости. Дополнительные сведения см. в статье "Создание единого проекта .NET MAUI".
- Создайте собственное приложение и включите в ней поддержку .NET MAUI. Дополнительные сведения см. в разделе "Включение поддержки MAUI для .NET".
- Инициализация .NET MAUI в проекте собственного приложения. Дополнительные сведения см. в разделе Инициализация MAUI .NET.
- Создайте пользовательский интерфейс .NET MAUI и преобразуйте его в соответствующий
ToPlatformEmbedding
собственный тип с помощью метода расширения. Дополнительные сведения см. в разделе "Использование элементов управления MAUI .NET".
- Создайте единый проект .NET MAUI, содержащий пользовательский интерфейс .NET MAUI и все зависимости. Дополнительные сведения см. в статье "Создание единого проекта .NET MAUI".
- Создайте собственное приложение и включите в ней поддержку .NET MAUI. Дополнительные сведения см. в разделе "Включение поддержки MAUI для .NET".
- Инициализация .NET MAUI в проекте собственного приложения. Дополнительные сведения см. в разделе Инициализация MAUI .NET.
- Создайте пользовательский интерфейс .NET MAUI и преобразуйте его в соответствующий
ToPlatformEmbedding
собственный тип с помощью метода расширения. Дополнительные сведения см. в разделе "Использование элементов управления MAUI .NET".
Примечание.
При использовании встроенного внедрения подсистема привязки данных .NET MAUI по-прежнему работает. Однако навигация по страницам должна выполняться с помощью собственного API навигации.
Создание методов расширения
Перед созданием собственного приложения, использующее элементы управления MAUI .NET, необходимо сначала создать проект библиотеки классов .NET MAUI и удалить папку Platform и 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
класс, который следует добавить в тот же проект библиотеки .NET MAUI, что и EmbeddedExtensions
класс:
#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
класс, который следует добавить в тот же проект библиотеки .NET MAUI, что и EmbeddedExtensions
класс:
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
класс, который следует добавить в тот же проект библиотеки .NET MAUI, что и EmbeddedExtensions
класс:
#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
Перед созданием собственного приложения, использующее элементы управления MAUI .NET, необходимо добавить проект приложения .NET MAUI в то же решение, что и проект библиотеки классов .NET MAUI, созданный ранее. Проект приложения .NET MAUI будет хранить пользовательский интерфейс, который вы планируете повторно использовать в собственном внедренном приложении. После добавления нового проекта приложения MAUI .NET в решение выполните следующие действия:
Удалите папку "Свойства" из проекта.
Удалите папку Platform из проекта.
Удалите папку Resources/AppIcon из проекта.
Удалите папку Resources/raw из проекта.
Удалите папку Resources/Splash из проекта.
AppShell
Удалите класс из проекта.Убедитесь,
App
что класс не задаетMainPage
свойство или переопределяетCreateWindow
метод:public partial class App : Application { public App() { InitializeComponent(); } }
MainPage
Удалите класс из проекта.Измените файл проекта таким образом, чтобы
$(TargetFramework)
свойство сборки было заданоnet8.0
, и$(OutputType)
свойство сборки удаляется:<PropertyGroup> <TargetFramework>net8.0</TargetFramework> <RootNamespace>MyMauiApp</RootNamespace> <UseMaui>true</UseMaui> <SingleProject>true</SingleProject> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> ... </PropertyGroup>
Внимание
Убедитесь, что свойство сборки задано
$(TargetFramework)
, а не свойство сборки$(TargetFrameworks)
.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, включая все зависимости и ресурсы, и убедиться, что проект будет правильно построен.
Создание единого проекта .NET MAUI
Перед созданием собственного приложения, использующее элементы управления MAUI .NET, необходимо добавить проект приложения .NET MAUI в то же решение, что и проект библиотеки классов .NET MAUI, созданный ранее. Проект приложения .NET MAUI будет хранить пользовательский интерфейс, который вы планируете повторно использовать в собственном внедренном приложении. После добавления нового проекта приложения MAUI .NET в решение выполните следующие действия:
Удалите папку "Свойства" из проекта.
Удалите папку Platform из проекта.
Удалите папку Resources/AppIcon из проекта.
Удалите папку Resources/raw из проекта.
Удалите папку Resources/Splash из проекта.
AppShell
Удалите класс из проекта.Убедитесь,
App
что класс не задаетMainPage
свойство или переопределяетCreateWindow
метод:public partial class App : Application { public App() { InitializeComponent(); } }
MainPage
Удалите класс из проекта.Измените файл проекта таким образом, чтобы
$(TargetFramework)
свойство сборки было заданоnet9.0
, и$(OutputType)
свойство сборки удаляется:<PropertyGroup> <TargetFramework>net9.0</TargetFramework> <RootNamespace>MyMauiApp</RootNamespace> <UseMaui>true</UseMaui> <SingleProject>true</SingleProject> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> ... </PropertyGroup>
Внимание
Убедитесь, что свойство сборки задано
$(TargetFramework)
, а не свойство сборки$(TargetFrameworks)
.MauiProgram
В классе изменитеCreateMauiApp
метод, чтобы принять универсальныйTApp
аргумент, и примите необязательныйAction<MauiAppBuilder>
аргумент, который вызывается перед возвратом метода. Кроме того, измените вызов наUseMauiApp<App>
UseMauiEmbeddedApp<TApp>
:public static class MauiProgram { // Create a MauiApp using the specified application. public static MauiApp CreateMauiApp<TApp>(Action<MauiAppBuilder>? additional = null) where TApp : App { var builder = MauiApp.CreateBuilder(); builder .UseMauiEmbeddedApp<TApp>() .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(); } }
MauiProgram
В классе добавьте перегрузкуCreateMauiApp
, которая принимает необязательныйAction<MauiAppBuilder>
аргумент:public static class MauiProgram { ... // Create a MauiApp using the default application. public static MauiApp CreateMauiApp(Action<MauiAppBuilder>? additional = null) => CreateMauiApp<App>(additional); }
Затем необходимо добавить необходимый пользовательский интерфейс .NET MAUI в проект, включая все зависимости и ресурсы, и убедиться, что проект выполняет сборку правильно.
Включение поддержки .NET MAUI
Чтобы использовать элементы управления .NET MAUI, производные от Element .NET для Android, .NET для iOS, .NET для Mac Catalyst или приложения WinUI, необходимо добавить собственный проект приложения в то же решение, что и проект библиотеки классов .NET MAUI, созданный ранее. Затем необходимо включить поддержку .NET MAUI в файле проекта собственного приложения, задав $(UseMaui)
свойства true
сборки $(MauiEnablePlatformUsings)
в первом <PropertyGroup>
узле в файле проекта:
<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>
Для приложений .NET для Mac Catalyst также необходимо задать $(SupportedOSPlatformVersion)
для свойства сборки не менее 15.0:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<SupportedOSPlatformVersion>15.0</SupportedOSPlatformVersion>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
</PropertyGroup>
Для приложений WinUI также необходимо задать для свойства false
сборки $(EnableDefaultXamlItems)
значение :
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
<EnableDefaultXamlItems>false</EnableDefaultXamlItems>
</PropertyGroup>
Это приведет к остановке получения ошибок сборки о том, что InitializeComponent
метод уже определен.
Затем добавьте $(PackageReference)
элементы сборки Microsoft.Maui.Controls
в файл проекта для пакетов NuGet:Microsoft.Maui.Controls.Compatiblity
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
</ItemGroup>
Затем добавьте $(PackageReference)
элементы сборки Microsoft.Maui.Controls
в файл проекта для пакета NuGet:
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
</ItemGroup>
Инициализация .NET MAUI
.NET MAUI необходимо инициализировать, прежде чем проект собственного приложения может создать элемент управления .NET MAUI. Выбор времени инициализации в первую очередь зависит от того, когда он наиболее удобно в потоке приложений, он может выполняться при запуске или непосредственно перед созданием элемента управления MAUI .NET. Описанный здесь подход заключается в инициализации .NET MAUI при создании начального пользовательского интерфейса приложения.
Как правило, шаблон инициализации .NET MAUI в проекте собственного приложения выглядит следующим образом:
- Создание объекта MauiApp.
- MauiContext Создайте объект из MauiApp объекта. Объект MauiContext будет использоваться для получения собственного представления из представления .NET MAUI.
В Android OnCreate
переопределение в MainActivity
классе обычно является местом для выполнения связанных с приложением задач запуска приложений. В следующем примере кода показано, как инициализировать MAUI .NET в MainActivity
классе:
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 context = UseWindowContext
? mauiApp.CreateEmbeddedWindowContext(this) // Create window context
: new MauiContext(mauiApp.Services, this); // Create app context
...
}
}
В iOS и Mac Catalyst AppDelegate
класс должен быть изменен для возврата true
для FinishedLaunching
переопределения:
namespace MyNativeEmbeddedApp.iOS;
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow? Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) => true;
}
Затем WillConnect
необходимо изменить метод в SceneDelegate
классе, чтобы создать основной контроллер представления и задать его в качестве представления UINavigationController
:
namespace MyNativeEmbeddedApp.iOS;
[Register("SceneDelegate")]
public class SceneDelegate : UIResponder, IUIWindowSceneDelegate
{
[Export("window")]
public UIWindow? Window { get; set; }
[Export("scene:willConnectToSession:options:")]
public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
if (scene is not UIWindowScene windowScene)
return;
Window = new UIWindow(windowScene);
var mainVC = new MainViewController();
var navigationController = new UINavigationController(mainVC);
navigationController.NavigationBar.PrefersLargeTitles = true;
Window.RootViewController = navigationController;
Window.MakeKeyAndVisible();
}
...
}
Затем в редакторе XML откройте файл Info.plist и добавьте следующий XML-код в конец файла:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
Затем .NET MAUI можно инициализировать в методе в контроллере основного ViewDidLoad
представления:
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 context = UseWindowContext
? mauiApp.CreateEmbeddedWindowContext(GetWindow()) // Create window context
: new MauiContext(mauiApp.Services); // Create app context
...
}
}
В MainWindow
Windows класс обычно является местом для выполнения задач запуска приложения, связанных с пользовательским интерфейсом:
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 context = UseWindowContext
? mauiApp.CreateEmbeddedWindowContext(this) // Create window context
: new MauiContext(mauiApp.Services); // Create app context
...
}
}
В этом примере MauiApp объект создается с помощью отложенной инициализации. Метод UseMauiEmbedding
расширения вызывается в объекте MauiAppBuilder . Поэтому проект собственного приложения должен содержать ссылку на созданный проект библиотеки классов .NET MAUI, содержащий этот метод расширения. Затем MauiContext объект создается из MauiApp объекта с определением bool
того, откуда находится контекст. Объект MauiContext будет использоваться при преобразовании элементов управления MAUI .NET в собственные типы.
Внедрение может выполняться в контексте приложения или контексте окна, но для максимальной совместимости .NET MAUI она должна выполняться в контексте окна.
Контекст приложения
Встроенная внедрение может выполняться в контексте приложения, где собственное приложение не имеет знаний о окне. При таком подходе для инициализации внедрения машинного внедрения требуется:
- Создание объекта MauiApp.
- MauiContext Создайте объект из MauiApp объекта. Объект MauiContext будет использоваться для получения собственного представления из представления .NET MAUI.
В следующем примере показан такой подход:
var mauiApp = MauiProgram.CreateMauiApp();
var context = new MauiContext(mauiApp.Services); // Activity also needs passing on Android
Затем можно создать и преобразовать представление .NET MAUI в собственное представление с ToPlatformEmbedded
помощью метода расширения, для которого требуется MauiContext объект в качестве аргумента.
Этот подход подходит для сценариев, когда машинное приложение должно внедрить простой пользовательский интерфейс .NET MAUI, но не требует доступа ко всем функциям MAUI .NET. Недостатком этого подхода является то, что средства, такие как горячая перезагрузка, и некоторые функции .NET MAUI не будут работать.
Совет
MauiApp Создание объекта при каждом внедрении представления MAUI .NET в собственном представлении не рекомендуется. Это может быть проблематично, если внедренные представления обращаются к свойству Application.Current
. Вместо этого MauiApp объект можно создать как общий статический экземпляр:
public static class MyEmbeddedMauiApp
{
static MauiApp? _shared;
public static MauiApp Shared => _shared ??= MauiProgram.CreateMauiApp();
}
С помощью этого подхода можно создать экземпляр MauiApp объекта в начале жизненного цикла приложения, чтобы избежать небольшой задержки при первом внедрении представления MAUI .NET в приложение.
В Android фрагмент представляет часть пользовательского интерфейса в действии. В следующем примере кода показано, как инициализировать MAUI .NET в фрагменте:
using Android.Runtime;
using Android.Views;
using AndroidX.Navigation.Fragment;
using Microsoft.Maui.Controls.Embedding;
using Fragment = AndroidX.Fragment.App.Fragment;
using View = Android.Views.View;
namespace MyNativeEmbeddedApp.Droid;
[Register("com.companyname.nativeembeddingdemo." + nameof(FirstFragment))]
public class FirstFragment : Fragment
{
public override View? OnCreateView(LayoutInflater inflater, ViewGroup? container, Bundle? savedInstanceState) =>
inflater.Inflate(Resource.Layout.fragment_first, container, false);
public override void OnViewCreated(View view, Bundle? savedInstanceState)
{
base.OnViewCreated(view, savedInstanceState);
// Ensure .NET MAUI app is built before creating .NET MAUI views
var mauiApp = MyEmbeddedMauiApp.Shared;
// Create .NET MAUI context
var context = new MauiContext(mauiApp.Services, Activity);
...
}
}
В iOS и Mac Catalyst AppDelegate
класс должен быть изменен для возврата true
для FinishedLaunching
переопределения:
namespace MyNativeEmbeddedApp.iOS;
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow? Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) => true;
}
Затем WillConnect
необходимо изменить метод в SceneDelegate
классе, чтобы создать основной контроллер представления и задать его в качестве представления UINavigationController
:
namespace MyNativeEmbeddedApp.iOS;
[Register("SceneDelegate")]
public class SceneDelegate : UIResponder, IUIWindowSceneDelegate
{
[Export("window")]
public UIWindow? Window { get; set; }
[Export("scene:willConnectToSession:options:")]
public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
if (scene is not UIWindowScene windowScene)
return;
Window = new UIWindow(windowScene);
var mainVC = new MainViewController();
var navigationController = new UINavigationController(mainVC);
navigationController.NavigationBar.PrefersLargeTitles = true;
Window.RootViewController = navigationController;
Window.MakeKeyAndVisible();
}
...
}
Затем в редакторе XML откройте файл Info.plist и добавьте следующий XML-код в конец файла:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
Затем .NET MAUI можно инициализировать в методе в контроллере основного ViewDidLoad
представления:
using Microsoft.Maui.Controls.Embedding;
namespace MyNativeEmbeddedApp.iOS;
public class MainViewController : UIViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Ensure .NET MAUI app is built before creating .NET MAUI views
var mauiApp = MyEmbeddedMauiApp.Shared;
// Create .NET MAUI context
var context = new MauiContext(mauiApp.Services);
...
}
}
В MainWindow
Windows класс обычно является местом для выполнения задач запуска приложения, связанных с пользовательским интерфейсом:
using Microsoft.Maui.Controls.Embedding;
using Microsoft.UI.Xaml;
namespace MyNativeEmbeddedApp.WinUI;
public sealed partial class MainWindow : Microsoft.UI.Xaml.Window
{
public MainWindow()
{
this.InitializeComponent();
}
private async void OnRootLayoutLoaded(object? sender, RoutedEventArgs e)
{
await Task.Yield();
// Ensure .NET MAUI app is built before creating .NET MAUI views
var mauiApp = MyEmbeddedMauiApp.Shared;
// Create .NET MAUI context
var context = new MauiContext(mauiApp.Services);
...
}
}
В этом примере MauiApp объект создается как общий статический экземпляр. При создании этого объекта вызывается, MauiProgram.CreateMauiApp
который в свою очередь вызывает UseMauiEmbedding
метод расширения для MauiAppBuilder объекта. Таким образом, проект собственного приложения должен содержать ссылку на созданный проект библиотеки классов .NET MAUI, содержащий MauiProgram
класс и пользовательский интерфейс .NET MAUI. Затем MauiContext объект создается из MauiApp объекта, который находится в MauiApp области объекта. Объект MauiContext будет использоваться при преобразовании элементов управления MAUI .NET в собственные типы.
Контекст окна
Встроенная внедрение может выполняться в контексте окна, где собственное приложение имеет знания о окне. В некоторых сценариях представления .NET MAUI требуют правильного доступа к окну. Например, адаптивные триггеры требуют доступа к окну представления и если нет окна, они не работают.
При таком подходе для инициализации внедрения машинного внедрения требуется:
- Создание объекта MauiApp.
- MauiContext Создайте объект с
CreateEmbeddedWindowContext
помощью метода. Объект MauiContext будет использоваться для получения собственного представления из представления .NET MAUI.
Метод CreateEmbeddedWindowContext
создает контекст окна, связанный с собственным окном с окном .NET MAUI:
var mauiApp = MauiProgram.CreateMauiApp();
var context = mauiApp.CreateEmbeddedWindowContext(this);
Затем можно создать и преобразовать представление .NET MAUI в собственное представление с ToPlatformEmbedded
помощью метода расширения, для которого требуется MauiContext объект в качестве аргумента.
Примечание.
Метод ToPlatformEmbedded
расширения имеет перегрузку, которая добавляет представление MAUI .NET в внедренное окно.
Преимущество этого подхода заключается в том, что для каждого собственного окна существует одно окно .NET MAUI, связанные с окном API-интерфейсы, связанные с окном, будут работать правильно, и средства, такие как горячая перезагрузка работает правильно.
Совет
MauiApp Создание объекта при каждом внедрении представления MAUI .NET в собственном представлении не рекомендуется. Это может быть проблематично, если внедренные представления обращаются к свойству Application.Current
. Вместо этого MauiApp объект можно создать как общий статический экземпляр:
public static class MyEmbeddedMauiApp
{
static MauiApp? _shared;
public static MauiApp Shared => _shared ??= MauiProgram.CreateMauiApp();
}
С помощью этого подхода можно создать экземпляр MauiApp объекта в начале жизненного цикла приложения, чтобы избежать небольшой задержки при первом внедрении представления MAUI .NET в приложение.
В Android фрагмент представляет часть пользовательского интерфейса в действии. В следующем примере кода показано, как инициализировать MAUI .NET в фрагменте:
using Android.Runtime;
using Android.Views;
using AndroidX.Navigation.Fragment;
using Microsoft.Maui.Controls.Embedding;
using Fragment = AndroidX.Fragment.App.Fragment;
using View = Android.Views.View;
namespace MyNativeEmbeddedApp.Droid;
[Register("com.companyname.nativeembeddingdemo." + nameof(FirstFragment))]
public class FirstFragment : Fragment
{
Activity? _window;
IMauiContext? _windowContext;
public IMauiContext WindowContext =>
_windowContext ??= MyEmbeddedMauiApp.Shared.CreateEmbeddedWindowContext(_window ?? throw new InvalidOperationException());
public override View? OnCreateView(LayoutInflater inflater, ViewGroup? container, Bundle? savedInstanceState) =>
inflater.Inflate(Resource.Layout.fragment_first, container, false);
public override void OnViewCreated(View view, Bundle? savedInstanceState)
{
base.OnViewCreated(view, savedInstanceState);
_window ??= Activity;
// Create MAUI embedded window context
var context = WindowContext;
...
}
}
В iOS и Mac Catalyst AppDelegate
класс должен быть изменен для возврата true
для FinishedLaunching
переопределения:
namespace MyNativeEmbeddedApp.iOS;
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow? Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) => true;
}
Затем WillConnect
необходимо изменить метод в SceneDelegate
классе, чтобы создать основной контроллер представления и задать его в качестве представления UINavigationController
:
namespace MyNativeEmbeddedApp.iOS;
[Register("SceneDelegate")]
public class SceneDelegate : UIResponder, IUIWindowSceneDelegate
{
[Export("window")]
public UIWindow? Window { get; set; }
[Export("scene:willConnectToSession:options:")]
public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
if (scene is not UIWindowScene windowScene)
return;
Window = new UIWindow(windowScene);
var mainVC = new MainViewController();
var navigationController = new UINavigationController(mainVC);
navigationController.NavigationBar.PrefersLargeTitles = true;
Window.RootViewController = navigationController;
Window.MakeKeyAndVisible();
}
...
}
Затем в редакторе XML откройте файл Info.plist и добавьте следующий XML-код в конец файла:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
Затем .NET MAUI можно инициализировать в методе в контроллере основного ViewDidLoad
представления:
using Microsoft.Maui.Controls.Embedding;
namespace MyNativeEmbeddedApp.iOS;
public class MainViewController : UIViewController
{
UIKit.UIWindow? _window;
IMauiContext? _windowContext;
public IMauiContext WindowContext =>
_windowContext ??= MyEmbeddedMauiApp.Shared.CreateEmbeddedWindowContext(_window ?? throw new InvalidOperationException());
public override void ViewDidLoad()
{
base.ViewDidLoad();
_window ??= ParentViewController!.View!.Window;
// Create MAUI embedded window context
var context = WindowContext;
...
}
}
В MainWindow
Windows класс обычно является местом для выполнения задач запуска приложения, связанных с пользовательским интерфейсом:
using Microsoft.Maui.Controls.Embedding;
using Microsoft.UI.Xaml;
namespace MyNativeEmbeddedApp.WinUI;
public sealed partial class MainWindow : Microsoft.UI.Xaml.Window
{
Microsoft.UI.Xaml.Window? _window;
IMauiContext? _windowContext;
public IMauiContext WindowContext =>
_windowContext ??= MyEmbeddedMauiApp.Shared.CreateEmbeddedWindowContext(_window ?? throw new InvalidOperationException());
public MainWindow()
{
this.InitializeComponent();
_window ??= this;
}
private async void OnRootLayoutLoaded(object? sender, RoutedEventArgs e)
{
await Task.Yield();
// Create MAUI embedded window context
var context = WindowContext;
...
}
}
В этом примере MauiApp объект создается как общий статический экземпляр. При создании этого объекта вызывается, MauiProgram.CreateMauiApp
который в свою очередь вызывает UseMauiEmbedding
метод расширения для MauiAppBuilder объекта. Таким образом, проект собственного приложения должен содержать ссылку на созданный проект библиотеки классов .NET MAUI, содержащий MauiProgram
класс и пользовательский интерфейс .NET MAUI. MauiContext Затем объект создается с CreateEmbeddedWindowContext
помощью метода, область действия в окне. Объект MauiContext будет использоваться при преобразовании элементов управления MAUI .NET в собственные типы.
Использование элементов управления MAUI .NET
После инициализации .NET MAUI в собственном приложении вы можете добавить пользовательский интерфейс .NET MAUI в макет собственного приложения. Это можно сделать, создав экземпляр пользовательского интерфейса и преобразовав его в соответствующий собственный тип с ToPlatformEmbedded
помощью метода расширения.
В ToPlatformEmbedded
Android метод расширения преобразует элемент управления MAUI .NET в объект Android View :
var mauiView = new MyMauiContent();
Android.Views.View nativeView = mauiView.ToPlatformEmbedded(context);
В этом примере производный ContentViewобъект преобразуется в объект Android View .
Примечание.
Метод ToPlatformEmbedded
расширения находится в созданной ранее библиотеке классов .NET MAUI. Поэтому проект собственного приложения должен содержать ссылку на этот проект.
Примечание.
Метод ToPlatformEmbedded
расширения находится в Microsoft.Maui.Controls.Embedding пространстве имен. Поэтому проект собственного приложения должен содержать инструкцию using
для этого пространства имен.
Затем View объект можно добавить в макет в собственном приложении:
rootLayout.AddView(nativeView, new LinearLayout.LayoutParams(MatchParent, WrapContent));
В iOS и Mac Catalyst ToPlatformEmbedded
метод расширения преобразует элемент управления MAUI .NET в UIView объект:
var mauiView = new MyMauiContent();
UIView nativeView = mauiView.ToPlatformEmbedded(context);
nativeView.WidthAnchor.ConstraintEqualTo(View.Frame.Width).Active = true;
nativeView.HeightAnchor.ConstraintEqualTo(500).Active = true;
В этом примере производный ContentViewобъект преобразуется в UIView объект, а затем ограничения ширины и высоты задаются для разрешения взаимодействия.
Примечание.
Метод ToPlatformEmbedded
расширения находится в созданной ранее библиотеке классов .NET MAUI. Поэтому проект собственного приложения должен содержать ссылку на этот проект.
Затем UIView объект можно добавить в представление в контроллере представления:
stackView.AddArrangedSubView(nativeView);
var mauiView = new MyMauiContent();
UIView nativeView = mauiView.ToPlatformEmbedded(context);
В этом примере производный ContentViewобъект преобразуется в UIView объект.
Примечание.
Метод ToPlatformEmbedded
расширения находится в Microsoft.Maui.Controls.Embedding пространстве имен. Поэтому проект собственного приложения должен содержать инструкцию using
для этого пространства имен.
Затем UIView объект можно добавить в представление в контроллере представления:
stackView.AddArrangedSubView(new ContainerView(nativeView));
ContainerView
— это пользовательский тип, который упаковывает представление MAUI .NET, чтобы обеспечить правильность его размера. Это достигается путем перенаправления IntrinsicContentSize
в представление SizeThatFits
.NET MAUI:
class ContainerView : UIView
{
public ContainerView(UIView view)
{
AddSubview(view);
}
public override CGSize IntrinsicContentSize =>
SizeThatFits(new CGSize(nfloat.MaxValue, nfloat.MaxValue));
public override CGSize SizeThatFits(CGSize size) =>
Subviews?.FirstOrDefault()?.SizeThatFits(size) ?? CGSize.Empty;
public override void LayoutSubviews()
{
if (Subviews?.FirstOrDefault() is { } view)
view.Frame = Bounds;
}
public override void SetNeedsLayout()
{
base.SetNeedsLayout();
InvalidateIntrinsicContentSize();
}
}
Кроме того, ToUIViewController
метод расширения в .NET MAUI можно использовать для преобразования страницы UIViewControllerMAUI .NET в :
MyMauiPage myMauiPage = new MyMauiPage();
UIViewController myPageController = myMauiPage.ToUIViewController(context);
В этом примере производный ContentPageобъект преобразуется в объект UIViewController.
В ToPlatformEmbedded
Windows метод расширения преобразует элемент управления MAUI .NET в FrameworkElement объект:
var mauiView = new MyMauiContent();
FrameworkElement nativeView = myMauiPage.ToPlatformEmbedded(context);
В этом примере производный ContentViewобъект преобразуется в FrameworkElement объект. Затем FrameworkElement объект можно задать как содержимое страницы WinUI.
Примечание.
Метод ToPlatformEmbedded
расширения находится в созданной ранее библиотеке классов .NET MAUI. Поэтому проект собственного приложения должен содержать ссылку на этот проект.
Примечание.
Метод ToPlatformEmbedded
расширения находится в Microsoft.Maui.Controls.Embedding пространстве имен. Поэтому проект собственного приложения должен содержать инструкцию using
для этого пространства имен.
Затем FrameworkElement объект можно добавить в макет в собственном приложении:
stackPanel.Children.Add(nativeView);
Внимание
Чтобы избежать ошибки, перед запуском собственного внедренного приложения в конфигурации отладки необходимо отключить горячую перезагрузку XAML.
Поддержка горячей перезагрузки XAML
Горячая перезагрузка XAML не поддерживается в встроенных приложениях. Однако вы по-прежнему можете использовать горячую перезагрузку XAML для быстрого итерации в пользовательском интерфейсе .NET MAUI, создав приложение .NET MAUI, которое использует пользовательский интерфейс .NET MAUI.
Чтобы просмотреть пользовательский интерфейс .NET MAUI с горячей перезагрузкой XAML, выполните следующие действия.
В проекте, содержащем пользовательский интерфейс .NET MAUI, обновите
MauiProgram
класс, чтобы добавитьCreateMauiApp
перегрузку, и измените существующийCreateMauiApp
метод, чтобы принять универсальный аргумент:public static class MauiProgram { public static MauiApp CreateMauiApp(Action<MauiAppBuilder>? additional = null) => CreateMauiApp<App>(additional); public static MauiApp CreateMauiApp<TApp>(Action<MauiAppBuilder>? additional = null) where TApp : App { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<TApp>() .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, преобразуйте каждый словарь ресурсов из автономного XAML-файла в словарь ресурсов, который поддерживается файлом программной части.
В проекте, содержав пользовательский интерфейс .NET MAUI, обновите экземпляр словаря ресурсов, как правило, в App.xaml, чтобы
Source
свойство также указало сборку, содержащую словарь ресурсов:<ResourceDictionary Source="Resources/Styles/Colors.xaml;assembly=NativeEmbeddingDemo" /> <ResourceDictionary Source="Resources/Styles/Styles.xaml;assembly=NativeEmbeddingDemo" />
Создайте новое приложение .NET MAUI и добавьте его в решение, содержащее проект пользовательского интерфейса .NET MAUI и собственные внедренные приложения.
В проекте приложения .NET MAUI добавьте ссылку на проект, содержащий пользовательский интерфейс .NET MAUI.
В проекте приложения .NET MAUI удалите все дочерние папки ресурсов , в которых ресурс предоставляется проектом пользовательского интерфейса .NET MAUI. Например, если проект пользовательского интерфейса .NET MAUI содержит папки "Шрифты ресурсов>", "Изображения ресурсов>" и "Стили ресурсов>", эти папки следует удалить из только что созданного приложения .NET MAUI. Это позволяет приложению .NET MAUI использовать ресурсы из проекта, содержащего пользовательский интерфейс .NET MAUI.
В приложении .NET MAUI обновите
App
класс, чтобы он был производным отApp
класса в проекте пользовательского интерфейса .NET MAUI:<myMauiUIProject:App xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:myMauiUIProject="clr-namespace:NativeEmbeddingDemo;assembly=NativeEmbeddingDemo" x:Class="TestHarnessApp.TestApp"> <myMauiUIProject:App.Resources> <!-- App specific resources go here --> </myMauiUIProject:App.Resources> </myMauiUIProject:App>
Затем обновите файл кода для
App
класса, чтобы он был производным отApp
класса в проекте пользовательского интерфейса .NET MAUI и загружает все ресурсы XAML из этого проекта:public partial class TestApp : myMauiUIProject.App { public TestApp() { var baseResources = Resources; InitializeComponent(); Resources.MergedDictionaries.Add(baseResources); MainPage = new HostPage(); } }
В приложении .NET MAUI добавьте страницу, отображающую пользовательский интерфейс из проекта, содержащего пользовательский интерфейс .NET MAUI:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:myMauiUIProject="clr-namespace:NativeEmbeddingDemo;assembly=NativeEmbeddingDemo" x:Class="TestHarnessApp.HostPage" Title="HostPage"> <myMauiUIProject:MyMauiContent /> </ContentPage>
В приложении .NET MAUI обновите
MauiProgram
класс для вызоваCreateMauiApp
метода в проекте, содержащего пользовательский интерфейс .NET MAUI:public static class MauiProgram { public static MauiApp CreateMauiApp() => NativeEmbeddingDemo.MauiProgram.CreateMauiApp<TestApp>(builder => { // Add any test harness configuration such as service stubs or mocks. }); }
Теперь вы сможете запустить проект приложения .NET MAUI на каждой платформе и использовать горячую перезагрузку XAML для итерации в пользовательском интерфейсе .NET MAUI.
Пример этого подхода см. в примере приложения.