Cómo: Implementar un convertidor de tipos
Se puede utilizar un convertidor de tipos para convertir valores entre tipos de datos, y ayudar en la configuración de propiedades en tiempo de diseño al proporcionar una conversión de texto a valor o una lista desplegable de valores que se pueden seleccionar. Un convertidor de tipos, si se configura correctamente, puede producir un código de configuración de propiedades utilizando InstanceDescriptor y objetos de System.Reflection para proporcionar al sistema de serialización del diseñador la información necesaria para generar código que inicialice la propiedad en tiempo de ejecución.
Convertidores de tipos para la traducción de valores
Los convertidores de tipos se utilizan principalmente para conversiones o traducciones de cadena en valor y para tipos de datos compatibles en tiempo de diseño y en tiempo de ejecución. En un host, tal como un explorador de propiedades de un diseñador de formularios, los convertidores de tipos permiten representar un valor de propiedad como texto para el usuario, y pueden convertir el texto escrito por el usuario en un valor del tipo de datos apropiado.
La mayoría de los tipos de datos nativos (Int32, String, tipos de enumeración, etc.) tienen convertidores de tipos predeterminados que permiten realizar conversiones de cadena en valor y comprobaciones de validación. Los convertidores de tipos predeterminados se encuentran en el espacio de nombres System.ComponentModel y se denominan nombreDeConvertidorDeTiposConverter. Cuando la funcionalidad predeterminada no se ajuste a sus necesidades, puede extender un convertidor de tipos, o puede implementar un convertidor de tipos personalizado si define un tipo personalizado que no tiene asociado ningún convertidor de tipos.
Nota
Generalmente, un atributo TypeConverterAttribute se aplica a una propiedad o a un miembro de datos para asociarlo a un convertidor de tipos. Si TypeConverterAttribute se aplica a un tipo, no es necesario volver a aplicarlo a las propiedades o a los miembros de datos de ese tipo.
La implementación de un convertidor de tipos no guarda relación con la funcionalidad de la interfaz de usuario. De ahí que el mismo convertidor de tipos se pueda aplicar tanto en formularios Windows Forms como en formularios Web Forms.
Para implementar un convertidor de tipos sencillo que pueda convertir una cadena a un punto
Defina una clase que se derive de TypeConverter.
Reemplace el método CanConvertFrom que especifica desde qué tipo puede realizar la conversión el convertidor. Este método está sobrecargado.
Reemplace el método ConvertFrom que implementa la conversión. Este método está sobrecargado.
Reemplace el método CanConvertTo que especifica desde qué tipo puede realizar la conversión el convertidor. No es necesario reemplazar este método si el destino de la conversión es un tipo de cadena. Este método está sobrecargado.
Reemplace el método ConvertTo que implementa la conversión. Este método está sobrecargado.
Reemplace el método IsValid que realiza la validación. Este método está sobrecargado.
El ejemplo de código siguiente implementa un convertidor de tipos que convierte un tipo String en un tipo Point y Point en String. En el ejemplo, no se reemplazan los métodos CanConvertTo y IsValid.
Option Explicit
Option Strict
Imports System
Imports System.ComponentModel
Imports System.Globalization
Imports System.Drawing
Public Class PointConverter
Inherits TypeConverter
' Overrides the CanConvertFrom method of TypeConverter.
' The ITypeDescriptorContext interface provides the context for the
' conversion. Typically, this interface is used at design time to
' provide information about the design-time container.
Public Overrides Overloads Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
If sourceType Is GetType(String) Then
Return True
End If
Return MyBase.CanConvertFrom(context, sourceType)
End Function
' Overrides the ConvertFrom method of TypeConverter.
Public Overrides Overloads Function ConvertFrom(context As ITypeDescriptorContext, culture As CultureInfo, value As Object) As Object
If TypeOf value Is String Then
Dim v As String() = CStr(value).Split(New Char() {","c})
Return New Point(Integer.Parse(v(0)), Integer.Parse(v(1)))
End If
Return MyBase.ConvertFrom(context, culture, value)
End Function
' Overrides the ConvertTo method of TypeConverter.
Public Overrides Overloads Function ConvertTo(context As ITypeDescriptorContext, culture As CultureInfo, value As Object, destinationType As Type) As Object
If destinationType Is GetType(String) Then
Return CType(value, Point).X & "," & CType(value, Point).Y
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
End Class
using System;
using System.ComponentModel;
using System.Globalization;
using System.Drawing;
public class PointConverter : TypeConverter {
// Overrides the CanConvertFrom method of TypeConverter.
// The ITypeDescriptorContext interface provides the context for the
// conversion. Typically, this interface is used at design time to
// provide information about the design-time container.
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType) {
if (sourceType == typeof(string)) {
return true;
}
return base.CanConvertFrom(context, sourceType);
}
// Overrides the ConvertFrom method of TypeConverter.
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value) {
if (value is string) {
string[] v = ((string)value).Split(new char[] {','});
return new Point(int.Parse(v[0]), int.Parse(v[1]));
}
return base.ConvertFrom(context, culture, value);
}
// Overrides the ConvertTo method of TypeConverter.
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType) {
if (destinationType == typeof(string)) {
return ((Point)value).X + "," + ((Point)value).Y;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
Convertidores de tipos que proporcionan una lista de valores estándar a la ventana Propiedades
Un convertidor de tipos puede proporcionar una lista de valores para un tipo de datos en un control de la ventana Propiedades. Cuando un convertidor de tipos proporciona un conjunto de valores estándar para un tipo, el campo de entrada para una propiedad del tipo asociado en un control de la ventana Propiedades, muestra una flecha abajo; al hacer clic en ella se muestra una lista de valores con los que establecer el valor de la propiedad.
Cuando se selecciona una propiedad del tipo con el que está asociado este convertidor de tipos, en un explorador de propiedades del entorno en tiempo de diseño, el campo de entrada del valor contendrá un botón que muestra una lista desplegable de los valores estándar para el tipo de propiedad de la que se puede seleccionar un valor.
Para implementar un convertidor de tipos sencillo que proporciona una lista desplegable de valores estándar en un explorador de propiedades
Defina una clase que se derive de TypeConverter.
Reemplace el método GetStandardValuesSupported y devuelva el valor true.
Reemplace el método GetStandardValues y devuelva una colección TypeConverter.StandardValuesCollection que contenga los valores estándar para el tipo de propiedad. Los valores estándar para una propiedad deben ser del mismo tipo que la propiedad.
Reemplace el método CanConvertFrom y devuelva el valor true para un valor de parámetro sourceType de tipo cadena.
Reemplace el método ConvertFrom y devuelva el valor apropiado para la propiedad según el parámetro value.
Aplique un objeto TypeConverterAttribute que indique el tipo de su convertidor de tipos al tipo para el que esté proporcionando un conjunto de valores estándar.
El siguiente ejemplo muestra un convertidor de tipos que proporciona una lista de valores estándar a un control de la ventana Propiedades para una propiedad del tipo al que se asocia. El convertidor de tipos de ejemplo es compatible con propiedades de tipo entero a las que se ha asociado. Para utilizar el ejemplo en Visual Studio, compile el código en una biblioteca de clases y agregue el componente IntStandardValuesControl al Cuadro de herramientas. Agregue una instancia de IntStandardValuesControl a un formulario en modo de diseño, y desplácese a la propiedad TestInt de la ventana Propiedades mientras está seleccionado el control. Al seleccionar el campo de entrada de valor de la propiedad se muestra una flecha abajo que muestra una lista desplegable de valores estándar cuando se hace clic sobre ella. Al introducir un valor entero se agregará el valor a la lista de valores estándar y se establece la propiedad en el valor especificado.
using System;
using System.ComponentModel;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
namespace StandardValuesTest
{
public class StandardValuesIntConverter : System.ComponentModel.TypeConverter
{
private ArrayList values;
public StandardValuesIntConverter()
{
// Initializes the standard values list with defaults.
values = new ArrayList(new int[] { 1, 2, 3, 4, 5 });
}
// Indicates this converter provides a list of standard values.
public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context)
{
return true;
}
// Returns a StandardValuesCollection of standard value objects.
public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context)
{
// Passes the local integer array.
StandardValuesCollection svc =
new StandardValuesCollection(values);
return svc;
}
// Returns true for a sourceType of string to indicate that
// conversions from string to integer are supported. (The
// GetStandardValues method requires a string to native type
// conversion because the items in the drop-down list are
// translated to string.)
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
if( sourceType == typeof(string) )
return true;
else
return base.CanConvertFrom(context, sourceType);
}
// If the type of the value to convert is string, parses the string
// and returns the integer to set the value of the property to.
// This example first extends the integer array that supplies the
// standard values collection if the user-entered value is not
// already in the array.
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if( value.GetType() == typeof(string) )
{
// Parses the string to get the integer to set to the property.
int newVal = int.Parse((string)value);
// Tests whether new integer is already in the list.
if( !values.Contains(newVal) )
{
// If the integer is not in list, adds it in order.
values.Add(newVal);
values.Sort();
}
// Returns the integer value to assign to the property.
return newVal;
}
else
return base.ConvertFrom(context, culture, value);
}
}
// Provides a test control with an integer property associated with
// the StandardValuesIntConverter type converter.
public class IntStandardValuesControl : System.Windows.Forms.UserControl
{
[TypeConverter(typeof(StandardValuesIntConverter))]
public int TestInt
{
get
{
return this.integer_field;
}
set
{
if(value.GetType() == typeof(int))
this.integer_field = value;
}
}
private int integer_field = 0;
public IntStandardValuesControl()
{
this.BackColor = Color.White;
this.Size = new Size(472, 80);
}
// OnPaint override displays instructions for the example.
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
if(this.DesignMode)
{
e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Blue), 5, 5);
e.Graphics.DrawString("The type converter for the TestInt property of this", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 20);
e.Graphics.DrawString("component provides a list of standard values to the", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 30);
e.Graphics.DrawString("Properties window. Setting a value through a property", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 40);
e.Graphics.DrawString("grid adds it to the list of standard values.", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 50);
}
else
{
e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Blue), 5, 5);
e.Graphics.DrawString("This control was intended for use in design mode.", new Font(FontFamily.GenericMonospace, 10), new SolidBrush(Color.Black), 5, 20);
}
}
}
}
Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Collections
Imports System.Drawing
Imports System.Windows.Forms
Namespace StandardValuesTest
Public Class StandardValuesIntConverter
Inherits System.ComponentModel.TypeConverter
Private values As ArrayList
Public Sub New()
' Initializes the standard values list with defaults.
values = New ArrayList(New Integer() {1, 2, 3, 4, 5})
End Sub 'New
' Indicates this type converter provides a list of standard values.
Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
Return True
End Function 'GetStandardValuesSupported
' Returns a StandardValuesCollection of standard value objects.
Public Overloads Overrides Function GetStandardValues(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.ComponentModel.TypeConverter.StandardValuesCollection
' Passes the local integer array.
Dim svc As New StandardValuesCollection(values)
Return svc
End Function 'GetStandardValues
' Returns true for a sourceType of string to indicate that
' conversions from string to integer are supported. (The
' GetStandardValues method requires a string to native type
' conversion because the items in the drop-down list are
' translated to string.)
Public Overloads Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
If sourceType Is GetType(String) Then
Return True
Else
Return MyBase.CanConvertFrom(context, sourceType)
End If
End Function 'CanConvertFrom
' If the type of the value to convert is string, parses the string
' and returns the integer to set the value of the property to.
' This example first extends the integer array that supplies the
' standard values collection if the user-entered value is not
' already in the array.
Public Overloads Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
If value.GetType() Is GetType(String) Then
' Parses the string to get the integer to set to the property.
Dim newVal As Integer = Integer.Parse(CStr(value))
' Tests whether new integer is already in the list.
If Not values.Contains(newVal) Then
' If the integer is not in list, adds it in order.
values.Add(newVal)
values.Sort()
End If
' Returns the integer value to assign to the property.
Return newVal
Else
Return MyBase.ConvertFrom(context, culture, value)
End If
End Function 'ConvertFrom
End Class 'StandardValuesIntConverter
' Provides a test control with an integer property associated with the
' StandardValuesIntConverter type converter.
Public Class IntStandardValuesControl
Inherits System.Windows.Forms.UserControl
<TypeConverter(GetType(StandardValuesIntConverter))> _
Public Property TestInt() As Integer
Get
Return Me.integer_field
End Get
Set(ByVal Value As Integer)
If Value.GetType() Is GetType(Integer) Then
Me.integer_field = Value
End If
End Set
End Property
Private integer_field As Integer = 0
Public Sub New()
Me.BackColor = Color.White
Me.Size = New Size(472, 80)
End Sub 'New
' OnPaint override displays instructions for the example.
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
If Me.DesignMode Then
e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Blue), 5, 5)
e.Graphics.DrawString("The type converter for the TestInt property of this", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 20)
e.Graphics.DrawString("component provides a list of standard values to the", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 30)
e.Graphics.DrawString("Properties window. Setting a value through a property", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 40)
e.Graphics.DrawString("grid adds it to the list of standard values.", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 50)
Else
e.Graphics.DrawString("TypeConverter.GetStandardValues Example Control", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Blue), 5, 5)
e.Graphics.DrawString("This control was intended for use in design mode.", New Font(FontFamily.GenericMonospace, 10), New SolidBrush(Color.Black), 5, 20)
End If
End Sub 'OnPaint
End Class 'IntStandardValuesControl
End Namespace 'StandardValuesTest
Convertidores de tipos que generan código para la inicialización de propiedades en tiempo de ejecución
.NET Framework permite generar dinámicamente un código de inicialización de propiedades en tiempo de diseño, que inicializará una propiedad en tiempo de ejecución.
Los programadores pueden compilar un convertidor de tipos que produzca un código de inicialización basado en constructores. Estos convertidores de tipos pueden generar código de constructor dinámicamente, mediante valores establecidos en tiempo de diseño para configurar propiedades de un tipo en tiempo de ejecución. El convertidor de tipos implementa la lógica para configurar el tipo y los valores de un constructor para la propiedad.
Si se necesita generar código además de un constructor para inicializar una propiedad, es posible generar código dinámicamente mediante la implementación de un objeto CodeDomSerializer personalizado y la aplicación de un objeto DesignerSerializerAttribute que asocie su objeto CodeDomSerializer para un tipo con el tipo al que pertenece la propiedad. Normalmente, este enfoque sólo se utiliza para escenarios en los que es importante la generación de código personalizado o controlado dinámicamente para la inicialización de componentes. Para obtener más información sobre este enfoque, consulte la documentación de CodeDomSerializer.
Para compilar un inicializador de propiedades personalizado basado en constructores, se debe asociar un convertidor de tipos al tipo de la propiedad que se va a inicializar, y el convertidor de tipos debe ser capaz de convertirse en un InstanceDescriptor.
Para implementar un convertidor de tipos que produzca un código de inicialización basado en constructores.
Defina una clase que se derive de TypeConverter.
Reemplace el método CanConvertTo. Si el parámetro destinationType es igual al tipo InstanceDescriptor, devuelva true.
Reemplace el método ConvertTo. Si el parámetro destinationType es igual al tipo InstanceDescriptor, construya y devuelva un InstanceDescriptor que represente al constructor y argumentos de constructor para los que generar código. Para crear un objeto InstanceDescriptor que represente el constructor y los parámetros adecuados, tiene que obtener un objeto ConstructorInfo a partir del objeto Type de la propiedad que esté inicializando por medio de una llamada al método GetConstructor o GetConstructors con la firma de método apropiada del constructor que esté buscando. A continuación, tiene que crear un nuevo descriptor de instancias y pasar el objeto ConstructorInfo para el tipo que representa el tipo de constructor que se va a utilizar junto con una matriz de objetos de parámetros que coinciden con la firma del constructor.
El ejemplo siguiente implementa un convertidor de tipos que puede generar código de inicialización de propiedades basado en constructores para propiedades del tipo Point.
public class PointConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType)
{
// Insert other ConvertTo operations here.
//
if (destinationType == typeof(InstanceDescriptor) &&
value is Point)
{
Point pt = (Point)value;
ConstructorInfo ctor = typeof(Point).GetConstructor(
new Type[] {typeof(int), typeof(int)});
if (ctor != null)
{
return new InstanceDescriptor(ctor, new object[] {pt.X, pt.Y});
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
Compilar el código
- Cuando desarrolle su TypeConverter personalizado, es recomendable que establezca el número de compilación para incrementarlo con cada compilación. Esto impide que las versiones anteriores, que se encuentran almacenadas en la memoria caché de su TypeConverter, se creen en el entorno de diseño.