Compartir a través de


Introducción a XAML

En este artículo se describen las características del lenguaje XAML y se muestra cómo puedes usar XAML para escribir aplicaciones de Windows Presentation Foundation (WPF). En este artículo se describe específicamente XAML como se implementa en WPF. XAML es un concepto de lenguaje más grande que WPF.

Qué es XAML

XAML es un lenguaje declarativo de marcado. Como se aplica al modelo de programación de .NET, XAML simplifica la creación de una interfaz de usuario para una aplicación .NET. Puedes crear elementos de interfaz de usuario visibles en el marcado XAML declarativo y, a continuación, separar la definición de la interfaz de usuario de la lógica en tiempo de ejecución mediante archivos de código subyacente que están unidos al marcado a través de definiciones de clase parciales. XAML representa directamente la creación de instancias de objetos en un conjunto específico de tipos subyacentes definidos en ensamblajes. Esto es diferente a la mayoría de los otros lenguajes de marcado, que suelen ser un lenguaje que suele ser interpretado sin un vínculo directo a un sistema de tipos subyacente. XAML habilita un flujo de trabajo en el que las partes independientes pueden trabajar en la interfaz de usuario y en la lógica de una aplicación, con herramientas potencialmente diferentes.

Cuando se representa como texto, los archivos XAML son archivos XML que generalmente tienen la .xaml extensión. Los archivos se pueden codificar mediante cualquier codificación XML, pero la codificación como UTF-8 es habitual.

En el ejemplo siguiente se muestra cómo crear un botón como parte de una interfaz de usuario. Este ejemplo está pensado para darle un sabor a cómo XAML representa metáforas comunes de programación de la interfaz de usuario (no es una muestra completa).

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

Sintaxis XAML en breve

En las secciones siguientes se explican las formas básicas de sintaxis XAML y se proporciona un ejemplo de marcado corto. Estas secciones no están pensadas para proporcionar información completa sobre cada forma de sintaxis, como cómo se representan en el sistema de tipos subyacente. Para obtener más información sobre los detalles de la sintaxis XAML, consulta Detalles de la sintaxis XAML.

Gran parte del material de las secciones siguientes será elemental para usted si tiene conocimientos previos con el lenguaje XML. Esto es una consecuencia de uno de los principios básicos de diseño de XAML. El lenguaje XAML define los conceptos propios, pero estos conceptos funcionan dentro del lenguaje XML y el formulario de marcado.

Elementos de objeto XAML

Normalmente, un elemento de objeto declara una instancia de un tipo. Ese tipo se define en los ensamblados a los que hace referencia la tecnología que usa XAML como lenguaje.

La sintaxis del elemento de objeto siempre comienza con un corchete angular de apertura (<). Esto va seguido del nombre del tipo en el que desea crear una instancia. (El nombre puede incluir un prefijo, un concepto que se explicará más adelante). Después de esto, puede declarar opcionalmente atributos en el elemento object. Para completar la etiqueta del elemento de objeto, finalice con un corchete angular de cierre (>). En su lugar, puede usar un formulario de autocierre que no tenga contenido, cerrando la etiqueta con una barra diagonal seguida de un corchete angular de cierre (/>). Por ejemplo, examine de nuevo el fragmento de código de marcado mostrado anteriormente.

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

Esto especifica dos elementos de objeto: <StackPanel> (con contenido y una etiqueta de cierre más adelante) y <Button .../> (el formulario de autocierre, con varios atributos). Los elementos StackPanel de objeto y Button cada uno se asignan al nombre de una clase definida por WPF y forman parte de los ensamblados de WPF. Al especificar una etiqueta de elemento de objeto, se crea una instrucción para el procesamiento XAML para crear una nueva instancia del tipo subyacente. Cada instancia se crea llamando al constructor sin parámetros del tipo subyacente al analizar y cargar el XAML.

Sintaxis de atributo (propiedades)

Las propiedades de un objeto se pueden expresar a menudo como atributos del elemento de objeto. La sintaxis de atributo asigna un nombre a la propiedad de objeto que se va a establecer, seguida del operador de asignación (=). El valor de un atributo siempre se especifica como una cadena contenida entre comillas.

La sintaxis de atributo es la sintaxis de configuración de propiedades más simplificada y es la sintaxis más intuitiva que se usará para los desarrolladores que han usado lenguajes de marcado en el pasado. Por ejemplo, el marcado siguiente crea un botón que tiene texto rojo y un fondo azul con un texto para mostrar de Content.

<Button Background="Blue" Foreground="Red" Content="This is a button"/>

Sintaxis del elemento Property

