Definición de tipos personalizados para usarlos con los servicios XAML de .NET

Al definir tipos personalizados que son objetos de negocio o son tipos que no tienen una dependencia en fotogramas concretos, hay ciertos procedimientos recomendados para XAML que puede seguir. Si sigue estos procedimientos, los servicios XAML de .NET y sus lectores y escritores XAML pueden descubrir las características de XAML de su tipo y darle una representación adecuada en una secuencia de nodos de XAML mediante el sistema de tipos de XAML. En este tema se describen los procedimientos recomendados para las definiciones de tipos, las definiciones de miembros y la atribución de CLR de tipos o miembros.

Patrones de constructor y definiciones de tipos para XAML

Para poder crear una instancia de ella como un elemento de objeto en XAML, una clase personalizada debe cumplir los requisitos siguientes:

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

  • La clase personalizada no debe ser una clase anidada. El "punto" adicional en la ruta de acceso de nombre completo hace que la división del espacio de nombres de clase sea ambigua e interfiere con otras características de XAML, como las propiedades adjuntas. Si se puede crear una instancia de un objeto como elemento de objeto, el objeto creado puede rellenar el formulario de elemento de propiedad de cualquier propiedad que tome el objeto como su tipo subyacente.

Sigue pudiendo proporcionar valores de objeto para los tipos que no cumplen estos criterios, si habilita un convertidor de valores. Para obtener más información, consulte Convertidores de tipo y extensiones de marcado para XAML.

Estructuras

Las estructuras siempre se pueden construir en XAML mediante la definición de CLR. Esto se debe a que un compilador de CLR crea implícitamente un constructor sin parámetros para una estructura. Este constructor inicializa todos los valores de propiedad en sus valores predeterminados.

En algunos casos, el comportamiento de construcción predeterminado de una estructura no es deseable. Esto puede deberse a que la estructura está pensada para rellenar valores y funcionar conceptualmente como una unión. Como unión, es posible que los valores contenidos tengan interpretaciones mutuamente excluyentes y que, por lo tanto, ninguna de sus propiedades se pueda establecer. Un ejemplo de esta estructura en el vocabulario de WPF es GridLength. Tales estructuras deberían implementar un convertidor de tipos para que los valores se puedan expresar en forma de atributo, usando 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 mediante un constructor sin parámetros.

Interfaces

Las interfaces se pueden usar como tipos subyacentes de los miembros. El sistema de tipos XAML comprueba la lista asignable y espera que el objeto proporcionado como valor se pueda asignar a la interfaz. No hay ningún concepto de cómo se debe presentar la interfaz como tipo XAML, siempre que un tipo asignable pertinente admita los requisitos de construcción XAML.

Factory Methods

Los Factory Methods son una característica de XAML 2009. Modifican el principio de XAML de que los objetos deben tener constructores sin parámetros. Los Factory Methods no se documentan en este artículo. Consulte Directiva x:FactoryMethod.

Enumeraciones

Las enumeraciones tienen un comportamiento de conversión de tipos nativos XAML. Los nombres de constantes de enumeración especificados en XAML se resuelven con el tipo de enumeración subyacente y devuelven el valor de enumeración a un escritor de objetos XAML.

XAML admite un uso de estilo de marcas para enumeraciones en las que se aplica FlagsAttribute. Para obtener más información, consulte Sintaxis de XAML en detalle. (Sintaxis de XAML en detalle se ha escrito para la audiencia de WPF, pero la mayor parte de la información de ese tema es relevante para XAML, que no es específico de un marco de implementación determinado).

Definiciones de miembros

Los tipos pueden definir miembros para el uso de XAML. Es posible que los tipos definan miembros que XAML pueda usar incluso si ese tipo concreto no se puede usar en XAML. Esto es posible debido a la herencia de CLR. Siempre que algún tipo que herede el miembro admita el uso de XAML como un tipo, y que el miembro admita el uso de XAML para su tipo subyacente o tenga disponible una sintaxis de XAML nativa, ese miembro es se puede utilizar en XAML.

Propiedades

Si define propiedades como una propiedad CLR pública con los patrones de descriptor de acceso típicos de CLR get y set y las palabras clave adecuadas para el lenguaje, el sistema de tipos de XAML puede notificar la propiedad como miembro con la información adecuada proporcionada para las propiedades XamlMember, como IsReadPublic y IsWritePublic.

