Inserción nativa
Normalmente, una aplicación de.NET Multi-platform App UI (.NET MAUI) incluye páginas que contienen diseños, como Grid, y diseños que contienen vistas, como Button. Las páginas, los diseños y las vistas se derivan de la clase Element. La inserción nativa permite que cualquier control de .NET MAUI que derive de Element se consuma en aplicaciones nativas de .NET para Android, .NET para iOS, .NET para Mac Catalyst y WinUI.
El proceso para consumir un control .NET MAUI en una aplicación nativa es el siguiente:
- Cree métodos de extensión para arrancar la aplicación insertada nativa. Para obtener más información, consulte Crear métodos de extensión.
- Cree un proyecto único de .NET MAUI que contenga la interfaz de usuario de .NET MAUI y las dependencias. Para obtener más información, consulte Creación de un proyecto único de .NET MAUI.
- Cree una aplicación nativa y habilite la compatibilidad con .NET MAUI en ella. Para obtener más información, consulte Habilitación de la compatibilidad con .NET MAUI.
- Inicialice .NET MAUI en el proyecto de aplicación nativa. Para obtener más información, consulte Initialize .NET MAUI(Inicializar .NET MAUI).
- Cree la interfaz de usuario de MAUI de .NET y conviértala en el tipo nativo adecuado con el método de extensión
ToPlatformEmbedding
. Para obtener más información, consulte Consumo de controles MAUI de .NET.
- Cree un proyecto único de .NET MAUI que contenga la interfaz de usuario de .NET MAUI y las dependencias. Para obtener más información, consulte Creación de un proyecto único de .NET MAUI.
- Cree una aplicación nativa y habilite la compatibilidad con .NET MAUI en ella. Para obtener más información, consulte Habilitación de la compatibilidad con .NET MAUI.
- Inicialice .NET MAUI en el proyecto de aplicación nativa. Para obtener más información, consulte Initialize .NET MAUI(Inicializar .NET MAUI).
- Cree la interfaz de usuario de MAUI de .NET y conviértala en el tipo nativo adecuado con el método de extensión
ToPlatformEmbedding
. Para obtener más información, consulte Consumo de controles MAUI de .NET.
Nota:
Al usar la inserción nativa, el motor de enlace de datos de .NET MAUI sigue funcionando. Sin embargo, la navegación de páginas debe realizarse mediante la API de navegación nativa.
Creación de métodos de extensión
Antes de crear una aplicación nativa que consuma controles MAUI de .NET, primero debe crear un proyecto de biblioteca de clases .NET MAUI y eliminar la carpeta Platforms y la clase Class1
de ella. A continuación, agregue una clase a ella denominada EmbeddedExtensions
que contenga el código siguiente:
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>();
}
}
Estos métodos de extensión están en el espacio de nombres Microsoft.Maui.Controls
y se usan para arrancar la aplicación insertada nativa en cada plataforma. Los métodos de extensión hacen referencia a los tipos EmbeddedPlatformApplication
, EmbeddedWindowHandler
, y EmbeddedWindowProvider
que también debe agregar al proyecto de biblioteca MAUI de .NET.
El código siguiente muestra la clase EmbeddedPlatformApplication
, que se debe agregar al mismo proyecto de biblioteca MAUI de .NET que la clase 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);
}
}
El código siguiente muestra la clase EmbeddedWindowHandler
, que se debe agregar al mismo proyecto de biblioteca MAUI de .NET que la clase 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.");
}
El código siguiente muestra la clase EmbeddedWindowProvider
, que se debe agregar al mismo proyecto de biblioteca MAUI de .NET que la clase 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;
}
Creación de un proyecto único de MAUI de .NET
Antes de crear una aplicación nativa que consuma controles MAUI de .NET, debe agregar un proyecto de aplicación MAUI de .NET a la misma solución que el proyecto de biblioteca de clases MAUI de .NET que ha creado anteriormente. El proyecto de aplicación MAUI de .NET almacenará la interfaz de usuario que quiere volver a usar en la aplicación insertada nativa. Después de agregar un nuevo proyecto de aplicación MAUI de .NET a la solución, realice los pasos siguientes:
Elimine la carpeta Propiedades del proyecto.
Elimine la carpeta Plataforma del proyecto.
Elimine la carpeta Resources/AppIcon del proyecto.
Elimine la carpeta Resources/raw del proyecto.
Elimine la carpetaResources/Splash del proyecto.
Elimine la clase
AppShell
del proyecto.Asegúrese de que la
App
clase no establece laMainPage
propiedad ni invalida elCreateWindow
método :public partial class App : Application { public App() { InitializeComponent(); } }
Elimine la clase
MainPage
del proyecto.Modifique el archivo de proyecto para que la propiedad de compilación
$(TargetFramework)
esté establecida ennet8.0
y se quite la propiedad de compilación$(OutputType)
:<PropertyGroup> <TargetFramework>net8.0</TargetFramework> <RootNamespace>MyMauiApp</RootNamespace> <UseMaui>true</UseMaui> <SingleProject>true</SingleProject> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> ... </PropertyGroup>
Importante
Asegúrese de establecer la propiedad de compilación
$(TargetFramework)
y no la propiedad de compilación$(TargetFrameworks)
.Modifique el método
CreateMauiApp
de la claseMauiProgram
para que acepte un argumento opcionalAction<MauiAppBuilder>
que se invoca antes de que el método devuelva: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(); }
En este punto, debe agregar la interfaz de usuario de .NET MAUI necesaria al proyecto, incluidas las dependencias y los recursos, y asegurarse de que el proyecto se compila correctamente.
Creación de un proyecto único de MAUI de .NET
Antes de crear una aplicación nativa que consuma controles MAUI de .NET, debe agregar un proyecto de aplicación MAUI de .NET a la misma solución que el proyecto de biblioteca de clases MAUI de .NET que ha creado anteriormente. El proyecto de aplicación MAUI de .NET almacenará la interfaz de usuario que quiere volver a usar en la aplicación insertada nativa. Después de agregar un nuevo proyecto de aplicación MAUI de .NET a la solución, realice los pasos siguientes:
Elimine la carpeta Propiedades del proyecto.
Elimine la carpeta Plataforma del proyecto.
Elimine la carpeta Resources/AppIcon del proyecto.
Elimine la carpeta Resources/raw del proyecto.
Elimine la carpetaResources/Splash del proyecto.
Elimine la clase
AppShell
del proyecto.Asegúrese de que la
App
clase no establece laMainPage
propiedad ni invalida elCreateWindow
método :public partial class App : Application { public App() { InitializeComponent(); } }
Elimine la clase
MainPage
del proyecto.Modifique el archivo de proyecto para que la propiedad de compilación
$(TargetFramework)
esté establecida ennet9.0
y se quite la propiedad de compilación$(OutputType)
:<PropertyGroup> <TargetFramework>net9.0</TargetFramework> <RootNamespace>MyMauiApp</RootNamespace> <UseMaui>true</UseMaui> <SingleProject>true</SingleProject> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> ... </PropertyGroup>
Importante
Asegúrese de establecer la propiedad de compilación
$(TargetFramework)
y no la propiedad de compilación$(TargetFrameworks)
.En la
MauiProgram
clase , modifique elCreateMauiApp
método para aceptar unTApp
argumento genérico y acepte un argumento opcionalAction<MauiAppBuilder>
que se invoca antes de que el método devuelva. Además, cambie la llamada deUseMauiApp<App>
aUseMauiEmbeddedApp<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(); } }
En la
MauiProgram
clase , agregue unaCreateMauiApp
sobrecarga que acepte un argumento opcionalAction<MauiAppBuilder>
:public static class MauiProgram { ... // Create a MauiApp using the default application. public static MauiApp CreateMauiApp(Action<MauiAppBuilder>? additional = null) => CreateMauiApp<App>(additional); }
A continuación, debe agregar la interfaz de usuario de .NET MAUI necesaria al proyecto, incluidas las dependencias y los recursos, y asegurarse de que el proyecto se compila correctamente.
Habilitación de la compatibilidad con .NET MAUI
Para consumir controles de .NET MAUI que deriven de Element en una aplicación .NET para Android, .NET para iOS, .NET para Mac Catalyst o WinUI, deberá agregar el proyecto de su aplicación nativa a la misma solución que el proyecto de biblioteca de clases de .NET MAUI que creó anteriormente. A continuación, debe habilitar la compatibilidad con MAUI de .NET en el archivo de proyecto de la aplicación nativa estableciendo las propiedades de compilación $(UseMaui)
y $(MauiEnablePlatformUsings)
en true
el primer nodo <PropertyGroup>
del archivo de proyecto:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
</PropertyGroup>
En el caso de las aplicaciones .NET para Mac Catalyst, también deberá establecer la propiedad de compilación $(SupportedOSPlatformVersion)
en 14.0 como mínimo:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
</PropertyGroup>
Para las aplicaciones .NET para Mac Catalyst, también deberá establecer la $(SupportedOSPlatformVersion)
propiedad build en un mínimo de 15.0:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<SupportedOSPlatformVersion>15.0</SupportedOSPlatformVersion>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
</PropertyGroup>
En el caso de las aplicaciones WinUI, también deberá establecer la propiedad de compilación $(EnableDefaultXamlItems)
en false
:
<PropertyGroup>
...
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<UseMaui>true</UseMaui>
<MauiEnablePlatformUsings>true</MauiEnablePlatformUsings>
<EnableDefaultXamlItems>false</EnableDefaultXamlItems>
</PropertyGroup>
Esto dejará de recibir errores de compilación sobre el método InitializeComponent
que ya se está definiendo.
A continuación, agregue elementos de compilación $(PackageReference)
al archivo de proyecto para los paquetes NuGet Microsoft.Maui.Controls
y Microsoft.Maui.Controls.Compatiblity
:
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
</ItemGroup>
A continuación, agregue $(PackageReference)
elementos de compilación al archivo de proyecto para el Microsoft.Maui.Controls
paquete NuGet:
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
</ItemGroup>
Inicializar .NET MAUI
.NET MAUI debe inicializarse antes de que un proyecto de aplicación nativa pueda construir un control .NET MAUI. Elegir cuándo inicializarlo depende principalmente de cuándo es más conveniente en el flujo de la aplicación: se puede realizar en el inicio o justo antes de que se construya un control MAUI de .NET. El enfoque descrito aquí es inicializar .NET MAUI cuando se crea la interfaz de usuario inicial de la aplicación.
Normalmente, el patrón para inicializar .NET MAUI en un proyecto de aplicación nativa es el siguiente:
- Crear un objeto MauiApp.
- Cree un objeto MauiContext a partir del objeto MauiApp. El MauiContext objeto se usará para obtener una vista nativa de la vista MAUI de .NET.
En Android, la invalidación OnCreate
de la clase MainActivity
suele ser el lugar para realizar tareas relacionadas con el inicio de la aplicación. En el ejemplo de código siguiente se muestra que se inicializa .NET MAUI en la clase 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
...
}
}
En iOS y Mac Catalyst, la clase AppDelegate
debería modificarse para devolver true
para la invalidación 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;
}
Después debería modificarse el método WillConnect
de la clase SceneDelegate
para crear su controlador de vista principal y establecerlo como vista de la clase 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();
}
...
}
Después, en el editor XML, abra el archivo Info.plist y agregue el siguiente XML al final del archivo:
<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>
A continuación, se puede inicializar .NET MAUI en el método ViewDidLoad
en el controlador de vista principal:
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
...
}
}
En Windows, la clase MainWindow
suele ser el lugar para realizar tareas de inicio de aplicaciones relacionadas con la interfaz de usuario:
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
...
}
}
En este ejemplo, el objeto MauiApp se crea mediante la inicialización diferida. El método de extensión UseMauiEmbedding
se invoca en el objeto MauiAppBuilder. Por lo tanto, el proyecto de aplicación nativa debe incluir una referencia al proyecto de biblioteca de clases MAUI de .NET que ha creado lo que contiene este método de extensión. A continuación, se crea un objeto MauiContext a partir del objeto MauiApp, con un bool
determinando desde dónde se limita el contexto. El objeto MauiContext se usará al convertir controles .NET MAUI a tipos nativos.
La inserción se puede realizar en un contexto de aplicación o en un contexto de ventana, pero para obtener la compatibilidad máxima de .NET MAUI debe realizarse en un contexto de ventana.
Contexto de la aplicación
La inserción nativa se puede realizar en un contexto de aplicación, donde la aplicación nativa no tiene conocimiento de una ventana. Con este enfoque, la inicialización de inserción nativa requiere que:
- Crear un objeto MauiApp.
- Cree un objeto MauiContext a partir del objeto MauiApp. El MauiContext objeto se usará para obtener una vista nativa de la vista MAUI de .NET.
En el ejemplo siguiente se muestra este enfoque:
var mauiApp = MauiProgram.CreateMauiApp();
var context = new MauiContext(mauiApp.Services); // Activity also needs passing on Android
A continuación, se puede crear y convertir una vista MAUI de .NET en una vista nativa con el ToPlatformEmbedded
método de extensión, que requiere el MauiContext objeto como argumento.
Este enfoque es adecuado para escenarios en los que una aplicación nativa necesita insertar una interfaz de usuario de .NET MAUI simple, pero no requiere acceso a todas las características de .NET MAUI. La desventaja de este enfoque es que las herramientas, como la recarga activa, y algunas características de MAUI de .NET, no funcionarán.
Sugerencia
No se recomienda crear un MauiApp objeto cada vez que se inserta una vista MAUI de .NET como una vista nativa. Esto puede ser problemático si las vistas incrustadas acceden a la Application.Current
propiedad . En su lugar, el MauiApp objeto se puede crear como una instancia estática compartida:
public static class MyEmbeddedMauiApp
{
static MauiApp? _shared;
public static MauiApp Shared => _shared ??= MauiProgram.CreateMauiApp();
}
Con este enfoque, puede crear instancias del MauiApp objeto al principio del ciclo de vida de la aplicación para evitar tener un pequeño retraso la primera vez que inserte una vista MAUI de .NET en la aplicación.
En Android, un fragmento representa una parte de la interfaz de usuario dentro de una actividad. En el ejemplo de código siguiente se muestra que .NET MAUI se inicializa en un fragmento:
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);
...
}
}
En iOS y Mac Catalyst, la clase AppDelegate
debería modificarse para devolver true
para la invalidación 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;
}
Después debería modificarse el método WillConnect
de la clase SceneDelegate
para crear su controlador de vista principal y establecerlo como vista de la clase 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();
}
...
}
Después, en el editor XML, abra el archivo Info.plist y agregue el siguiente XML al final del archivo:
<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>
A continuación, se puede inicializar .NET MAUI en el método ViewDidLoad
en el controlador de vista principal:
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);
...
}
}
En Windows, la clase MainWindow
suele ser el lugar para realizar tareas de inicio de aplicaciones relacionadas con la interfaz de usuario:
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);
...
}
}
En este ejemplo, el MauiApp objeto se crea como una instancia estática compartida. Cuando se crea este objeto, se llama a que, a su vez, MauiProgram.CreateMauiApp
llama al UseMauiEmbedding
método de extensión en el MauiAppBuilder objeto . Por lo tanto, el proyecto de aplicación nativa debe incluir una referencia al proyecto de biblioteca de clases MAUI de .NET que creó que contiene la MauiProgram
clase y la interfaz de usuario de .NET MAUI. A continuación, se crea un MauiContext objeto a partir del MauiApp objeto , cuyo ámbito es el MauiApp objeto . El objeto MauiContext se usará al convertir controles .NET MAUI a tipos nativos.
Contexto de ventana
La inserción nativa se puede realizar en un contexto de ventana, donde la aplicación nativa tiene conocimiento de una ventana. En algunos escenarios, las vistas MAUI de .NET requieren acceso a una ventana para que funcione correctamente. Por ejemplo, los desencadenadores adaptables requieren acceso a la ventana de una vista y, si no hay ninguna ventana, no funcionan.
Con este enfoque, la inicialización de inserción nativa requiere que:
- Crear un objeto MauiApp.
- Cree un MauiContext objeto con el
CreateEmbeddedWindowContext
método . El MauiContext objeto se usará para obtener una vista nativa de la vista MAUI de .NET.
El CreateEmbeddedWindowContext
método crea un contexto de ventana que relaciona una ventana nativa con una ventana MAUI de .NET:
var mauiApp = MauiProgram.CreateMauiApp();
var context = mauiApp.CreateEmbeddedWindowContext(this);
A continuación, se puede crear y convertir una vista MAUI de .NET en una vista nativa con el ToPlatformEmbedded
método de extensión, que requiere el MauiContext objeto como argumento.
Nota:
El ToPlatformEmbedded
método de extensión tiene una sobrecarga que agrega una vista MAUI de .NET a una ventana incrustada.
La ventaja de este enfoque es que hay una sola ventana de .NET MAUI para cada ventana nativa, las API relacionadas con ventanas funcionarán correctamente y las herramientas como la recarga activa funcionan correctamente.
Sugerencia
No se recomienda crear un MauiApp objeto cada vez que se inserta una vista MAUI de .NET como una vista nativa. Esto puede ser problemático si las vistas incrustadas acceden a la Application.Current
propiedad . En su lugar, el MauiApp objeto se puede crear como una instancia estática compartida:
public static class MyEmbeddedMauiApp
{
static MauiApp? _shared;
public static MauiApp Shared => _shared ??= MauiProgram.CreateMauiApp();
}
Con este enfoque, puede crear instancias del MauiApp objeto al principio del ciclo de vida de la aplicación para evitar tener un pequeño retraso la primera vez que inserte una vista MAUI de .NET en la aplicación.
En Android, un fragmento representa una parte de la interfaz de usuario dentro de una actividad. En el ejemplo de código siguiente se muestra que .NET MAUI se inicializa en un fragmento:
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;
...
}
}
En iOS y Mac Catalyst, la clase AppDelegate
debería modificarse para devolver true
para la invalidación 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;
}
Después debería modificarse el método WillConnect
de la clase SceneDelegate
para crear su controlador de vista principal y establecerlo como vista de la clase 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();
}
...
}
Después, en el editor XML, abra el archivo Info.plist y agregue el siguiente XML al final del archivo:
<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>
A continuación, se puede inicializar .NET MAUI en el método ViewDidLoad
en el controlador de vista principal:
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;
...
}
}
En Windows, la clase MainWindow
suele ser el lugar para realizar tareas de inicio de aplicaciones relacionadas con la interfaz de usuario:
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;
...
}
}
En este ejemplo, el MauiApp objeto se crea como una instancia estática compartida. Cuando se crea este objeto, se llama a que, a su vez, MauiProgram.CreateMauiApp
llama al UseMauiEmbedding
método de extensión en el MauiAppBuilder objeto . Por lo tanto, el proyecto de aplicación nativa debe incluir una referencia al proyecto de biblioteca de clases MAUI de .NET que creó que contiene la MauiProgram
clase y la interfaz de usuario de .NET MAUI. A continuación, se crea un MauiContext objeto con el CreateEmbeddedWindowContext
método , cuyo ámbito es la ventana. El objeto MauiContext se usará al convertir controles .NET MAUI a tipos nativos.
Consumo de controles .NET MAUI
Después de inicializar .NET MAUI en la aplicación nativa, puede agregar la interfaz de usuario de .NET MAUI al diseño de la aplicación nativa. Esto se puede lograr mediante la creación de una instancia de la interfaz de usuario y la conversión al tipo nativo adecuado con el método de extensión ToPlatformEmbedded
.
En Android, el método de extensión ToPlatformEmbedded
convierte el control MAUI de .NET en un objeto android View:
var mauiView = new MyMauiContent();
Android.Views.View nativeView = mauiView.ToPlatformEmbedded(context);
En este ejemplo, un objeto derivado de ContentView se convierte en un objeto View de Android.
Nota:
El método de extensión ToPlatformEmbedded
está en la biblioteca de clases MAUI de .NET que creó anteriormente. Por lo tanto, el proyecto de aplicación nativa debe incluir una referencia a ese proyecto.
Nota:
El ToPlatformEmbedded
método de extensión está en el Microsoft.Maui.Controls.Embedding espacio de nombres . Por lo tanto, el proyecto de aplicación nativa debe incluir una using
instrucción para ese espacio de nombres.
A continuación, el objeto View se puede agregar a un diseño de la aplicación nativa:
rootLayout.AddView(nativeView, new LinearLayout.LayoutParams(MatchParent, WrapContent));
En iOS y Mac Catalyst, el método de extensión ToPlatformEmbedded
convierte el control .NET MAUI en un objeto UIView:
var mauiView = new MyMauiContent();
UIView nativeView = mauiView.ToPlatformEmbedded(context);
nativeView.WidthAnchor.ConstraintEqualTo(View.Frame.Width).Active = true;
nativeView.HeightAnchor.ConstraintEqualTo(500).Active = true;
En este ejemplo, un objeto derivado de ContentView se convierte en un objeto UIView y después se le establecen restricciones de anchura y altura para permitir la interacción.
Nota:
El método de extensión ToPlatformEmbedded
está en la biblioteca de clases MAUI de .NET que creó anteriormente. Por lo tanto, el proyecto de aplicación nativa debe incluir una referencia a ese proyecto.
A continuación, el objeto UIView se puede agregar a una vista en el controlador de vista:
stackView.AddArrangedSubView(nativeView);
var mauiView = new MyMauiContent();
UIView nativeView = mauiView.ToPlatformEmbedded(context);
En este ejemplo, un objeto derivado de ContentView se convierte en un objeto UIView.
Nota:
El ToPlatformEmbedded
método de extensión está en el Microsoft.Maui.Controls.Embedding espacio de nombres . Por lo tanto, el proyecto de aplicación nativa debe incluir una using
instrucción para ese espacio de nombres.
A continuación, el objeto UIView se puede agregar a una vista en el controlador de vista:
stackView.AddArrangedSubView(new ContainerView(nativeView));
ContainerView
es un tipo personalizado que ajusta la vista MAUI de .NET para asegurarse de que tiene el tamaño correcto. Esto se logra redirigiendo IntrinsicContentSize
a la vista MAUI de SizeThatFits
.NET :
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();
}
}
Además, se puede usar un ToUIViewController
método de extensión en .NET MAUI para convertir una página .NET MAUI en :UIViewController
MyMauiPage myMauiPage = new MyMauiPage();
UIViewController myPageController = myMauiPage.ToUIViewController(context);
En este ejemplo, un objeto derivado de ContentPage se convierte en un objeto UIViewController.
En Windows, el método de extensión ToPlatformEmbedded
convierte el control MAUI de .NET en un objeto FrameworkElement:
var mauiView = new MyMauiContent();
FrameworkElement nativeView = myMauiPage.ToPlatformEmbedded(context);
En este ejemplo, un objeto derivado de ContentView se convierte en un objeto FrameworkElement. Después, el objeto FrameworkElement se puede definir como el contenido de una página WinUI.
Nota:
El método de extensión ToPlatformEmbedded
está en la biblioteca de clases MAUI de .NET que creó anteriormente. Por lo tanto, el proyecto de aplicación nativa debe incluir una referencia a ese proyecto.
Nota:
El ToPlatformEmbedded
método de extensión está en el Microsoft.Maui.Controls.Embedding espacio de nombres . Por lo tanto, el proyecto de aplicación nativa debe incluir una using
instrucción para ese espacio de nombres.
A continuación, el objeto FrameworkElement se puede agregar a un diseño de la aplicación nativa:
stackPanel.Children.Add(nativeView);
Importante
Para evitar un error, la recarga activa de XAML debe deshabilitarse antes de ejecutar una aplicación insertada nativa en la configuración de depuración.
Compatibilidad con la Recarga activa de XAML
La Recarga activa de XAML no se admite en aplicaciones insertadas nativas. Sin embargo, todavía puede usar la Recarga activa de XAML para iterar rápidamente en la interfaz de usuario de .NET MAUI mediante la creación de una aplicación de .NET MAUI que consuma la interfaz de usuario de .NET MAUI.
Para ver la interfaz de usuario de .NET MAUI con la Recarga activa de XAML:
En el proyecto que contiene la interfaz de usuario de .NET MAUI, actualice la clase
MauiProgram
para agregar una sobrecargaCreateMauiApp
y modifique el métodoCreateMauiApp
existente para aceptar un argumento genérico: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(); } }
En el proyecto que contiene la interfaz de usuario de .NET MAUI, convierta cada diccionario de recursos de un archivo XAML independiente en un diccionario de recursos respaldado por un archivo de código subyacente.
En el proyecto que contiene la interfaz de usuario de .NET MAUI, actualice la creación de instancias del diccionario de recursos, normalmente en App.xaml, de modo que la propiedad
Source
especifique también el ensamblado que contiene el diccionario de recursos:<ResourceDictionary Source="Resources/Styles/Colors.xaml;assembly=NativeEmbeddingDemo" /> <ResourceDictionary Source="Resources/Styles/Styles.xaml;assembly=NativeEmbeddingDemo" />
Cree una nueva aplicación de .NET MAUI y agréguela a la solución que contiene el proyecto de la interfaz de usuario de .NET MAUI y las aplicaciones insertadas nativas.
En el proyecto de aplicación de .NET MAUI, agregue una referencia al proyecto que contiene la interfaz de usuario de .NET MAUI.
En el proyecto de la aplicación de .NET MAUI, elimine las carpetas secundarias de recursos en las que el proyecto de la interfaz de usuario de .NET MAUI proporciona el recurso. Por ejemplo, si el proyecto de la interfaz de usuario de .NET MAUI contiene carpetas de Recursos > Fuentes, Recursos > Imágenes y Recursos > Estilos, estas carpetas deben eliminarse de la aplicación de .NET MAUI que acaba de crear. Esto permite que la aplicación de .NET MAUI consuma los recursos del proyecto que contiene la interfaz de usuario de .NET MAUI.
En la aplicación de .NET MAUI, actualice la clase
App
para que derive de la claseApp
en el proyecto de la interfaz de usuario de .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>
A continuación, actualice el archivo de código subyacente de la clase
App
para que derive de la claseApp
en el proyecto de la interfaz de usuario de .NET MAUI y cargue los recursos de XAML desde este proyecto:public partial class TestApp : myMauiUIProject.App { public TestApp() { var baseResources = Resources; InitializeComponent(); Resources.MergedDictionaries.Add(baseResources); MainPage = new HostPage(); } }
En la aplicación de .NET MAUI, agregue una página que muestre la interfaz de usuario del proyecto que contiene la interfaz de usuario de .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>
En la aplicación de .NET MAUI, actualice la clase
MauiProgram
para llamar al métodoCreateMauiApp
en el proyecto que contiene la interfaz de usuario de .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. }); }
Ahora debería poder ejecutar el proyecto de la aplicación de .NET MAUI en cada plataforma y usar la Recarga activa de XAML para iterar en la interfaz de usuario de .NET MAUI.
Para obtener un ejemplo de este enfoque, consulte la aplicación de ejemplo.