Para algunas propiedades de un elemento de objeto, no es posible la sintaxis de atributo, ya que el objeto o la información necesarios para proporcionar el valor de la propiedad no se pueden expresar adecuadamente dentro de las comillas y limitaciones de cadena de texto de la sintaxis de atributo. En estos casos, se puede usar una sintaxis diferente conocida como sintaxis de elemento de propiedad.

La sintaxis de la etiqueta de inicio del elemento de propiedad es <TypeName.PropertyName>. Por lo general, el contenido de esa etiqueta es un elemento de objeto del tipo que la propiedad toma como su valor. Después de especificar el contenido, debe cerrar el elemento de propiedad con una etiqueta final. La sintaxis de la etiqueta final es </TypeName.PropertyName>.

Si es posible una sintaxis de atributo, el uso de la sintaxis de atributo suele ser más conveniente y permite un marcado más compacto, pero suele ser solo una cuestión de estilo, no una limitación técnica. En el ejemplo siguiente se muestran las mismas propiedades que se establecen en el ejemplo de sintaxis de atributo anterior, pero esta vez mediante la sintaxis de elemento de propiedad para todas las propiedades de Button.

<Button>
    <Button.Background>
        <SolidColorBrush Color="Blue"/>
    </Button.Background>
    <Button.Foreground>
        <SolidColorBrush Color="Red"/>
    </Button.Foreground>
    <Button.Content>
        This is a button
    </Button.Content>
</Button>

Sintaxis de colecciones

El lenguaje XAML incluye algunas optimizaciones que producen un marcado más legible. Una de estas optimizaciones es que si una propiedad determinada toma un tipo de colección, los elementos que declaras en el marcado como elementos hijos dentro del valor de esa propiedad pasan a formar parte de la colección. En este caso, una colección de objetos hijos es el valor que se asigna a la propiedad de colección.

En el ejemplo siguiente se muestra la sintaxis de colección para establecer valores de la GradientStops propiedad .

<LinearGradientBrush>
    <LinearGradientBrush.GradientStops>
        <!-- no explicit new GradientStopCollection, parser knows how to find or create -->
        <GradientStop Offset="0.0" Color="Red" />
        <GradientStop Offset="1.0" Color="Blue" />
    </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

Propiedades de contenido XAML

XAML especifica una característica de lenguaje por la que una clase puede designar exactamente una de sus propiedades para que sea la propiedad de contenido XAML. Los elementos secundarios de ese elemento de objeto se usan para establecer el valor de esa propiedad de contenido. En otras palabras, para la propiedad de contenido exclusivamente, puedes omitir un elemento de propiedad al establecer esa propiedad en el marcado XAML y generar una metáfora padre/hijo más visible en el marcado.

Por ejemplo, Border especifica una propiedad de contenido de Child. Los dos Border elementos siguientes se tratan de forma idéntica. La primera aprovecha la sintaxis de la propiedad de contenido y omite el elemento de la propiedad Border.Child. La segunda muestra Border.Child explícitamente.

<Border>
    <TextBox Width="300"/>
</Border>
<!--explicit equivalent-->
<Border>
    <Border.Child>
        <TextBox Width="300"/>
    </Border.Child>
</Border>

Como regla del lenguaje XAML, el valor de una propiedad de contenido XAML debe proporcionarse completamente antes o completamente después de cualquier otro elemento de propiedad en ese elemento de objeto. Por ejemplo, el siguiente código no se compila.

<Button>I am a
  <Button.Background>Blue</Button.Background>
  blue button</Button>

Para obtener más información sobre los detalles de la sintaxis XAML, consulta Detalles de la sintaxis XAML.

Contenido de texto

Un pequeño número de elementos XAML puede procesar directamente el texto como su contenido. Para habilitar esto, uno de los siguientes casos debe ser verdadero.

  • La clase debe declarar una propiedad de contenido y esa propiedad de contenido debe ser de un tipo asignable a una cadena (el tipo podría ser Object). Por ejemplo, cualquier ContentControl utiliza Content como propiedad de contenido y es de tipo Object, y admite el uso siguiente en un ContentControl como Button: <Button>Hello</Button>.

  • El tipo debe declarar un convertidor de tipos, en cuyo caso el contenido de texto se usa como texto de inicialización para ese convertidor de tipos. Por ejemplo, <Brush>Blue</Brush> convierte el valor de contenido de Blue en un pincel. Este caso es menos común en la práctica.

  • El tipo debe ser un primitivo de lenguaje XAML conocido.

Propiedades de contenido y sintaxis de colección combinadas

Considere este ejemplo.