Las propiedades específicas pueden habilitar una sintaxis de texto aplicando TypeConverterAttribute. Para obtener más información, consulte Convertidores de tipo y extensiones de marcado para XAML.

En ausencia de una sintaxis de texto o una conversión XAML nativa, y en ausencia de un mayor direccionamiento indirecto, como un uso de la extensión de marcado, el tipo de una propiedad (TargetType en el sistema de tipos de XAML) debe ser capaz de devolver una instancia a un escritor de objetos XAML tratando el tipo de destino como un tipo CLR.

Si usa XAML 2009, se puede usar la x:Extensión de marcado de referencia para proporcionar valores si no se cumplen las consideraciones anteriores; sin embargo, es más un problema de uso que un problema de definición de tipos.

Eventos

Si define eventos como un evento CLR público, el sistema de tipos de XAML puede notificar el evento como miembro con IsEvent como true. El cableado de los controladores de eventos no está dentro del ámbito de las funcionalidades de los servicios XAML de .NET; el cableado se reserva para marcos e implementaciones específicos.

Métodos

El código insertado para los métodos no es una funcionalidad de XAML predeterminada. En la mayoría de los casos, no se hace referencia directamente a los miembros de métodos desde XAML, y el papel de los métodos en XAML es únicamente proporcionar compatibilidad con patrones XAML concretos. x:Directiva FactoryMethod es una excepción.

Campos

Las directrices de diseño CLR desalentan los campos no estáticos. En el caso de los campos estáticos, solo puede acceder a los valores de campo estático a través de la x:Extensión de marcado estático; en este caso, no está haciendo nada especial en la definición de CLR para exponer un campo para usos x:estáticos.

Miembros adjuntables

Los miembros adjuntables se exponen a XAML a través de un patrón de método de descriptor de acceso en un tipo definidor. El tipo definidor en sí no tiene que usarse en XAML como un objeto. De hecho, un patrón común es declarar una clase de servicio cuyo rol sea el de poseer el miembro adjuntable e implementar los comportamientos relacionados, pero que no sirva ninguna otra función, como la representación de una interfaz de usuario. En las secciones siguientes, el marcador de posición PropertyName representa el nombre del miembro adjuntable. Ese nombre debe ser válido en la gramática XamlName.

Tenga cuidado con las colisiones de nombres entre estos patrones y otros métodos de un tipo. Si existe un miembro que coincide con uno de los patrones, un procesador XAML la puede interpretar como una ruta de uso de miembros adjuntables, aunque no sea su intención.

El descriptor de acceso GetPropertyName

La signatura del descriptor de acceso GetPropertyName debe ser:

public static object GetPropertyName(object target)

  • El objeto target puede especificarse como un tipo más específico en la implementación. Puede usarlo para definir el ámbito de uso de su miembro adjuntable; los usos fuera del ámbito previsto producirán excepciones de conversión no válida que después se mostrarán mediante un error de análisis de XAML. El nombre de parámetro target no es un requisito, pero se denomina target por convención en la mayoría de las implementaciones.

  • El valor devuelto puede especificarse como un tipo más específico en la implementación.

Para admitir una sintaxis de texto habilitada TypeConverter para el uso de atributos del miembro adjuntable, aplique TypeConverterAttribute al descriptor de acceso GetPropertyName. La aplicación a get en lugar de a set puede parecer poco intuitiva; sin embargo, esta convención puede admitir el concepto de miembro adjuntable de solo lectura serializable, lo que resulta útil en escenarios de diseñador.

El descriptor de acceso SetPropertyName

La signatura del descriptor de acceso SetPropertyName debe ser:

public static void SetPropertyName(object target, object value)

  • El objeto target se puede especificar como un tipo más concreto en la implementación, con la misma lógica y consecuencias que se describen en la sección anterior.

  • El objeto value puede especificarse como un tipo más específico en la implementación.

Recuerde que el valor de este método es la entrada procedente de la utilización de XAML, normalmente en el formulario de atributo. Desde el formulario de atributo, debe haber compatibilidad con el convertidor de valores para una sintaxis de texto y se debe atribuir en el descriptor de acceso de GetPropertyName.

Almacenes de miembros adjuntables

