Introducción a XAML

Browse sample.Examina la muestra

En una aplicación de la interfaz de usuario de aplicaciones multiplataforma de .NET (.NET MAUI), XAML se usa principalmente para definir el contenido visual de una página y funciona junto con un archivo de código subyacente de C#. El archivo de código subyacente proporciona compatibilidad de código para el marcado. Juntos, estos dos archivos contribuyen a una nueva definición de clase que incluye vistas secundarias e inicialización de propiedades. Dentro del archivo XAML, se hace referencia a clases y propiedades con elementos y atributos XML, y se establecen vínculos entre el marcado y el código.

Anatomía de un archivo XAML

Una nueva aplicación .NET MAUI contiene tres archivos XAML y sus archivos de código subyacente asociados:

Screenshot of the structure of a new .NET MAUI app.

El primer emparejamiento de archivos es App.xaml un archivo de XAML y App.xaml.cs, un archivo de código subyacente de C# asociado al archivo XAML. Tanto App.xaml como App.xaml.cs contribuyen a una clase denominada App que deriva de Application. El segundo emparejamiento de archivos es AppShell.xaml y AppShell.xaml.cs, que contribuyen a una clase denominada AppShell que deriva de Shell. La mayoría de las otras clases con archivos XAML contribuyen a una clase que deriva de ContentPage, y definen la interfaz de usuario de una página. Esto es cierto en los archivos MainPage.xaml y MainPage.xaml.cs.

El archivo MainPage.xaml tiene la siguiente estructura:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyMauiApp.MainPage">
    ...
</ContentPage>

Las dos declaraciones de espacio de nombres XML (xmlns) hacen referencia a URI de microsoft.com. Sin embargo, no hay contenido en estos URI y básicamente funcionan como identificadores de versión.

La primera declaración de espacio de nombres XML significa que las etiquetas definidas en el archivo XAML sin prefijo hacen referencia a clases de .NET MAUI, por ejemplo ContentPage. La segunda declaración de espacio de nombres define un prefijo de x. Se usa para varios elementos y atributos que son intrínsecos de XAML, y que son compatibles con otras implementaciones de XAML. No obstante, estos elementos y atributos son ligeramente diferentes en función del año incorporado en el URI. .NET MAUI es compatible con la especificación XAML 2009.

Al final de la primera etiqueta, el prefijo x se usa para un atributo denominado Class. Dado que el uso de este prefijo x es prácticamente universal para el espacio de nombres XAML, los atributos XAML, como Class, casi siempre se denominan x:Class. El atributo x:Class especifica un nombre de clase .NET completo: la clase MainPage del espacio de nombres MyMauiApp. Esto significa que este archivo XAML define una nueva clase denominada MainPage en el espacio de nombres MyMauiApp que deriva de ContentPage (la etiqueta en la que aparece el atributo x:Class).

El atributo x:Class solo puede aparecer en el elemento raíz de un archivo XAML para definir una clase derivada de C#. Es la única clase nueva definida en el archivo XAML. Para todo lo demás que aparece en un archivo XAML, simplemente se crea una instancia de las clases existentes y se inicializa.

El archivo MainPage.xaml.cs tiene un aspecto similar a este:

namespace MyMauiApp;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

La MainPage clase deriva de ContentPage y es una definición de clase parcial.

Cuando Visual Studio compila el proyecto, un generador de origen genera un nuevo origen de C# que contiene la definición del método InitializeComponent al que se llama desde el constructor MainPage y lo agrega al objeto de compilación.

En tiempo de ejecución, el código de la clase MauiProgram arranca la aplicación y ejecuta el constructor de la clase App, que crea una instancia de AppShell. La clase AppShell crea una instancia de la primera página de la aplicación que se va a mostrar, que es MainPage. El constructor MainPage llama a InitializeComponent, que inicializa todos los objetos definidos en el archivo XAML, los conecta todos juntos en relaciones de elementos primarios y secundarios, adjunta los controladores de eventos definidos en código a los eventos establecidos en el archivo XAML ,y establece el árbol resultante de objetos como el contenido de la página.