<StackPanel>
    <Button>First Button</Button>
    <Button>Second Button</Button>
</StackPanel>

Aquí, cada uno Button es un elemento secundario de StackPanel. Se trata de un marcado simplificado e intuitivo que omite dos etiquetas por dos razones diferentes.

  • Elemento omitido de la propiedad StackPanel.Children:StackPanel proviene de Panel. Panel define Panel.Children como su propiedad de contenido XAML.

  • Elemento de objeto UIElementCollection omitido: La propiedad Panel.Children es de tipo UIElementCollection, que implementa IList. Se puede omitir la etiqueta de elemento de la colección, en función de las reglas XAML para procesar colecciones como IList. (En este caso, UIElementCollection en realidad no se puede crear una instancia porque no expone un constructor sin parámetros, y por eso el elemento de objeto UIElementCollection se muestra como comentario).

<StackPanel>
    <StackPanel.Children>
        <!--<UIElementCollection>-->
        <Button>First Button</Button>
        <Button>Second Button</Button>
        <!--</UIElementCollection>-->
    </StackPanel.Children>
</StackPanel>

Sintaxis de atributo (eventos)

La sintaxis de atributo también se puede usar para los miembros que son eventos en lugar de propiedades. En este caso, el nombre del atributo es el nombre del evento. En la implementación de WPF de eventos para XAML, el valor del atributo es el nombre de un controlador que implementa el delegado de ese evento. Por ejemplo, el marcado siguiente asigna un controlador para el Click evento a un Button creado en el marcado:

<Button Click="Button_Click" >Click Me!</Button>

Hay más eventos y XAML en WPF que solo en este ejemplo de la sintaxis de atributo. Por ejemplo, puede preguntarse qué representa el ClickHandler elemento al que se hace referencia aquí y cómo se define. Esto se explicará en la próxima sección Eventos y código subyacente xaml de este artículo.

Mayúsculas y minúsculas y espacios en blanco en XAML

En general, XAML distingue mayúsculas de minúsculas. Para resolver tipos de respaldo, XAML de WPF distingue mayúsculas de minúsculas por las mismas reglas que clR distingue entre mayúsculas y minúsculas. Los elementos de objeto, los elementos de propiedad y los nombres de atributo deben especificarse mediante el uso de mayúsculas y minúsculas cuando se comparan por nombre con el tipo subyacente del ensamblado o con un miembro de un tipo. Las palabras clave del lenguaje XAML y los primitivos también distinguen entre las mayúsculas y las minúsculas. Los valores no siempre distinguen mayúsculas de minúsculas. La distinción entre mayúsculas y minúsculas de los valores dependerá del comportamiento del convertidor de tipos asociado con la propiedad a la que se aplica el valor, o del tipo del valor de la propiedad. Por ejemplo, las propiedades que toman el tipo Boolean pueden tomar true o True como valores equivalentes, pero solo porque la conversión nativa del analizador XAML de WPF de cadena a Boolean ya permite estos valores como equivalentes.

Los procesadores y serializadores XAML de WPF omitirán o quitarán todos los espacios en blanco no asignados y normalizarán cualquier espacio en blanco significativo. Esto es coherente con las recomendaciones de comportamiento del espacio en blanco predeterminadas de la especificación XAML. Este comportamiento solo es consecuencia cuando se especifican cadenas dentro de las propiedades de contenido XAML. En términos más sencillos, XAML convierte los caracteres de espacio, avance de línea y tabulación en espacios y, a continuación, conserva un espacio si se encuentra en cualquier extremo de una cadena contigua. La explicación completa sobre el manejo de espacios en blanco en XAML no está cubierta en este artículo. Para obtener más información, consulta Procesamiento de espacios en blanco en XAML.

Extensiones de marcado

Las extensiones de marcado son un concepto de lenguaje XAML. Cuando se usa para proporcionar el valor de una sintaxis de atributo, las llaves ({ y }) indican un uso de extensión de marcado. Este uso orienta el procesamiento XAML para evitar el tratamiento general de los valores de los atributos como cadenas literales o valores convertibles a cadenas.

Las extensiones de marcado más comunes que se utilizan en la programación de aplicaciones WPF son Binding, que se emplean para las expresiones de enlace de datos, y las referencias de recursos StaticResource y DynamicResource. Mediante el uso de extensiones de marcado, puede usar la sintaxis de atributo para proporcionar valores para las propiedades incluso si esa propiedad no admite una sintaxis de atributo en general. Las extensiones de marcado suelen usar tipos de expresión intermedios para habilitar características como aplazar valores o hacer referencia a otros objetos que solo están presentes en tiempo de ejecución.

