Clases XAML y personalizadas para WPF

Extensible Application Markup Language (XAML) tal como se implementa en .NET admite la capacidad de definir una clase o estructura personalizada en cualquier lenguaje common language runtime (CLR) y, a continuación, obtener acceso a esa clase mediante el marcado XAML. Puede usar una mezcla de tipos definidos por el Windows Presentation Foundation (WPF) y sus tipos personalizados dentro del mismo archivo de marcado, normalmente asignando los tipos personalizados a un prefijo de espacio de nombres de XAML. En este tema se explican los requisitos que debe satisfacer una clase personalizada para ser utilizable como elemento XAML.

Este tema contiene las secciones siguientes.

  • Clases personalizadas en aplicaciones o ensamblados
  • Requisitos para una clase personalizada como elemento XAML
  • Requisitos para las propiedades de una clase personalizada como atributos XAML
  • Requisitos para la sintaxis de atributo del controlador de eventos XAML en los eventos de una clase personalizada
  • Escribir propiedades de colección
  • Declarar propiedades de contenido XAML
  • Serializar XAML
  • Temas relacionados

Clases personalizadas en aplicaciones o ensamblados

Las clases personalizadas que se utilizan en XAML se pueden definir de dos maneras distintas: dentro del código subyacente o de otro código que genere la aplicación Windows Presentation Foundation (WPF) primaria, o como una clase en un ensamblado independiente, tal como una aplicación ejecutable o una DLL utilizada como biblioteca de clases. Cada uno de estos enfoques tiene ventajas y desventajas particulares.

  • La ventaja de crear una biblioteca de clases es que cualquiera de esas clases personalizadas puede compartirse entre muchas posibles aplicaciones diferentes. Una biblioteca independiente también facilita las tareas de control de versión de las aplicaciones y simplifica la creación de una clase cuyo uso previsto sea el de elemento raíz en una página XAML.

  • La ventaja de definir las clases personalizadas en la aplicación consiste en que esta técnica es relativamente ligera, y minimiza los problemas de implementación y pruebas que se encuentran al introducir ensamblados independientes además del ejecutable principal de la aplicación. Sin embargo, una desventaja notable es que no se pueden usar las clases definidas en el mismo ensamblado que el elemento raíz de otras páginas XAML de esa aplicación.

  • Ya estén definidas en el mismo ensamblado o en uno diferente, las clases personalizadas deben asignarse entre el espacio de nombres CLR y el espacio de nombres XML para utilizarse en XAML como elementos. Vea Espacios de nombres y asignación de espacios de nombres XAML para WPF.

Requisitos para una clase personalizada como elemento XAML

Para permitir la creación de instancias como elementos de objeto, la clase debe cumplir los requisitos siguientes:

  • La clase personalizada debe ser pública y admitir un constructor público predeterminado (sin parámetros). (Vea en la siguiente sección las notas relativas a las estructuras).

  • Su clase personalizada no debe ser una clase anidada. Las clases anidadas y el "punto" en su sintaxis de uso de CLR general interfieren con otras características de WPF y/o XAML como, por ejemplo, las propiedades adjuntas.

Además de permitir la sintaxis de elemento de objeto, su definición del objeto también debe habilitar la sintaxis de elemento de propiedad para cualquier otra propiedad pública que admita el objeto como el tipo de valor. Esto se debe a que ahora es posible crear instancias de un objeto como un elemento de objeto y a que el objeto puede rellenar el valor de elemento de propiedad de tales propiedades.

Estructuras

