Clases TypeConverter y XAML

En este tema se presenta el propósito de la conversión de tipos desde cadenas como característica general del lenguaje XAML. En .NET Framework, la clase TypeConverter tiene una finalidad concreta como parte de la implementación para una clase personalizada administrada que se puede usar como valor de propiedad en el uso de atributos XAML. Si escribe una clase personalizada, y quiere que las instancias de la clase se puedan usar como valores de atributos XAML que se puedan establecer, es posible que necesite aplicar TypeConverterAttribute a la clase, escribir una clase TypeConverter personalizada, o ambas cosas.

Conceptos de la conversión de tipos

Valores de cadena y XAML

Cuando se establece un valor de atributo en un archivo XAML, el tipo inicial de ese valor es una cadena en texto puro. Incluso otros primitivos, como Double son inicialmente cadenas de texto para un procesador XAML.

Un procesador XAML necesita dos fragmentos de información para procesar un valor de atributo. El primer fragmento de información es el tipo de valor de la propiedad que se va a establecer. Cualquier cadena que defina un valor de atributo y que se procese en XAML se tiene que convertir o resolver en última instancia en un valor de ese tipo. Si el valor es un primitivo que el analizador XAML entiende (por ejemplo, un valor numérico), se tratará de convertir la cadena directamente. Si el valor es una enumeración, se usa la cadena para comprobar una coincidencia de nombre con una constante con nombre en esa enumeración. Si el valor no es un primitivo que el analizador entiende ni una enumeración, entonces el tipo en cuestión debe poder proporcionar una instancia del tipo, o un valor, basado en una cadena convertida. Esto se hace indicando una clase de convertidor de tipos. El convertidor de tipos es, en realidad, una clase del asistente para proporcionar valores de otra clase, tanto para el escenario de XAML como, potencialmente, para las llamadas de código en el código de .NET Framework.

Usar el comportamiento de conversión de tipos existente en XAML

Dependiendo de la medida en que esté familiarizado con los conceptos de XAML subyacentes, es posible que ya use el comportamiento de conversión de tipos en aplicaciones XAML básicas sin darse cuenta. Por ejemplo, WPF define literalmente centenares de propiedades que toman un valor de tipo Point. Point es un valor que describe una coordenada en un espacio de coordenadas bidimensional y, en realidad, solo tiene dos propiedades importantes: X y Y. Cuando se especifica un punto en XAML, se especifica como una cadena con un delimitador (por lo general, una coma) entre los valores X y Y que se proporcionan. Por ejemplo: <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"/>.

Incluso este tipo primitivo de Point y su uso simple en XAML requieren un convertidor de tipos. En este caso, es la clase PointConverter.

El convertidor de tipos para Point que se define en el nivel de clase racionaliza los usos del marcado de todas las propiedades que toman Point. Si en este caso no hubiera un convertidor de tipos, se necesitaría el marcado siguiente, mucho más detallado, para obtener el mismo ejemplo mostrado previamente:

<LinearGradientBrush>
  <LinearGradientBrush.StartPoint>
    <Point X="0" Y="0"/>
  </LinearGradientBrush.StartPoint>
  <LinearGradientBrush.EndPoint>
    <Point X="1" Y="1"/>
  </LinearGradientBrush.EndPoint>
</LinearGradientBrush>

La decisión entre usar la cadena de conversión de tipos o una sintaxis equivalente más detallada suele depender del estilo de codificación. Es posible que el flujo de trabajo de las herramientas de XAML también influya en el modo de establecer los valores. Algunas herramientas de XAML suelen crear la forma más detallada del marcado porque facilita la operación de ida y vuelta en las vistas de los diseñadores o su propio mecanismo de serialización.

Los convertidores de tipos existentes generalmente se pueden detectar en los tipos de WPF y .NET Framework al comprobar en una clase (o propiedad) si aplicó un atributo TypeConverterAttribute. Este atributo denominará la clase que es el convertidor de tipos para los valores de ese tipo, tanto para los fines de XAML como, potencialmente, para otros propósitos.

Convertidores de tipos y extensiones de marcado

Las extensiones de marcado y los convertidores de tipos rellenan los roles ortogonales en lo que se refiere al comportamiento del procesador XAML y los escenarios a los que se aplican. Aunque el contexto está disponible para los usos de la extensión de marcado, en las implementaciones de extensión de marcado no se suele comprobar el comportamiento de conversión de tipos de aquellas propiedades en las que una extensión de marcado proporciona un valor. En otras palabras, aunque una extensión de marcado devuelva una cadena de texto como salida de ProvideValue, no se invoca el comportamiento de la conversión de tipos en esa cadena tal y como se aplica a una propiedad concreta o al tipo de valor de propiedad. Generalmente, el propósito de una extensión de marcado es procesar una cadena y devolver un objeto sin ningún convertidor de tipos implicado.