Por ejemplo, el marcado siguiente establece el valor de la propiedad Style mediante sintaxis de atributo. La propiedad Style acepta una instancia de la clase Style, que por defecto no puede ser instanciada mediante una cadena de sintaxis de atributo. Pero en este caso, el atributo hace referencia a una extensión de marcado determinada, StaticResource. Cuando se procesa esa extensión de marcado, devuelve una referencia a un estilo que se creó anteriormente como un recurso con clave en un diccionario de recursos.

<Window x:Class="index.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="100" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
        <Style TargetType="Border" x:Key="PageBackground">
            <Setter Property="BorderBrush" Value="Blue"/>
            <Setter Property="BorderThickness" Value="5" />
        </Style>
    </Window.Resources>
    <Border Style="{StaticResource PageBackground}">
        <StackPanel>
            <TextBlock Text="Hello" />
        </StackPanel>
    </Border>
</Window>

Para obtener una lista de referencia de todas las extensiones de marcado para XAML implementadas específicamente en WPF, consulta Extensiones XAML de WPF. Para obtener una lista de referencia de las extensiones de marcado definidas por System.Xaml y que están más disponibles para las implementaciones XAML de .NET, consulta Características del lenguaje de espacio de nombres XAML (x:). Para obtener más información sobre los conceptos de la extensión de marcado, consulta Extensiones de marcado y XAML de WPF.

Convertidores de tipos

En la sección Sintaxis XAML en Breve , se indicó que el valor del atributo debe ser capaz de establecerlo mediante una cadena. El control nativo básico de cómo se convierten las cadenas en otros tipos de objeto o valores primitivos se basa en el tipo String en sí mismo, junto con el procesamiento nativo para ciertos tipos, como DateTime o Uri. Pero muchos tipos de WPF o sus miembros amplían el comportamiento básico de procesamiento de atributos de cadena, permitiendo que las instancias de objetos más complejos se especifiquen como cadenas y atributos.

La Thickness estructura es un ejemplo de un tipo que tiene habilitada una conversión de tipos para usos XAML. Thickness indica medidas dentro de un rectángulo anidado y se usa como valor para propiedades como Margin. Al colocar un convertidor de tipos en Thickness, todas las propiedades que usan un Thickness son más fáciles de especificar en XAML porque se pueden especificar como atributos. En el ejemplo siguiente se usa una conversión de tipos y sintaxis de atributo para proporcionar un valor para : Margin

<Button Margin="10,20,10,30" Content="Click me"/>

El ejemplo de sintaxis de atributo anterior es equivalente al siguiente ejemplo de sintaxis más detallada, donde Margin se establece mediante la sintaxis de elemento de propiedad que contiene un elemento de objeto Thickness. Las cuatro propiedades clave de Thickness se establecen como atributos en la nueva instancia:

<Button Content="Click me">
    <Button.Margin>
        <Thickness Left="10" Top="20" Right="10" Bottom="30"/>
    </Button.Margin>
</Button>

Nota:

También hay un número limitado de objetos en los que la conversión de tipos es la única manera pública de establecer una propiedad en ese tipo sin incluir una subclase, ya que el propio tipo no tiene un constructor sin parámetros. Un ejemplo es Cursor.

Para obtener más información sobre la conversión de tipos, consulta TypeConverters y XAML.

Elementos raíz y espacios de nombres

Un archivo XAML solo debe tener un elemento raíz, para que sea un archivo XML bien formado y un archivo XAML válido. Para escenarios típicos de WPF, se usa un elemento raíz que tiene un significado destacado en el modelo de aplicación de WPF (por ejemplo, Window o Page para una página, ResourceDictionary para un diccionario externo o Application para la definición de la aplicación). En el ejemplo siguiente se muestra el elemento raíz de un archivo XAML típico para una página de WPF, con el elemento raíz de Page.

<Page x:Class="index.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Page1">

</Page>

El elemento raíz también contiene los atributos xmlns y xmlns:x. Estos atributos indican al procesador XAML cuáles espacios de nombres XAML contienen las definiciones de tipo para los tipos subyacentes que el marcado referenciará como elementos. El xmlns atributo indica específicamente el espacio de nombres XAML predeterminado. Dentro del espacio de nombres XAML predeterminado, los elementos de objeto del marcado se pueden especificar sin prefijo. Para la mayoría de los escenarios de aplicaciones de WPF, y para casi todos los ejemplos proporcionados en las secciones de WPF del SDK, el espacio de nombres predeterminado de XAML se asigna al espacio de nombres WPF http://schemas.microsoft.com/winfx/2006/xaml/presentation. El xmlns:x atributo indica un espacio de nombres XAML adicional, que asigna el espacio de nombres http://schemas.microsoft.com/winfx/2006/xaml del lenguaje XAML.

