.NET MAUI en 2026: Desarrollo Multiplataforma y Preguntas de Entrevista
Tutorial de .NET MAUI cubriendo desarrollo multiplataforma con .NET 10, handlers, MVVM, HybridWebView y preguntas de entrevista esenciales para 2026.

.NET MAUI (Multi-platform App UI) se ha consolidado como un framework multiplataforma de grado producción con .NET 10. Lanzado como una versión Long-Term Support con soporte hasta noviembre de 2028, .NET MAUI 10 se distribuye como workload y paquetes NuGet, ofreciendo mejoras en calidad, rendimiento y nuevas APIs como las optimizaciones de HybridWebView y SafeAreaEdges. Este tutorial cubre la construcción de una aplicación multiplataforma, la arquitectura que hace funcionar a MAUI, y las preguntas de entrevista que los equipos de contratación formulan en 2026.
.NET 10 es una versión Long-Term Support (con soporte hasta noviembre de 2028). MAUI 10 prioriza la calidad y el rendimiento por encima de nuevos controles de UI, convirtiéndola en la versión más estable de MAUI hasta la fecha. Ahora se distribuye como workload de .NET y paquetes NuGet, permitiendo el versionado fijo por proyecto.
Configuración de un Proyecto .NET MAUI 10 desde Cero
El camino más rápido hacia una aplicación MAUI funcional comienza con la CLI de .NET. .NET 10 introduce una plantilla de proyecto actualizada que incluye las configuraciones predeterminadas de .NET Aspire, conectando telemetría y descubrimiento de servicios de forma nativa.
# Install the MAUI workload (if not already present)
dotnet workload install maui
# Create a new MAUI app
dotnet new maui -n CrossPlatformDemo
cd CrossPlatformDemo
# Run on Android emulator
dotnet build -t:Run -f net10.0-androidLa estructura de proyecto único consolida el código específico de cada plataforma en una carpeta Platforms/ mientras comparte el resto. El archivo MauiProgram.cs sirve como raíz de composición, donde se registran los servicios, fuentes y handlers.
using Microsoft.Extensions.Logging;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// Register services for dependency injection
builder.Services.AddSingleton<IApiService, ApiService>();
builder.Services.AddTransient<MainViewModel>();
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}La inyección de dependencias en MAUI sigue el mismo patrón que ASP.NET Core. Los servicios Singleton persisten durante toda la vida de la aplicación, los servicios Transient se crean por cada solicitud, y los servicios Scoped requieren precaución porque MAUI no tiene un concepto de scope integrado como las solicitudes HTTP.
Handlers: La Arquitectura Detrás del Renderizado Multiplataforma
MAUI reemplazó los renderers de Xamarin.Forms con una arquitectura de handlers. Los handlers mapean cada control multiplataforma a su contraparte nativa mediante una capa de abstracción delgada. La diferencia clave: los handlers son sin estado y están desacoplados de la vista virtual, lo que los hace más rápidos y fáciles de personalizar.
using Microsoft.Maui.Handlers;
public class CustomEntryHandler : EntryHandler
{
protected override void ConnectHandler(MauiAppCompatEditText platformView)
{
base.ConnectHandler(platformView);
// Remove the default underline on Android
platformView.SetBackgroundColor(Android.Graphics.Color.Transparent);
}
}
// Register in MauiProgram.cs
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler<Entry, CustomEntryHandler>();
});En .NET 10, los controles de Entry y Editor en Android pasaron de AppCompatEditText a MauiAppCompatEditText, agregando soporte nativo para el evento SelectionChanged. Los handlers mejorados de CollectionView y CarouselView introducidos en .NET 9 son ahora los predeterminados en iOS y Mac Catalyst, resolviendo problemas de estabilidad que existían desde hace tiempo.
Android 16 Edge-to-Edge: Breaking Change y Corrección Requerida
Android 16 elimina el mecanismo de desactivación del display edge-to-edge. Las aplicaciones que apuntan a API 36 deben manejar correctamente los insets de las barras del sistema, o el contenido se renderiza incorrectamente. .NET 10 introdujo un breaking change silencioso: en .NET 9, ContentPage respetaba las barras del sistema por defecto (comportamiento Container), pero .NET 10 cambia el default a edge-to-edge (None).
Las aplicaciones que migran de .NET 9 a .NET 10 pueden ver el contenido renderizándose bajo la barra de estado o la barra de navegación en Android 15+. La corrección requiere establecer explícitamente SafeAreaEdges para restaurar el comportamiento anterior.
La corrección implica agregar un estilo implícito global en App.xaml:
<!-- App.xaml -->
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CrossPlatformDemo.App">
<Application.Resources>
<ResourceDictionary>
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="SafeAreaEdges" Value="Container" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>El enum SafeAreaEdges proporciona control granular:
None: contenido edge-to-edge sin padding de safe areaSoftInput: siempre agregar padding para el tecladoContainer: fluir bajo el teclado, mantenerse fuera de las barras y la muescaDefault: comportamiento predeterminado de la plataformaAll: respetar todos los insets de safe area
Este enfoque utiliza la Window Insets API en runtime, haciéndolo compatible con Android 16 donde los opt-outs a nivel de manifiesto ya no funcionan.
MVVM con CommunityToolkit.Mvvm: Eliminando el Código Repetitivo
El generador de código fuente CommunityToolkit.Mvvm elimina aproximadamente el 80% de la ceremonia MVVM. Sin implementaciones manuales de INotifyPropertyChanged, sin wrappers de comandos: los atributos dirigen la generación de código.
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class MainViewModel : ObservableObject
{
private readonly IApiService _apiService;
public MainViewModel(IApiService apiService)
{
_apiService = apiService;
}
// Source generator creates the 'Title' property with change notification
[ObservableProperty]
private string _title = string.Empty;
// Source generator creates the 'IsLoading' property
[ObservableProperty]
private bool _isLoading;
// Source generator creates an async ICommand
[RelayCommand]
private async Task LoadDataAsync()
{
IsLoading = true;
try
{
Title = await _apiService.FetchTitleAsync();
}
finally
{
IsLoading = false;
}
}
}El XAML se enlaza directamente a las propiedades y comandos generados:
<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:CrossPlatformDemo.ViewModels"
x:DataType="vm:MainViewModel">
<VerticalStackLayout Padding="20" Spacing="16">
<Label Text="{Binding Title}"
FontSize="24"
HorizontalOptions="Center" />
<Button Text="Load Data"
Command="{Binding LoadDataCommand}"
IsEnabled="{Binding IsLoading, Converter={StaticResource InverseBoolConverter}}" />
<ActivityIndicator IsRunning="{Binding IsLoading}"
IsVisible="{Binding IsLoading}" />
</VerticalStackLayout>
</ContentPage>El atributo x:DataType habilita los bindings compilados, que son más rápidos que los bindings basados en reflexión y producen errores en tiempo de compilación cuando una ruta de binding es incorrecta.
¿Listo para aprobar tus entrevistas de .NET?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Generador de Código Fuente XAML en .NET 10
.NET MAUI 10 introduce un generador de código fuente XAML que compila el XAML en tiempo de compilación en lugar de parsearlo en runtime. Esto mejora el rendimiento de inicio y detecta errores XAML durante la compilación.
Para habilitar la generación de código fuente XAML, agregar la propiedad al archivo de proyecto:
<PropertyGroup>
<MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>.NET 10 Preview 5 también introdujo namespaces XML implícitos que eliminan las declaraciones xmlns repetitivas. Un archivo GlobalXmlns.cs puede agregar múltiples namespaces:
[assembly: XmlnsDefinition(
"http://schemas.microsoft.com/dotnet/maui/global",
"MyApp.Views")]
[assembly: XmlnsDefinition(
"http://schemas.microsoft.com/dotnet/maui/global",
"MyApp.Controls")]Con esta configuración, los archivos XAML se vuelven más limpios:
<!-- Antes -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:MyApp.Controls"
x:Class="MyApp.MainPage">
<controls:TagView />
</ContentPage>
<!-- Después (con xmlns global) -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/maui/global"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainPage">
<TagView />
</ContentPage>HybridWebView en .NET 10: El Puente entre lo Nativo y la Web
HybridWebView permite incrustar contenido web dentro de una aplicación MAUI manteniendo comunicación bidireccional entre C# y JavaScript. .NET 10 agrega tres capacidades: invocación JavaScript de tipo fire-and-forget, eventos de inicialización para configuración específica de la plataforma, e interceptación de solicitudes web.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
// Initialization event for platform-specific tweaks
hybridWebView.WebViewInitialized += (sender, args) =>
{
System.Diagnostics.Debug.WriteLine("WebView ready");
};
}
// Call JavaScript from C#
private async void OnCallJsClicked(object sender, EventArgs e)
{
var result = await hybridWebView.InvokeJavaScriptAsync<string>(
"getFormData",
HybridSampleContext.Default.String
);
await DisplayAlertAsync("Result", result, "OK");
}
// Fire-and-forget: no return type needed (.NET 10)
private async void OnResetClicked(object sender, EventArgs e)
{
await hybridWebView.InvokeJavaScriptAsync("resetForm");
}
}Las excepciones JavaScript lanzadas durante InvokeJavaScriptAsync ahora se reenvían automáticamente a .NET como excepciones, eliminando las fallas silenciosas. La interceptación de solicitudes web permite modificar headers o proporcionar respuestas locales:
webView.WebResourceRequested += (s, e) =>
{
if (e.Uri.ToString().Contains("api/secure"))
{
e.Handled = true;
e.SetResponse(200, "OK", "application/json", GetCustomStream());
}
};APIs Obsoletas y Ruta de Migración
.NET MAUI 10 marca como obsoletas varias APIs. Las aplicaciones que utilizan estas APIs deben migrar a las alternativas recomendadas.
ListView y celdas relacionadas: ListView, EntryCell, ImageCell, SwitchCell, TextCell y ViewCell están obsoletos. Usar CollectionView en su lugar, que tiene mejor rendimiento y estabilidad con los nuevos handlers de iOS/Mac Catalyst.
MessagingCenter: Hecho interno en .NET 10. Reemplazar con WeakReferenceMessenger de CommunityToolkit.Mvvm.
Métodos de animación: FadeTo, RotateTo, ScaleTo y métodos similares están obsoletos. Usar los equivalentes async: FadeToAsync, RotateToAsync, ScaleToAsync.
TableView: Obsoleto en favor de CollectionView.
DisplayAlert/DisplayActionSheet: Reemplazados por DisplayAlertAsync y DisplayActionSheetAsync.
Migración de Xamarin.Forms a .NET MAUI
Xamarin.Forms llegó al fin de su soporte en mayo de 2024. La migración a MAUI implica cambios estructurales más allá de un simple reemplazo de namespaces. A continuación se presenta una lista de verificación de migración basada en conversiones de proyectos reales:
Xamarin.Forms no tiene soporte desde mayo de 2024. Las aplicaciones que todavía se ejecutan sobre Xamarin presentan riesgos de seguridad y compatibilidad. .NET MAUI 10 (LTS, con soporte hasta noviembre de 2028) es el objetivo de migración designado.
- Estructura del proyecto: Convertir los proyectos específicos de cada plataforma al modelo de proyecto único de MAUI. Mover el código compartido a la raíz, el código de plataforma bajo
Platforms/. - Namespaces: Reemplazar
Xamarin.FormsconMicrosoft.Maui.ControlsyXamarin.EssentialsconMicrosoft.Maui.Essentials(ahora integrado en MAUI). - Renderers a Handlers: Los renderers personalizados deben ser reescritos como handlers. La API de handlers es más simple pero la lógica de mapeo difiere.
- Inicio: Reemplazar la inicialización en
App.xaml.csconMauiProgram.csusando el patrón builder. - Paquetes NuGet: Muchos paquetes de la era Xamarin tienen equivalentes en MAUI. Verificar la compatibilidad antes de actualizar.
- Inyección de dependencias: MAUI usa
Microsoft.Extensions.DependencyInjectionde forma nativa. Reemplazar cualquier contenedor DI de terceros o llamadas aDependencyService.
El .NET Upgrade Assistant automatiza partes de los pasos 1-2, pero los handlers (paso 3) y los ajustes de lógica de negocio requieren trabajo manual.
Preguntas de Entrevista .NET MAUI Esenciales para 2026
Estas preguntas reflejan lo que los equipos de contratación preguntan en 2026, basándose en el ecosistema actual de .NET 10. Para más preparación de entrevistas C# y .NET, consultar la guía completa de entrevistas .NET.
¿En qué se diferencia la arquitectura de handlers de MAUI de los renderers de Xamarin.Forms?
Los renderers en Xamarin.Forms estaban fuertemente acoplados tanto al control multiplataforma como a la vista nativa, creando una dependencia bidireccional. Los handlers en MAUI son mapeadores sin estado: reciben notificaciones de cambio de propiedades y las aplican a la vista nativa mediante un diccionario de mappers. Este desacoplamiento significa que los handlers son más fáciles de probar, extender y reutilizar. Los diccionarios PropertyMapper y CommandMapper reemplazan el patrón de override OnElementPropertyChanged, haciendo que la personalización sea explícita en lugar de estar enterrada en sentencias switch.
¿Cuáles son las trampas de los tiempos de vida de DI específicos de MAUI?
MAUI soporta los tiempos de vida Singleton, Transient y Scoped, pero Scoped se comporta de manera diferente que en ASP.NET Core. No existe un límite de scope natural (como una solicitud HTTP). Un servicio Scoped registrado en MAUI actúa como un Singleton a menos que se creen scopes personalizados manualmente. Errores comunes: registrar un ViewModel como Singleton cuando contiene estado específico de la página (datos obsoletos al navegar), o registrar una conexión de base de datos como Transient (agotamiento del pool de conexiones). La regla general: los ViewModels son Transient, los servicios son Singleton, y Scoped se evita a menos que el ciclo de vida del scope se gestione explícitamente.
Al responder preguntas sobre inyección de dependencias, es importante demostrar comprensión de cómo el ciclo de vida de MAUI difiere del modelo de scope por solicitud de ASP.NET Core. Los entrevistadores buscan conocimiento sobre fugas de memoria y problemas de estado obsoleto específicos de aplicaciones móviles de larga ejecución.
¿Cómo difiere el binding compilado del binding basado en reflexión en MAUI?
Los bindings basados en reflexión resuelven las rutas de propiedades en tiempo de ejecución usando System.Reflection, lo cual es lento y produce errores en tiempo de ejecución ante errores de tipeo. Los bindings compilados, habilitados con x:DataType, resuelven las rutas de binding en tiempo de compilación. El compilador genera código de acceso directo a propiedades, omitiendo la reflexión por completo. Esto mejora el tiempo de inicio, reduce las asignaciones de memoria y detecta errores de binding durante la compilación. En .NET 10, el nuevo generador de código fuente XAML optimiza aún más al compilar XAML en tiempo de compilación en lugar de parsearlo en runtime.
¿Qué estrategias existen para compartir código entre una aplicación MAUI y un backend ASP.NET Core?
El enfoque recomendado utiliza una biblioteca de clases compartida que contiene DTOs, lógica de validación y reglas de negocio. Tanto la aplicación MAUI como el backend ASP.NET Core referencian esta biblioteca. .NET 10 refuerza este patrón con la nueva integración de .NET Aspire para MAUI, que proporciona descubrimiento de servicios y telemetría entre proyectos móviles y de backend. Los contratos compartidos usando generadores de código fuente de System.Text.Json aseguran consistencia en la serialización. La restricción clave: la biblioteca compartida debe apuntar a net10.0 (no a TFMs específicos de plataforma) para mantenerse portable.
¿Cuál es la diferencia entre HybridWebView y BlazorWebView en MAUI?
BlazorWebView aloja una aplicación Blazor completa dentro de la aplicación MAUI. Los componentes Razor se renderizan en una WebView incrustada, pero el runtime de .NET se ejecuta nativamente (no a través de WebAssembly). HybridWebView es más liviano: carga contenido HTML/CSS/JS estático y proporciona interoperabilidad de C# a JavaScript sin la sobrecarga del framework Blazor. La elección depende del caso de uso. BlazorWebView es adecuado para equipos con componentes Blazor existentes que desean reutilización de código. HybridWebView conviene en escenarios donde el contenido web existente (dashboards, mapas, editores) necesita integración nativa sin un framework completo.
Para profundizar en estos conceptos, se pueden practicar más preguntas de entrevista .NET en SharpSkill con ejercicios interactivos.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Sources
- What's new in .NET MAUI for .NET 10: documentación oficial que cubre todas las características de .NET 10 MAUI
- .NET MAUI GitHub Releases: notas de versión de las releases de servicio 10.0.x
- Edge-to-Edge Is Now Mandatory in Android 16: guía de migración para los requisitos de Android 16
- MAUI GitHub Discussion #34171: comentarios de la comunidad sobre la estabilidad de .NET MAUI 10
Lo que los Desarrolladores .NET MAUI 10 Deben Recordar
- .NET MAUI 10 es una versión LTS (soporte hasta noviembre de 2028) enfocada en estabilidad y rendimiento, convirtiéndola en una opción confiable para aplicaciones multiplataforma en producción
- Android 16 hace obligatorio el edge-to-edge: agregar
SafeAreaEdges="Container"como estilo global en App.xaml para restaurar el comportamiento de .NET 9 - El generador de código fuente XAML (
MauiXamlInflator=SourceGen) compila XAML en tiempo de compilación, mejorando el rendimiento de inicio y detectando errores más temprano - ListView, TableView y MessagingCenter están obsoletos: migrar a CollectionView y WeakReferenceMessenger
- La arquitectura de handlers reemplaza los renderers de Xamarin con mapeadores sin estado, mejorando la testabilidad mediante
PropertyMapperyCommandMapper - Los generadores de código fuente de CommunityToolkit.Mvvm eliminan la mayor parte del código repetitivo MVVM: los atributos
[ObservableProperty]y[RelayCommand]reemplazan las implementaciones manuales - HybridWebView en .NET 10 agrega llamadas JavaScript fire-and-forget, eventos de inicialización e interceptación de solicitudes para la integración nativa-web
- La preparación para entrevistas debe enfocarse en la arquitectura de handlers vs renderers, las trampas de tiempos de vida de DI en aplicaciones de larga ejecución, bindings compilados y las diferencias entre HybridWebView y BlazorWebView
¿Sabrías detectar el bug en .NET?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 14 de septiembre de 2026
Etiquetas
Compartir
Artículos relacionados

.NET 10 en 2026: Nuevas Funcionalidades, AOT Nativo y C# 14 para Preparación de Entrevistas
.NET 10 es la nueva versión LTS con mejoras en Native AOT, extension members de C# 14, el keyword field y aplicaciones basadas en archivos. Guía completa de nuevas funcionalidades, mejoras de rendimiento y conocimientos para entrevistas técnicas .NET en 2026.

Clean Architecture .NET en 2026: CQRS, MediatR y Preguntas de Entrevista para Desarrolladores
Clean Architecture en .NET con CQRS y MediatR 14. Domina los patrones en capas, pipeline behaviors y prepárate para entrevistas de desarrollador senior.

Tiempo de Vida de DbContext en ASP.NET Core: Rendimiento vs Seguridad de Hilos en Operaciones Async
Domina la gestión del tiempo de vida de DbContext en ASP.NET Core. Aprende cuándo usar scoped vs transient, cómo manejar operaciones async de forma segura y optimizar el rendimiento con pooling de DbContext.