Una situación común en la que es necesario usar una extensión de marcado en lugar de un convertidor de tipos es cuando se hace referencia a un objeto que ya existe. En el mejor de los casos, un convertidor de tipos sin estado solamente podría generar una nueva instancia, lo que podría no ser conveniente. Para más información sobre las extensiones de marcado, vea Extensiones de marcado y XAML de WPF.

Convertidores de tipos nativos

En la implementación de WPF y .NET Framework del analizador de XAML, hay algunos tipos que presentan un control nativo de la conversión de tipos, si bien no se trata de tipos que puedan considerarse primitivos por convención. Un ejemplo de este tipo es DateTime. La razón para esto se basa en cómo funciona la arquitectura de .NET Framework: el tipo DateTime se define en mscorlib, la biblioteca más básica de .NET. DateTime no se puede atribuir con un atributo que proceda de otro ensamblado que tiene una dependencia (TypeConverterAttribute proviene de System), por lo que no se admite el mecanismo de detección de convertidores de tipos habitual mediante atribución. En su lugar, el analizador de XAML tiene una lista de tipos que necesitan este procesamiento nativo y los procesa de manera parecida a los primitivos auténticos. (En el caso de DateTime, implica llamar a Parse).

Implementación de un convertidor de tipos

TypeConverter

En el ejemplo de Point anterior, se mencionó la clase PointConverter. Para las implementaciones de .NET de XAML, todos los convertidores de tipos que se usan para XAML son clases que se derivan de la clase base TypeConverter. La clase TypeConverter existía en las versiones de .NET Framework anteriores a la existencia de XAML. Uno de sus usos originales era proporcionar conversión de cadenas para los diálogos de propiedades en los diseñadores visuales. Para XAML, el rol de TypeConverter se expande para incluir el hecho de ser la clase base para las conversiones en cadenas y a partir de cadenas que permiten analizar un valor de atributo de cadena, y posiblemente, el procesamiento de un valor en tiempo de ejecución de una propiedad de objeto determinada para convertirlo en una cadena para la serialización como un atributo.

TypeConverter define a cuatro miembros que son importantes para las conversiones en cadenas y a partir de cadenas con fines de procesamiento de XAML:

De estos, el método más importante es ConvertFrom. Este método convierte la cadena de entrada en el tipo de objeto necesario. Desde un punto de vista estricto, el método ConvertFrom se podría implementar de modo que convirtiese un abanico mucho más extenso de tipos en el tipo de destino previsto del convertidor y, de este modo, servir para otros fines además de XAML, como admitir conversiones en tiempo de ejecución, pero para la finalidad de XAML, lo único que cuenta es la ruta de acceso al código que puede procesar una entrada String.

El siguiente método en importancia es ConvertTo. Si una aplicación se convierte en una representación de marcado (por ejemplo, se guarda en XAML como archivo), ConvertTo es responsable de generar una representación de marcado. En este caso, la ruta de acceso del código pertinente para XAML es la que se usa al pasar un destinationType de String.

CanConvertTo y CanConvertFrom son métodos de compatibilidad que se usan cuando un servicio consulta las capacidades de la implementación de TypeConverter . Estos métodos se tienen que implementar para obtener true en los casos específicos de tipo que los métodos de conversión equivalentes de su convertidor admiten. Para los propósitos de XAML, esto suele traducirse en el tipo String .

Información de referencia cultural y convertidores de tipos para XAML