Este uso de xmlns para definir un ámbito para el uso y la asignación de un ámbito de nombres es coherente con la especificación XML 1.0. Los ámbitos de nombres XAML son diferentes de los ámbitos de nombres XML solo en que un ámbito de nombres XAML también implica algo sobre cómo los elementos del ámbito de nombres están respaldados por tipos cuando se trata de la resolución de tipos y el análisis del XAML.

Los xmlns atributos solo son estrictamente necesarios en el elemento raíz de cada archivo XAML. xmlns las definiciones se aplicarán a todos los elementos descendientes del elemento raíz (este comportamiento es coherente de nuevo con la especificación XML 1.0 para xmlns). Los atributos xmlns también se permiten en otros elementos en lugar de la raíz, y se aplicarían a los elementos descendientes del elemento de definición. Sin embargo, la definición frecuente o la redefinición de los espacios de nombres XAML pueden dar lugar a un estilo de marcado XAML que es difícil de leer.

La implementación de WPF de su procesador de XAML incluye una infraestructura que está familiarizada con las bibliotecas principales de WPF. Los ensamblados principales de WPF se sabe que contienen los tipos que admiten las asignaciones de WPF al espacio de nombres XAML predeterminado. Esto se habilita a través de la configuración que forma parte del archivo de compilación del proyecto y los sistemas de compilación y proyecto de WPF. Por lo tanto, declarar el espacio de nombres XAML predeterminado xmlns es todo lo necesario para hacer referencia a elementos XAML que proceden de ensamblados WPF.

El prefijo x:

En el ejemplo anterior del elemento raíz, el prefijo x: se usó para asignar el espacio de nombres http://schemas.microsoft.com/winfx/2006/xamlXAML , que es el espacio de nombres XAML dedicado que admite construcciones de lenguaje XAML. Este x: prefijo se utiliza para mapear este espacio de nombres XAML en las plantillas de proyectos, en ejemplos y en la documentación de este SDK. El espacio de nombres XAML para el lenguaje XAML contiene varias construcciones de programación que usarás con frecuencia en tu XAML. A continuación se muestra una lista de las construcciones de programación de prefijos más comunes x: que usará:

  • x:Key: Establece una clave única para cada recurso dentro de un ResourceDictionary o en conceptos de diccionario similares en otros marcos. x:Key probablemente supondrá el 90 % de los usos de x: que encontrará en el marcado típico de una aplicación WPF.

  • x:Class: especifica el espacio de nombres CLR y el nombre de clase de la clase que proporciona código subyacente para una página XAML. Debe tener esta clase para admitir código subyacente según el modelo de programación de WPF y, por lo tanto, casi siempre verá x: asignado, incluso si no hay recursos.

  • x:Name: especifica un nombre de objeto en tiempo de ejecución para la instancia que existe en el código en tiempo de ejecución después de procesar un elemento de objeto. En general, usará con frecuencia una propiedad equivalente definida por WPF para x:Name. Estas características se asignan específicamente a una propiedad de respaldo de Common Language Runtime (CLR) y, por lo tanto, son más convenientes para la programación de aplicaciones, donde frecuentemente se usa código durante la ejecución para encontrar los elementos con nombre de XAML inicializados. La propiedad de este tipo más común es FrameworkElement.Name. Puede seguir usando x:Name cuando la propiedad de nivel Name de marco de WPF equivalente no se admite en un tipo determinado. Esto ocurre en determinados escenarios de animación.

  • x:Static: habilita una referencia que devuelve un valor estático que no es una propiedad compatible con XAML.

  • x:Type: construye una Type referencia basada en un nombre de tipo. Se usa para especificar atributos que toman Type, como Style.TargetType, aunque con frecuencia la propiedad tiene una conversión nativa de cadena a Type de tal manera que el uso de la extensión de marcado x:Type es opcional.

Hay construcciones de programación adicionales en el x: espacio de nombres prefijo/XAML, que no son tan comunes. Para obtener más información, consulta Características del lenguaje de espacio de nombres XAML (x:).

Prefijos personalizados y tipos personalizados

Para sus propios ensamblados personalizados o para ensamblados fuera del núcleo WPF de PresentationCore, PresentationFramework y WindowsBase, puede especificar el ensamblado como parte de una asignación xmlns personalizada. A continuación, puedes hacer referencia a tipos de ese ensamblado en tu XAML, siempre que ese tipo se haya implementado correctamente para admitir los usos de XAML que estás intentando.