Las estructuras que se definen como tipos personalizados siempre se pueden construir en XAML en WPF. Esto se debe a que los compiladores de CLR crean implícitamente un constructor predeterminado para una estructura que inicializa todos los valores de propiedad a sus valores predeterminados. En algunos casos, no es deseable para una estructura el comportamiento predeterminado de construcción o de uso de elementos de objeto. Esto se puede deber a que la estructura está pensada para rellenar valores y funcionar, desde el punto de vista conceptual, como una unión, cuyos valores contenidos pueden tener interpretaciones mutuamente excluyentes, en cuyo caso ninguna de sus propiedades se podría establecer. Un ejemplo de WPF de este tipo de estructura es GridLength. Generalmente, tales estructuras deberían implementar un convertidor de tipos para que los valores se puedan expresar en forma de atributo y usar las convenciones de cadena que crean las diferentes interpretaciones o modos de los valores de la estructura. La estructura también debería exponer un comportamiento similar por la construcción del código a través de un constructor no predeterminado.

Requisitos para las propiedades de una clase personalizada como atributos XAML

Las propiedades deben hacer referencia a un tipo por valor (tal como un tipo primitivo) o usar una clase para tipos que tenga un constructor predeterminado o un convertidor de tipos dedicado al que pueda obtener acceso un procesador XAML. En la implementación de CLR XAML, los procesadores de XAML buscan estos convertidores a través de la compatibilidad nativa con los primitivos del lenguaje o a través de la aplicación de TypeConverterAttribute a un tipo o miembro en las definiciones de tipo de respaldo.

Como alternativa, la propiedad puede hacer referencia a un tipo de clase abstracto o a una interfaz. Para las clases abstractas o las interfaces, la expectativa del análisis de XAML es que el valor de propiedad deba rellenarse con instancias de clase prácticas que implementen la interfaz, o con instancias de tipos derivadas de la clase abstracta.

Las propiedades se pueden declarar en una clase abstracta, pero solo se pueden establecer en las clases prácticas derivadas de la clase abstracta. Esto se debe a que crear el elemento de objeto para la clase requiere un constructor predeterminado público en la clase.

Sintaxis de atributo con convertidor de tipos

Si se proporciona un convertidor de tipos dedicado, con atributos, en el nivel de clase, la conversión de tipos aplicada habilita la sintaxis de atributo para cualquier propiedad que necesita crear instancias de ese tipo. Un convertidor de tipos no permite el uso de elemento de objeto del tipo; solamente la presencia de un constructor predeterminado para ese tipo habilita el uso de elemento de objeto. Por consiguiente, las propiedades habilitadas con convertidor de tipos, en general, no son utilizables en sintaxis de propiedad, a menos que el propio tipo admita también la sintaxis de elemento de objeto. La excepción consiste en que se puede especificar una sintaxis de elemento de propiedad, pero el elemento de propiedad debe contener una cadena. Ese uso es esencialmente el equivalente a un uso de sintaxis de atributo, realmente; este tipo de uso no es común a menos que se necesite una administración más sólida del espacio en blanco en el valor del atributo. Por ejemplo, lo siguiente es un uso de elemento de propiedad que toma una cadena y el uso de atributo equivalente:

<Button>Hallo!
  <Button.Language>
    de-DE
  </Button.Language>
</Button>
<Button Language="de-DE">Hallo!</Button>

Ejemplos de propiedades donde se permite la sintaxis de atributo, pero se rechaza la sintaxis de elemento de propiedad que contiene un elemento de objeto a través de XAML, son varias propiedades que toman el tipo Cursor. La clase Cursor tiene un objeto CursorConverter convertidor de tipos dedicado, pero no expone un constructor predeterminado, por lo que la propiedad Cursor solamente se puede establecer mediante sintaxis de atributo aunque el tipo Cursor real sea un tipo de referencia.

Convertidores de tipo por propiedad