Normalmente, los métodos de descriptor de acceso no son suficientes para proporcionar un medio para colocar valores de miembro adjuntable en un gráfico de objetos, o para recuperar valores del gráfico de objetos y serializarlos correctamente. Para proporcionar esta funcionalidad, los objetos target de las firmas de descriptor de acceso anteriores deben ser capaces de almacenar valores. El mecanismo de almacenamiento debe ser coherente con el principio de los miembros adjuntables que el miembro se puede adjuntar a destinos en los que el miembro adjuntable no está en la lista de miembros. Los servicios XAML de .NET proporcionan una técnica de implementación para los almacenes de miembros adjuntables a través de las API IAttachedPropertyStore y AttachablePropertyServices. Los escritores XAML usan IAttachedPropertyStore para detectar la implementación del almacén, y deben implementarse en el tipo que sea el target de los descriptores de acceso. Las API estáticas AttachablePropertyServices se usan en el cuerpo de los descriptores de acceso y hacen referencia al miembro adjuntable por su AttachableMemberIdentifier.

La asignación correcta de los tipos, miembros y ensamblados es importante para notificar información del sistema de tipos de XAML a los servicios XAML de .NET. Notificar la información del sistema de tipos de XAML es pertinente si se da alguna de las situaciones siguientes:

  • Tiene la intención de usar tus tipos con sistemas XAML que se basan directamente en lectores y escritores XAML de los servicios XAML de .NET.
  • Define o usa un marco que usa XAML basado en esos lectores y escritores XAML.

Para obtener una lista de cada atributo relacionado con XAML que es pertinente para la compatibilidad con XAML de los tipos personalizados, consulte Atributos de CLR relacionados con XAML para tipos y bibliotecas personalizados.

Uso

La utilización de tipos personalizados requiere que el autor del marcado asigne un prefijo para el ensamblado y el espacio de nombres CLR que contiene el tipo personalizado. Este procedimiento no se documenta en este tema.

Nivel de acceso

XAML proporciona un medio para cargar tipos que tienen un nivel de acceso internal y crear instancias de ellos. Esta funcionalidad se proporciona para que el código de usuario pueda definir sus propios tipos y, a continuación, crear instancias de esas clases desde el marcado que también forma parte del mismo ámbito de código de usuario.

Un ejemplo de WPF es cada vez que el código de usuario define un UserControl que está pensado como una manera de refactorizar un comportamiento de interfaz de usuario, pero no como parte de ningún mecanismo de extensión posible que pueda inferirse declarando la clase auxiliar con el nivel de acceso public. Tal UserControl se puede declarar con acceso internal si el código de respaldo se compila en el mismo ensamblado desde el que se hace referencia a él como un tipo XAML.

Para una aplicación que carga XAML en plena confianza y usa XamlObjectWriter, la carga de clases con el nivel de acceso internal siempre está habilitada.

Para una aplicación que carga XAML en confianza parcial, puede controlar las características del nivel de acceso mediante la API XamlAccessLevel. Además, los mecanismos de aplazamiento (como el sistema de plantillas de WPF) deben poder propagar los permisos de nivel de acceso y conservarlos para las evaluaciones en tiempo de ejecución finales; esto se controla internamente pasando la información XamlAccessLevel.

Implementación de WPF

XAML de WPF usa un modelo de acceso de confianza parcial en el que, si BAML se carga con confianza parcial, el acceso está restringido a AssemblyAccessTo para el ensamblado que es el origen BAML. Para el aplazamiento, WPF usa IXamlObjectWriterFactory.GetParentSettings como mecanismo para pasar la información de nivel de acceso.

En la terminología XAML de WPF, un tipo interno es un tipo definido por el mismo ensamblado que también incluye el XAML de referencia. Este tipo se puede asignar mediante un espacio de nombres XAML que omite deliberadamente la parte "assembly=" de una asignación, por ejemplo, xmlns:local="clr-namespace:WPFApplication1". Si BAML hace referencia a un tipo interno y ese tipo tiene el nivel de acceso internal, esto genera una clase GeneratedInternalTypeHelper para el ensamblado. Si desea evitar GeneratedInternalTypeHelper, debe usar el nivel de acceso public o debe factorizar la clase pertinente en un ensamblado independiente y hacer que ese ensamblado sea dependiente.

Vea también