A continuación se muestra un ejemplo básico de cómo funcionan los prefijos personalizados en el marcado XAML. El prefijo custom se define en la etiqueta de elemento raíz y se asigna a un ensamblado específico que se empaqueta y está disponible con la aplicación. Este ensamblado contiene un tipo NumericUpDown, que se implementa para admitir el uso general de XAML, así como el uso de una herencia de clases que permite su inserción en este punto determinado en un modelo de contenido XAML de WPF. Una instancia de este NumericUpDown control se declara como un elemento de objeto, usando el prefijo para que un analizador XAML sepa qué espacio de nombres XAML contiene el tipo y, por tanto, dónde está el ensamblado de respaldo que contiene la definición de tipo.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:NumericUpDownCustomControl;assembly=CustomLibrary"
    >
  <StackPanel Name="LayoutRoot">
    <custom:NumericUpDown Name="numericCtrl1" Width="100" Height="60"/>
...
  </StackPanel>
</Page>

Para obtener más información sobre los tipos personalizados en XAML, consulta CLASES PERSONALIZADAS y XAML para WPF.

Para obtener más información sobre cómo están relacionados los espacios de nombres XML y los espacios de nombres de código en los ensamblados, consulta Espacios de nombres XAML y Asignación de espacios de nombres para XAML de WPF.

Eventos y código subyacente XAML

La mayoría de las aplicaciones de WPF constan de marcado XAML y código subyacente. Dentro de un proyecto, el XAML se escribe como un .xaml archivo y un lenguaje CLR como Microsoft Visual Basic o C# se usa para escribir un archivo de código subyacente. Cuando un archivo XAML se compila como parte de los modelos de aplicación y programación de WPF, la ubicación del archivo de código subyacente XAML para un archivo XAML se identifica especificando un espacio de nombres y una clase como atributo x:Class del elemento raíz del XAML.

En los ejemplos hasta ahora, ha visto varios botones, pero ninguno de estos botones tenía ningún comportamiento lógico asociado a ellos todavía. El mecanismo de nivel de aplicación principal para agregar un comportamiento para un elemento de objeto es usar un evento existente de la clase de elemento y escribir un controlador específico para ese evento que se invoca cuando ese evento se genera en tiempo de ejecución. El nombre del evento y el nombre del controlador que se va a usar se especifican en el marcado, mientras que el código que implementa el controlador se define en el código subyacente.

<Page x:Class="ExampleNamespace.ExamplePage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Button Click="Button_Click">Click me</Button>
    </StackPanel>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ExampleNamespace;

public partial class ExamplePage : Page
{
    public ExamplePage() =>
        InitializeComponent();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var buttonControl = (Button)e.Source;
        buttonControl.Foreground = Brushes.Red;
    }
}
Class ExamplePage
    Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        Dim buttonControl = DirectCast(e.Source, Button)
        buttonControl.Foreground = Brushes.Red
    End Sub
End Class

Observe que el archivo de código subyacente usa el espacio de nombres ExampleNamespace CLR (el espacio de nombres no está visible en Visual Basic) y declara ExamplePage como una clase parcial dentro de ese espacio de nombres. Esto paraleliza el x:Class valor del atributo de ExampleNamespace. ExamplePage que se proporcionó en la raíz de marcado. El compilador de marcado de WPF creará una clase parcial para cualquier archivo XAML compilado derivando una clase del tipo de elemento raíz. Cuando se proporciona código subyacente que también define la misma clase parcial, el código resultante se combina dentro del mismo espacio de nombres y clase de la aplicación compilada.

Importante

En Visual Basic, el espacio de nombres raíz está implícito tanto para XAML como para el código subyacente. Solo los espacios de nombres anidados son visibles. En este artículo se muestra el XAML del proyecto de C#.

Para obtener más información sobre los requisitos para la programación de código subyacente en WPF, vea Code-behind, Event Handler y Partial Class Requirements in WPF.

Si no quieres crear un archivo de código subyacente independiente, también puedes insertar el código en un archivo XAML. Sin embargo, el código insertado es una técnica menos versátil que tiene limitaciones sustanciales. Para obtener más información, consulta Code-Behind y XAML en WPF.

Eventos enrutados