Nota:

La clase AppShell usa el shell de .NET MAUI para establecer la primera página de la aplicación que se va a mostrar. Sin embargo, el shell está fuera del ámbito de esta introducción a XAML. Para más información, consulta Shell de .NET MAUI.

Establecimiento del contenido de la página

Un objeto ContentPage debe contener un solo elemento secundario, que puede ser una vista o un diseño con vistas secundarias. El elemento secundario de ContentPage se establece automáticamente como el valor de la propiedad ContentPage.Content.

En el siguiente ejemplo se muestra un objeto ContentPage que contiene un objeto Label:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.HelloXamlPage"
             Title="Hello XAML Page">
    <Label Text="Hello, XAML!"
           VerticalOptions="Center"
           HorizontalTextAlignment="Center"
           Rotation="-15"
           FontSize="18"
           FontAttributes="Bold"
           TextColor="Blue" />
</ContentPage>

En el ejemplo anterior, la relación entre clases, propiedades y XML debe ser evidente. Una clase de .NET MAUI (como ContentPage o Label) aparece en el archivo XAML como un elemento XML. Las propiedades de esa clase, incluyendo Title en ContentPage y siete propiedades de Label normalmente aparecen como atributos XML.

Existen muchos métodos abreviados para establecer los valores de estas propiedades. Algunas propiedades son tipos de datos básicos. Por ejemplo, las propiedades Title y Text son del tipo string, y Rotation es de tipo double. La propiedad HorizontalTextAlignment es de tipo TextAlignment, que es una enumeración. Para una propiedad de cualquier tipo de enumeración, todo lo que debes suministrar es un nombre de miembro.

Sin embargo, para las propiedades de tipos más complejos, se usan convertidores para analizar el XAML. Estas son clases de .NET MAUI que derivan de TypeConverter. En el ejemplo anterior, se aplican automáticamente varios convertidores .NET MAUI para convertir valores de cadena en su tipo correcto:

  • LayoutOptionsConverter para la propiedad VerticalOptions. Este convertidor convierte los nombres de los campos estáticos públicos de la estructura LayoutOptions en valores de tipo LayoutOptions.
  • ColorTypeConverter para la propiedad TextColor. Este convertidor convierte los nombres de los campos estáticos públicos de la clase Colors o valores RGB hexadecimales, con o sin un canal alfa.

Cuando ejecutas una aplicación .NET MAUI, normalmente aparece el objeto MainPage. Para ver una página diferente, puedes establecerla como la nueva página de inicio en el archivo AppShell.xaml, o ir a la nueva página desde MainPage.

Para implementar la navegación, en el constructor MainPage.xaml.cs, puedes crear un objeto Button sencillo y usar el controlador de eventos para ir a HelloXamlPage:

public MainPage()
{
    InitializeComponent();

    Button button = new Button
    {
        Text = "Navigate!",
        HorizontalOptions = LayoutOptions.Center,
        VerticalOptions = LayoutOptions.Center
    };

    button.Clicked += async (sender, args) =>
    {
        await Navigation.PushAsync(new HelloXamlPage());
    };

    Content = button;
}

Al compilar e implementar la nueva versión de esta aplicación, aparece un botón en la pantalla. Al presionar, va a HelloXamlPage:

Screenshot of rotated Label text.

Puedes volver a MainPage mediante la barra de navegación que aparece en todas las plataformas.

Nota:

Una alternativa a este modelo de navegación es usar el shell de .NET MAUI. Para obtener más información, consulta Información general de .NET MAUI Shell.

Interacciones de código y XAML

El elemento secundario de la mayoría de los derivados de ContentPage es un diseño, como StackLayout o Grid, y el diseño puede contener varios elementos secundarios. En XAML, estas relaciones entre elementos primarios y secundarios se establecen con la jerarquía XML normal:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
    <StackLayout>
        <Slider VerticalOptions="Center" />
        <Label Text="A simple Label"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="Center" />
    </StackLayout>
