Este sitio utiliza cookies de Google para prestar sus servicios y analizar su tráfico. Tu dirección IP y user-agent se comparten con Google, junto con las métricas de rendimiento y de seguridad, para garantizar la calidad del servicio, generar estadísticas de uso y detectar y solucionar abusos.Más información

View site in english Ir a la página de inicio Contacta conmigo
viernes, 10 de junio de 2016

El control PropertyGrid, conversores de tipo

Para terminar esta serie sobre el control PropertyGrid vamos a ver cómo utilizar los diferentes conversores de tipo derivados de la clase TypeConverter para personalizar la forma en que se muestran o editan las propiedades. El .NET framework proporciona conversores para bastantes tipos de propiedades, pero, además, podemos implementar fácilmente nuestros propios conversores, derivando una clase de TypeConverter o de algún conversor especializado y sobrecargando unas pocas funciones.

Si prefieres empezar esta serie desde el principio, en este enlace tienes el artículo sobre los conceptos básicos del control PropertyGrid.

En este enlace puedes descargar el código fuente de la versión final del proyecto sobre el control PropertyGrid que utilizaré en este artículo, escrito en csharp con Visual Studio 2013.

Conversores estándar

Para empezar, vamos a aplicar uno de los conversores estándar del .NET Framework que nos permitirá desplegar las propiedades de una propiedad (valga la redundancia) de tipo complejo para editarlas una por una por separado.

La clase que utilizaremos es ExpandableTypeConverter, y, como siempre, utilizaremos un atributo para decorar la propiedad que deseamos expandir, en este caso de la clase Shape. El atributo TypeConverter:

[CategoryGlobal("CAT_CUSTOMEDITORS")]
[DisplayNameGlobal("NAME_SHAPEPROPERTY")]
[DescriptionGlobal("DESC_SHAPEPROPERTY")]
[TypeConverter(typeof(ExpandableObjectConverter))]
[Editor(typeof(ShapeEditor), typeof(UITypeEditor))]
public Shape ShapeProperty { get; set; }

Y este es el aspecto que presentará el control PropertyGrid:

Miembros de la propiedad desplegados
Miembros de la propiedad desplegados

Podéis ver que también se han traducido las propiedades de la clase Shape, a las que aplicamos también los atributos globalizados.

Conversores de tipo personalizados

El control PropertyGrid permite editar las propiedades del objeto mediante editores simples de texto. Esto hace que debamos implementar un conversor de tipo que transforme un string en el tipo de referencia, para procesar y aceptar la entrada del usuario, y el tipo de referencia en un string para mostrarlo en el control.

El procedimiento es simple. En primer lugar, derivamos una clase de TypeConverter, o de alguna de sus clases derivadas, y reemplazaremos las funciones necesarias para implementar la funcionalidad que deseamos. Vamos a ver un ejemplo de conversor que nos permitirá mostrar al usuario una propiedad de colección IEnumerable como una lista de sus tres valores iniciales, la clase EnumerableTypeConverter.

Reemplazamos el método CanConvertFrom para indicar que el conversor puede convertir el tipo a partir de un objeto string:

public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}

Simplemente devolveremos true cuando el tipo indicado en sourceType sea uno de los aceptados por nuestro conversor.

Otro método que debemos reemplazar es ConvertFrom. Este método se utiliza cuando debemos convertir la entrada del usuario al tipo de la propiedad correspondiente:

public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value.GetType() == typeof(string))
{
return context.PropertyDescriptor.GetValue(context.Instance);
}
return base.ConvertFrom(context, culture, value);
}

En este caso, no queremos que el usuario pueda cambiar el valor de la propiedad mediante la edición directa, sino simplemente mostrarle la lista en un formato personalizado, así que simplemente devolveremos el valor actual de la propiedad, obteniendo el PropertyDescriptor del parámetro context. La propiedad Instance de context representa el objeto que estamos editando en el control actualmente.

El otro método que debemos reemplazar es ConvertTo, que proporcionará una cadena personalizada construida a partir del valor actual de la propiedad:

public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture,
object value, Type destinationType)
{
if (destinationType == typeof(string))
{
IEnumerable ienum = value as IEnumerable;
if (ienum != null)
{
int cnt = 0;
string result = "";
string c = "";
foreach (object obj in ienum)
{
if (cnt < 3)
{
result += c + obj.ToString();
c = ", ";
}
cnt++;
if (cnt > 3)
{
result += "...";
break;
}
}
return result;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}

En este caso, comprobamos que el objeto pasado en value tenga el interfaz IEnumerable y creamos una cadena de texto con los tres primeros elementos de la colección separados por comas. Si la colección contiene más de tres elementos, añadimos tres puntos al final de la cadena para indicarlo.

Como siempre, decoramos la propiedad con el atributo TypeConverter:

[CategoryGlobal("CAT_COMPOSITEPROPERTIES")]
[DisplayNameGlobal("NAME_COLLECTIONPROPERTY")]
[DescriptionGlobal("DESC_COLLECTIONPROPERTY")]
[TypeConverter(typeof(EnumerableTypeConverter))]
public List<string> CollectionProperty { get; set; }

Y este es el resultado obtenido en el PropertyGrid:

Propiedad de colección personalizada
Propiedad de colección personalizada

Mostrar una lista de posibles valores al usuario

Otra utilidad que podemos obtener de los conversores de tipos es la de mostrar al usuario una lista desplegable con los posibles valores que puede tener la propiedad. En este ejemplo editaremos una propiedad de tipo int a partir de una lista con los valores permitidos, mediante la clase IntRangeTypeConverter.

El primer método a reemplazar es GetStandardValuesSupported, donde simplemente debemos devolver true:

public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}

Si además queremos, como en este caso, que el usuario deba seleccionar exclusivamente un número de la lista, habrá que reemplazar GetStandardValuesExclusive:

public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}

La lista de valores la proporcionamos reemplazando el método GetStandardValues, que devuelve un objeto de tipo StandardValuesCollection construido a partir de una colección que debe ser del mismo tipo que el tipo de la propiedad editada, en esta caso, de tipo int:

public override StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(new int[6] { 0, 10, 20, 30, 40, 50 });
}

Como en el caso anterior, además debemos reemplazar los métodos CanConvertFrom, ConvertFrom y ConvertTo.

Decoramos la propiedad a la que queremos aplicar el conversor:

[CategoryGlobal("CAT_SIMPLEPROPERTIES")]
[DisplayNameGlobal("NAME_INTPROPERTY")]
[DescriptionGlobal("DESC_INTPROPERTY")]
[DefaultValue(0)]
[TypeConverter(typeof(IntRangeTypeConverter))]
public int IntProperty { get; set; }

Y este es el resultado en el control PropertyGrid:

Propiedad de tipo int con un conjunto limitado de valores
Propiedad int con un conjunto limitado de valores

Traducción de valores enum

El último conversor que vamos a implementar nos permitirá mostrar al usuario los valores de una propiedad de tipo enum traducidos al lenguaje actual. En este caso vamos a derivar el conversor de la clase EnumConverter, para aprovechar la funcionalidad de este editor especializado en enumeraciones. Utilizaremos el archivo de recursos para proporcionar las cadenas de texto traducidas para cada miembro de la enumeración.

Aunque podemos implementar un conversor genérico, en este caso estará especializado en enumeraciones del tipo ValueEnum, indicándolo en el constructor de la clase:

public class ValueEnumTranslateConverter : EnumConverter
{
public ValueEnumTranslateConverter()
: base(typeof(ValueEnum))
{
}

Como en los ejemplos anteriores, reemplazaremos los métodos CanConvertFrom, ConvertFrom y ConvertTo. En ConvertFrom, compararemos la entrada del usuario con las traducciones de la enumeración para obtener el valor correspondiente de la enumeración:

public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
string v = value as string;
if (v != null)
{
if (string.Compare(v, Resources.VE_ONE, true) == 0)
{
return ValueEnum.One;
}
else if (string.Compare(v, Resources.VE_TWO, true) == 0)
{
return ValueEnum.Two;
}
else if (string.Compare(v, Resources.VE_THREE, true) == 0)
{
return ValueEnum.Three;
}
else
{
return context.PropertyDescriptor.GetValue(context.Instance);
}
}
return base.ConvertFrom(context, culture, value);
}

Y en ConvertTo, convertiremos el valor de la propiedad en la cadena correspondiente al idioma actual:

public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
switch ((ValueEnum)value)
{
case ValueEnum.One:
return Resources.VE_ONE;
case ValueEnum.Two:
return Resources.VE_TWO;
default:
return Resources.VE_THREE;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}

Decoramos la propiedad correspondiente con el atributo TypeConverter:

[CategoryGlobal("CAT_COMPOSITEPROPERTIES")]
[DisplayNameGlobal("NAME_ENUMPROPERTY")]
[DescriptionGlobal("DESC_ENUMPROPERTY")]
[DefaultValue(ValueEnum.One)]
[TypeConverter(typeof(ValueEnumTranslateConverter))]
public ValueEnum EnumProperty { get; set; }

Y este es el resultado en el control PropertyGrid:

Propiedad enum traducida
Propiedad enum traducida
Comparte este artículo: Compartir en Twitter Compártelo en Facebook Compartir en Google Plus Compartir en LinkedIn
Comentarios (1):
* (Su comentario será publicado después de la revisión)

E-Mail


Nombre


Web


Mensaje


CAPTCHA
Change the CAPTCHA codeSpeak the CAPTCHA code