Una característica de evento determinada que es fundamental para WPF es un evento enrutado. Los eventos enrutados permiten que un elemento controle un evento generado por un elemento diferente, siempre y cuando los elementos estén conectados a través de una relación de árbol. Al especificar el control de eventos con un atributo XAML, el evento enrutado se puede escuchar y controlar en cualquier elemento, incluidos los elementos que no enumeran ese evento en particular en la tabla de miembros de clase. Esto se logra cualificando el atributo de nombre de evento con el nombre de la clase propietaria. Por ejemplo, el elemento primario StackPanel en el ejemplo en curso StackPanel / Button podría registrar un controlador para el evento del botón del elemento secundario Click especificando el atributo Button.Click en el elemento StackPanel object, con el nombre del controlador como el valor del atributo. Para obtener más información, consulte Información general sobre eventos enrutados.

Elementos con nombre

De forma predeterminada, la instancia de objeto que se crea en un gráfico de objetos mediante el procesamiento de un elemento de objeto XAML no tiene un identificador único ni una referencia de objeto. En cambio, si llama a un constructor en el código, casi siempre usa el resultado del constructor para establecer una variable en la instancia construida, de modo que pueda hacer referencia a la instancia más adelante en el código. Para proporcionar acceso estandarizado a los objetos creados a través de una definición de marcado, XAML define el atributo x:Name. Puede establecer el valor del x:Name atributo en cualquier elemento de objeto. En el código subyacente, el identificador que elija es equivalente a una variable de instancia que hace referencia a la instancia construida. En todos los aspectos, los elementos con nombre funcionan como si fueran instancias de objeto (el nombre hace referencia a esa instancia) y el código subyacente puede hacer referencia a los elementos con nombre para controlar las interacciones en tiempo de ejecución dentro de la aplicación. Esta conexión entre instancias y variables se realiza mediante el compilador de marcado XAML de WPF y, más específicamente, implica características y patrones como, por InitializeComponent ejemplo, que no se tratarán con detalle en este artículo.

Los elementos XAML de nivel de marco de WPF heredan una Name propiedad, que es equivalente al atributo definido por x:Name XAML. Algunas otras clases también proporcionan equivalentes de nivel de propiedad para x:Name, que normalmente también se define como una Name propiedad. Por lo general, si no puedes encontrar una Name propiedad en la tabla de miembros del elemento o tipo elegido, usa x:Name en su lugar. Los x:Name valores proporcionarán un identificador a un elemento XAML que se puede usar en tiempo de ejecución, ya sea por subsistemas específicos o por métodos de utilidad como FindName.

En el ejemplo siguiente se establece Name en un StackPanel elemento . A continuación, un controlador de un Button dentro de esa StackPanel hace referencia al StackPanel a través de su referencia de instancia buttonContainer según lo establecido por Name.

<StackPanel Name="buttonContainer">
    <Button Click="RemoveThis_Click">Click to remove this button</Button>
</StackPanel>
private void RemoveThis_Click(object sender, RoutedEventArgs e)
{
    var element = (FrameworkElement)e.Source;
    
    if (buttonContainer.Children.Contains(element))
        buttonContainer.Children.Remove(element);
}
Private Sub RemoveThis_Click(sender As Object, e As RoutedEventArgs)
    Dim element = DirectCast(e.Source, FrameworkElement)

    If buttonContainer.Children.Contains(element) Then
        buttonContainer.Children.Remove(element)
    End If
End Sub

Al igual que una variable, el nombre XAML de una instancia se rige por un concepto de ámbito, de modo que los nombres se puedan aplicar para que sean únicos dentro de un ámbito determinado que sea predecible. El marcado principal que define una página denota un ámbito de nombres XAML único, y el límite del ámbito de nombres XAML es el elemento raíz de esa página. Sin embargo, otros orígenes de marcado pueden interactuar con una página en tiempo de ejecución, como estilos o plantillas dentro de estilos, y estos orígenes de marcado suelen tener sus propios ámbitos de nombres XAML que no se conectan necesariamente con el ámbito de nombres XAML de la página. Para obtener más información sobre x:Name y los ámbitos de nombres XAML, consulta Name, x:Name Directive o WPF XAML Namescopes.

Propiedades adjuntas y eventos adjuntos

XAML especifica una característica de lenguaje que permite especificar determinadas propiedades o eventos en cualquier elemento, incluso si la propiedad o evento no existe en las definiciones del tipo para el elemento en el que se va a establecer. La versión de propiedades de esta característica se denomina propiedad adjunta, la versión de eventos se denomina evento adjunto. Conceptualmente, puedes pensar en propiedades adjuntas y eventos adjuntos como miembros globales que se pueden establecer en cualquier instancia de elemento o objeto XAML. Sin embargo, ese elemento o clase o una infraestructura más amplia debe soportar un repositorio de propiedades de soporte para los valores adjuntos.

Las propiedades adjuntas en XAML se usan normalmente a través de la sintaxis de atributo. En la sintaxis de atributo, especifique una propiedad adjunta con el formato ownerType.propertyName.