Cada implementación de TypeConverter puede tener su propia interpretación de lo que constituye una cadena válida para una conversión y, además, puede usar o ignorar la descripción de tipos que se pasa como parámetros. Existe una consideración importante con respecto a la referencia cultural y a la conversión de tipos de XAML. XAML admite plenamente el uso de cadenas traducibles como valores de atributo. Pero no se admite el uso de esa cadena traducible como entrada del convertidor de tipos con requisitos de referencia cultural concretos, porque los convertidores de tipos para los valores de atributo de XAML requieren necesariamente un comportamiento de análisis de lenguaje fijo mediante la referencia cultural en-US. Para más información sobre las razones de diseño que motivan esta restricción, consulte la especificación del lenguaje XAML ([MS-XAML].

Un ejemplo que ilustra por qué una referencia cultural puede ser un problema, es que algunas referencias culturales usan una coma como delimitador de separador decimal para los números. Esto entra en conflicto con el comportamiento de muchos de los convertidores de tipos de XAML de WPF, consistente en usar una coma como delimitador (basándose en precedentes históricos como el formato X,Y común o las listas delimitadas por comas). El problema tampoco se resuelve pasando una referencia cultural en el XAML circundante (estableciendo Language o xml:lang en la referencia cultural sl-SI, que es una de las que usan la coma para separar los decimales de esta manera).

Implementación de ConvertFrom

Para ser igual de utilizable que una implementación de TypeConverter que admite XAML, el método ConvertFrom para ese convertidor tiene que aceptar una cadena como parámetro value . Si la cadena tuviera un formato válido y la implementación de TypeConverter pudiera convertirla, el objeto devuelto debería admitir una conversión al tipo que la propiedad espera. De lo contrario, la implementación de ConvertFrom debe devolver null.

Cada implementación de TypeConverter puede tener su propia interpretación de lo que constituye una cadena válida para una conversión y, además, puede usar o ignorar la descripción de tipos o los contextos culturales que se pasan como parámetros. Pero es posible que el procesamiento de XAML de WPF no pase valores al contexto de descripción del tipo en todos los casos y que tampoco pase referencias culturales basadas en xml:lang.

Nota

No use los caracteres de llave, en concreto {, como elementos posibles del formato de cadena. Estos caracteres están reservados como entrada y salida de una secuencia de extensión de marcado.

Implementación de ConvertTo

ConvertTo se usa en teoría para admitir la serialización. La compatibilidad con la serialización a través de ConvertTo para el tipo personalizado y su convertidor de tipos no es un requisito imprescindible. Pero, si implementa un control, o usa la serialización como parte de las características o del diseño de la clase, conviene implementar ConvertTo.

Para ser igual de utilizable que una implementación de TypeConverter que admite XAML, el método ConvertTo para ese convertidor tiene que aceptar una instancia del tipo (o un valor) que se admita como el parámetro value. Cuando el parámetro destinationType es del tipo String, el objeto devuelto debe poder convertirse en String. La cadena devuelta debe representar un valor serializado de value. Lo ideal sería que el formato de serialización elegido sea capaz de generar el mismo valor que si esa cadena se pasara a la implementación de ConvertFrom del mismo convertidor sin incurrir en una pérdida significativa de información.

Si el valor no se puede serializar o el convertidor no admite la serialización, la implementación de ConvertTo debe devolver null y, en este caso, se permite generar una excepción. Pero, si se generan excepciones, será necesario dejar constancia de la imposibilidad de usar esa conversión como parte de su implementación de CanConvertTo para así poder dar cabida al procedimiento recomendado de comprobar primero con CanConvertTo para evitar excepciones.

Si el parámetro destinationType no es de tipo String, puede elegir su propio control de convertidor. Por lo general, se revertiría al control de la implementación base, que en el ConvertTo más básico genera una excepción específica.

Implementación de CanConvertTo

Su implementación de CanConvertTo debe devolver true para destinationType de tipo Stringo, si no, respetar la implementación base.

Implementación de CanConvertFrom

Su implementación de CanConvertFrom debe devolver true para sourceType de tipo Stringo, si no, respetar la implementación base.

Aplicación de TypeConverterAttribute

Para que un procesador XAML use el convertidor de tipos personalizado como el convertidor de tipos activo para una clase personalizada, debe aplicar TypeConverterAttribute en la definición de clase. El ConverterTypeName que se especifica a través del atributo debe ser el nombre de tipo del convertidor de tipos personalizado. Con este atributo aplicado, cuando un procesador XAML controla valores en los que el tipo de propiedad usa el tipo de la clase personalizada, puede especificar cadenas y devolver instancias de objeto.

También puede proporcionar un convertidor de tipos por cada propiedad. En lugar de aplicar un atributo TypeConverterAttribute a la definición de clase, aplíquelo a una definición de propiedad (la definición principal, no las implementaciones de get/set dentro de ella). El tipo de la propiedad tiene que coincidir con el tipo que el convertidor de tipos personalizado procesa. Con este atributo aplicado, cuando un procesador XAML controla valores de esa propiedad, puede procesar cadenas de entrada y devolver instancias de objeto. La técnica de convertidor de tipos por propiedad resulta especialmente útil si decide usar un tipo de propiedad de Microsoft .NET Framework o de alguna otra biblioteca donde no se puede controlar la definición de clase y no se puede aplicar un TypeConverterAttribute.

Vea también