</ContentPage>

Este archivo XAML se completa sintácticamente y produce la siguiente interfaz de usuario:

Screenshot of multiple controls on a page.

Sin embargo, aunque se puede interactuar con Slider y Button, la interfaz de usuario no se actualiza. El objeto Slider debe hacer que el objeto Label muestre el valor actual, y el objeto Button debe hacer algo.

Mostrar un valor Slider con un objeto Label se puede lograr completamente en XAML con un enlace de datos. Sin embargo, es útil ver primero la solución de código. Aun así, controlar el clic Button requiere código. Esto significa que el archivo de código subyacente para XamlPlusCodePage debe contener controladores para el evento ValueChanged de Slider y el evento Clicked de Button:

namespace XamlSamples
{
    public partial class XamlPlusCodePage
    {
        public XamlPlusCodePage()
        {
            InitializeComponent();
        }

        void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
        {
            valueLabel.Text = args.NewValue.ToString("F3");
        }

        async void OnButtonClicked(object sender, EventArgs args)
        {
            Button button = (Button)sender;
            await DisplayAlert("Clicked!", "The button labeled '" + button.Text + "' has been clicked", "OK");
        }
    }
}

De vuelta en el archivo XAML, las etiquetas Slider y Button deben incluir atributos para los eventos ValueChanged y Clicked que hacen referencia a estos controladores:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
    <StackLayout>
        <Slider VerticalOptions="Center"
                ValueChanged="OnSliderValueChanged" />
        <Label x:Name="valueLabel"
               Text="A simple Label"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Clicked="OnButtonClicked" />
    </StackLayout>
</ContentPage>

Observa que la asignación de un controlador a un evento tiene la misma sintaxis que asignar un valor a una propiedad. Además, para que el controlador de eventos ValueChanged de Slider use Label para mostrar el valor actual, el controlador debe hacer referencia a ese objeto desde el código. Por lo tanto, Label necesita un nombre, que se especifica con el atributo x:Name. El prefijo x del atributo x:Name indica que este atributo es intrínseco de XAML. El nombre que asignes al atributo x:Name tiene las mismas reglas que los nombres de variables de C#. Por ejemplo, debe comenzar con una letra o un guion bajo y no contener espacios insertados.

El controlador de eventos ValueChanged ahora puede establecer Label para mostrar el nuevo valor Slider, que está disponible en los argumentos del evento:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = args.NewValue.ToString("F3");
}

Como alternativa, el controlador podría obtener el objeto Slider que genera este evento a partir del argumento sender y obtiene la propiedad Value a partir de él:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = ((Slider)sender).Value.ToString("F3");
}

El resultado es que cualquier manipulación de Slider causa que su valor se muestre en Label:

Screenshot of multiple controls on a page, with Slider value displayed.

En el ejemplo anterior, Button simula una respuesta a un evento Clicked mostrando una alerta con el objeto Text del botón. Por lo tanto, el controlador de eventos puede convertir el argumento sender en Button y después acceder a sus propiedades:

async void OnButtonClicked(object sender, EventArgs args)
{
    Button button = (Button)sender;
    await DisplayAlert("Clicked!", "The button labeled '" + button.Text + "' has been clicked", "OK");
}

El método OnButtonClicked se define en async porque el método DisplayAlert es asincrónico y debe ir precedido del operador await, que devuelve cuando el método se completa. Debido a que este método obtiene el objeto Button que activa el evento a partir del argumento sender, se podría usar el mismo controlador para varios botones.

Pasos siguientes

XAML está diseñado principalmente para crear instancias e inicializar objetos. Pero a menudo, las propiedades deben establecerse en objetos complejos que no se pueden representar fácilmente como cadenas XML y, a veces, las propiedades definidas por una clase deben establecerse en una clase secundaria. Estas dos necesidades requieren las características esenciales de sintaxis XAML de elementos de propiedad y propiedades adjuntas.