La propia propiedad puede declarar también un convertidor de tipos en el nivel de propiedad. Esto habilita un "minilenguaje" que crea instancias de objetos del tipo de la propiedad en el propio código, procesando valores de cadena de entrada del atributo como entrada para una operación ConvertFrom basada en el tipo correspondiente. Normalmente, esto se hace para proporcionar un descriptor de acceso adecuado y no como único medio para habilitar la configuración de una propiedad en XAML. Sin embargo, también es posible utilizar convertidores de tipos para atributos cuando se desee utilizar tipos CLR que no proporcionen un constructor predeterminado ni un convertidor de tipos con atributos. Ejemplos de WPF  APIs son determinadas propiedades que toman el tipo CultureInfo. En este caso, WPF utilizaba el tipo Microsoft .NET FrameworkCultureInfoexistente para resolver mejor la compatibilidad y los escenarios de migración utilizados en versiones anteriores de los marcos de trabajo, pero el tipo CultureInfo no admitía los constructores necesarios o la conversión de tipos de nivel de tipo para ser directamente utilizable como un valor de propiedad XAML.

Siempre que exponga una propiedad que tenga un uso XAML, en particular si está creando controles, debería considerar respaldar esa propiedad con una propiedad de dependencia. Esto es especialmente cierto si se utiliza la implementación Windows Presentation Foundation (WPF) existente del procesador de XAML, porque puede mejorar el rendimiento utilizando el respaldo con DependencyProperty. Una propiedad de dependencia expondrá características del sistema de propiedades para la propiedad de modo que los usuarios esperen una propiedad que permita el acceso mediante XAML. Esto incluye características tales como la animación, el enlace de datos y la compatibilidad con estilos. Para obtener más información, vea Propiedades de dependencia personalizadas y Carga de XAML y propiedades de dependencia.

Escribir y asignar atributos a un convertidor de tipos

A veces, necesitará escribir una clase personalizada derivada de TypeConverter para proporcionar conversión de tipos a un tipo de propiedad. Para obtener instrucciones sobre cómo derivar y crear un convertidor de tipos que admita usos XAML y cómo aplicar el objeto TypeConverterAttribute, vea Clases TypeConverter y XAML.

Requisitos para la sintaxis de atributo del controlador de eventos XAML en los eventos de una clase personalizada

Para ser utilizable como un evento CLR, el evento se debe exponer como un evento público en una clase que admita un constructor predeterminado o en una clase abstracta donde se pueda tener acceso al evento en clases derivadas. Para poder utilizarse cómodamente como un evento enrutado, el evento de CLR debe implementar métodos add y remove explícitos que agreguen y quiten controladores para la firma de evento CLR y reenvíen esos controladores a los métodos AddHandler y RemoveHandler. Estos métodos agregan o quitan controladores del almacén del controlador de eventos enrutados en la instancia a la que está asociado el evento.

NotaNota

Es posible registrar controladores directamente para eventos enrutados utilizando AddHandler, así como no definir deliberadamente un evento CLR que exponga el evento enrutado.En general, esto no es recomendable, porque el evento no habilitará la sintaxis de atributo XAML para adjuntar controladores y la clase resultante ofrecerá una vista XAML menos transparente de las capacidades de ese tipo.

Escribir propiedades de colección

Las propiedades que toman un tipo de colección tienen una sintaxis XAML que permite especificar objetos que se agregan a la colección. Esta sintaxis tiene dos características notables.

  • El objeto que es el objeto de colección no necesita especificarse en la sintaxis de elemento de objeto. La presencia de ese tipo de colección es implícita siempre que se especifique una propiedad de XAML que tome un tipo de colección.

  • Los elementos secundarios de la propiedad de colección del marcado se procesan para convertirse en miembros de la colección. Normalmente, el acceso del código a los miembros de una colección se realiza a través de métodos de lista y diccionario, tales como Add, o mediante un indizador. Sin embargo, la sintaxis XAML no admite métodos ni indizadores. Las colecciones son obviamente un requisito muy común para generar un árbol de elementos y se necesita alguna manera de rellenarlas en XAML declarativo. Por consiguiente, los elementos secundarios de una propiedad de colección se procesan agregándolos a la colección que es el valor de tipo de la propiedad de colección.

La implementación de XAML de .NET y, por tanto, el procesador XAML de WPF usan la siguiente definición para lo que constituye una propiedad de colección. El tipo de propiedad de la propiedad debe implementar una de las opciones siguientes:

Cada uno de estos tipos de CLR tiene un método Add, que es utilizado por el procesador de XAML para agregar elementos a la colección subyacente cuando crea el gráfico de objeto.

NotaNota

Las interfaces de List y Dictionary genéricas (IList<T> y IDictionary<TKey, TValue>) no se admiten para la detección de colecciones por parte del procesador de XAML de WPF.Sin embargo, puede utilizar List<T> como una clase base, ya que implementa directamente IList o Dictionary<TKey, TValue>, ya que ésta implementa directamente IDictionary.

Al declarar una propiedad que toma una colección, tenga cuidado con el modo en el que se inicializa ese valor de propiedad en las nuevas instancias del tipo. Si no está implementando la propiedad como una propiedad de dependencia, es aconsejable hacer que la propiedad utilice un campo de respaldo que llame al constructor del tipo de colección. Si la propiedad es una propiedad de dependencia, quizá necesite inicializar la propiedad de colección como parte del constructor de tipo predeterminado. Esto se debe a que una propiedad de dependencia toma su valor predeterminado de los metadatos y, normalmente, no es conveniente que el valor inicial de una propiedad de colección sea una colección estática compartida. Debería haber una instancia de colección por cada instancia del tipo contenedor. Para obtener más información, vea Propiedades de dependencia personalizadas.

Puede implementar un tipo de colección personalizado para la propiedad de colección. Debido al tratamiento de propiedad de colección implícito, no es necesario que el tipo de colección personalizado proporcione un constructor predeterminado utilizar implícitamente el tipo en XAML. Sin embargo, puede proporcionar un constructor predeterminado para el tipo de colección. Esta puede ser una práctica que vale la pena. A menos que proporcione un constructor predeterminado, no puede declarar explícitamente la colección como un elemento de objeto. Es posible que algunos autores de marcado prefieran considerar la colección explícita como una cuestión de estilo de marcado. Además, un constructor predeterminado puede simplificar los requisitos de inicialización al crear nuevos objetos que utilicen el tipo de colección como un valor de propiedad.

Declarar propiedades de contenido XAML

El lenguaje XAML define el concepto de una propiedad de contenido XAML. Cada clase utilizable en sintaxis de objeto puede tener exactamente una propiedad de contenido XAML. Para declarar una propiedad para que sea la propiedad de contenido XAML para la clase, aplique ContentPropertyAttribute como parte de la definición de clase. Especifique el nombre de la propiedad de contenido XAML que desee como Name en el atributo.

Puede especificar una propiedad de colección como propiedad de contenido XAML. El resultado es un uso para esa propiedad mediante la cual el elemento de objeto puede tener uno o más elementos secundarios, sin elementos de objeto de colección intermedios ni etiquetas de elemento de propiedad. Estos elementos se tratan entonces como el valor para la propiedad de contenido XAML y se agregan a la instancia de respaldo de la colección.

Algunas propiedades XAML de WPF existentes utilizan el tipo de propiedad de Object. Esto permite que una propiedad de contenido XAML pueda tomar valores primitivos tales como String así como un valor de objeto de referencia único. Si sigue este modelo, el tipo será responsable de la determinación de tipo, así como del control de los tipos posibles. La razón típica para un tipo de contenido Object es admitir tanto un medio simple para agregar contenido de objetos en forma de cadena (que recibe un tratamiento de presentación predeterminado) como un medio avanzado de agregar contenido de objetos que especifique una presentación no predeterminada o datos adicionales.

Serializar XAML

Para determinados escenarios, tales como la creación de controles, quizá desee asegurarse de que toda representación de objetos para la que se pueda crear instancias en XAML pueda serializarse también en XAML equivalente. Los requisitos de serialización no se describen en este tema. Vea Información general sobre la creación de controles y Árbol de elementos y serialización.

Vea también

Conceptos

Información general sobre XAML (WPF)

Propiedades de dependencia personalizadas

Información general sobre la creación de controles

Información general sobre elementos base

Carga de XAML y propiedades de dependencia