Superficialmente, esto se parece a un uso de elemento de propiedad, pero en este caso, el ownerType especificado siempre es un tipo diferente al elemento de objeto en el que se establece la propiedad adjunta. ownerType es el tipo que proporciona los métodos de acceso requeridos por un procesador XAML para obtener o establecer la propiedad adjunta.

El escenario más común para las propiedades anexadas es permitir que los elementos secundarios transmitan un valor de propiedad a su elemento primario.

En el ejemplo siguiente se muestra la DockPanel.Dock propiedad adjunta. La DockPanel clase define los descriptores de acceso para DockPanel.Dock y posee la propiedad adjunta. La DockPanel clase también incluye lógica que itera sus elementos secundarios y comprueba específicamente cada elemento para un valor establecido de DockPanel.Dock. Si se encuentra un valor, ese valor se usa durante el diseño para colocar los elementos secundarios. El uso de la DockPanel.Dock propiedad adjunta y esta capacidad de posicionamiento es, de hecho, la razón motivadora para la clase DockPanel.

<DockPanel>
    <Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>
    <Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>
</DockPanel>

En WPF, la mayoría o todas las propiedades adjuntas también se implementan como propiedades de dependencia. Para obtener más información, vea Información general sobre las propiedades adjuntas.

Los eventos adjuntos usan una forma similar ownerType.eventName de sintaxis de atributo. Al igual que los eventos no adjuntos, el valor de atributo de un evento adjunto en XAML especifica el nombre del método de controlador que se invoca cuando el evento se controla en el elemento. Los usos de eventos adjuntos en XAML de WPF son menos comunes. Para obtener más información, consulte Información general sobre eventos adjuntos.

Tipos base

El XAML subyacente de WPF y su espacio de nombres XAML son una colección de tipos que corresponden tanto a objetos de CLR como a elementos de marcado para XAML. Sin embargo, no todas las clases se pueden asignar a elementos. Las clases abstractas, como ButtonBase, y ciertas clases base no abstractas, se usan para la herencia en el modelo de objetos CLR. Las clases base, incluidas las abstractas, siguen siendo importantes para el desarrollo xaml porque cada uno de los elementos XAML concretos hereda miembros de alguna clase base en su jerarquía. A menudo, estos miembros incluyen propiedades que se pueden establecer como atributos en el elemento o eventos que se pueden controlar. FrameworkElement es la clase de interfaz de usuario base concreta de WPF en el nivel de marco de WPF. Al diseñar la interfaz de usuario, usará varias clases de forma, panel, decorador o control, que se derivan de FrameworkElement. Una clase base relacionada, FrameworkContentElement, admite elementos orientados a documentos que funcionan bien para una presentación de diseño de flujo, mediante API que reflejan deliberadamente las API en FrameworkElement. La combinación de atributos en el nivel de elemento y un modelo de objetos CLR proporciona un conjunto de propiedades comunes que se pueden establecer en la mayoría de los elementos XAML concretos, independientemente del elemento XAML específico y su tipo subyacente.

Seguridad

XAML es un lenguaje de marcado que representa directamente la creación de instancias y la ejecución de objetos. Por eso los elementos creados en XAML tienen la misma capacidad de interactuar con los recursos del sistema (acceso a la red, E/S del sistema de archivos, por ejemplo) que el código de la aplicación. XAML también tiene el mismo acceso a los recursos del sistema que la aplicación de hospedaje.

Seguridad de acceso de código (CAS) en WPF

A diferencia de .NET Framework, WPF para .NET no admite CAS. Para obtener más información, consulte Diferencias de seguridad de acceso al código.

Carga de XAML desde el código

XAML se puede usar para definir toda la interfaz de usuario, pero a veces también es adecuado definir solo una parte de la interfaz de usuario en XAML. Esta funcionalidad se puede usar para:

  • Habilite la personalización parcial.
  • Almacenamiento local de la información de la interfaz de usuario.
  • Modele un objeto de negocio.

La clave de estos escenarios es la XamlReader clase y su Load método. La entrada es un archivo XAML y la salida es un objeto que representa todo el árbol en tiempo de ejecución de objetos creados a partir de ese marcado. A continuación, puede insertar el objeto para que sea una propiedad de otro objeto que ya existe en la aplicación. Siempre que la propiedad esté en el modelo de contenido y tenga funcionalidades de visualización que notificarán al motor de ejecución que se ha agregado contenido nuevo a la aplicación, puedes modificar fácilmente el contenido de una aplicación en ejecución cargando dinámicamente en XAML.

Consulte también