El control PropertyGrid, conceptos básicos
Uno de los controles del GUI (interfaz gráfico de usuario) más versátiles que nos ofrece la .NET framework es el control PropertyGrid. Se trata de un componente mediante el cual el usuario puede editar todas las propiedades de un determinado objeto que el desarrollador haya configurado para ello, permitiendo estructurarlas en categorías y ordenarlas por orden alfabético. Además, proporciona muchas opciones para personalizar tanto la presentación como la edición de las propiedades y muestra al usuario un texto de ayuda sobre cada una de ellas. En esta serie vamos a ver un tutorial completo sobre el uso de este control y sus características principales, empezando por este artículo sobre su uso básico.
En este enlace podéis descargar el proyecto con el código de ejemplo del control PropertyGrid, realizado con Visual Studio 2013 que usaré en este artículo.
Este control no se encuentra instalado por defecto en el cuadro de herramientas de Visual Studio, por lo que el primer paso que hay que dar es añadirlo. Para ello, basta con pulsar con el botón derecho sobre el mismo y seleccionar la opción Elegir Elementos…
En la pestaña Componentes de .NET Framework buscaremos el control PropertyGrid y lo marcaremos para que se instale. Aparecerá en la sección Todos los formularios Windows Forms del cuadro de herramientas.
Ahora ya podemos arrastrarlo sobre nuestro formulario, este es el aspecto que presenta:
En la parte superior se encuentra la barra de herramientas, que nos permite mostrar las propiedades del objeto clasificadas por categorías o bien por orden alfabético. La parte central estará ocupada por las propiedades del objeto, mientras que en la parte inferior se mostrará la ayuda sobre la propiedad que el usuario tenga seleccionada en el control.
Este es el aspecto que presentará el control una vez que lancemos el programa:
Configuración de los objetos para mostrarlos en el PropertyGrid
La configuración de la clase del objeto que vamos a mostrar en el control es muy sencilla, y se realiza por medio de atributos, la mayoría de ellos en el ensamblado System.ComponentModel, por lo que lo primero que debemos incluir es la instrucción using correspondiente:
using System.ComponenModel;
El primer atributo que veremos es Browsable, con este atributo podemos indicar qué propiedades queremos mostrar y cuáles no. Por defecto, se mostrarán todas las propiedades public, por lo que solamente necesitamos usar este atributo con las propiedades que no queremos mostrar al usuario, de esta manera:
[Browsable(false)]
public string NonBrowsable { get; set; }
Podemos clasificar las distintas propiedades en categorías con el atributo Category, para mostrarlas agrupadas, de la siguiente manera:
[Category("Nombre de la categoría")]
public string StringProperty { get; set; }
El atributo se debe añadir a todas las propiedades. Las que no estén decoradas con él se mostrarán agrupadas en una categoría por defecto con el nombre Varios.
Por defecto, las propiedades se mostrarán al usuario con el nombre que les hayamos dado. Podemos cambiar este comportamiento mediante el atributo DisplayName, indicando el nombre que queremos darle, que puede contener espacios y símbolos no permitidos en los nombres de propiedades:
[Category("Nombre de la categoría")]
[DisplayName("Nombre para mostrar al usuario")]
public string StringProperty { get; set; }
También podemos añadir un texto de ayuda que proporcione información al usuario sobre el uso de la propiedad en cuestión, usando el atributo Description:
[Category("Nombre de la categoría")]
[DisplayName("Nombre para mostrar al usuario")]
[Description("Texto de ayuda")]
public string StringProperty { get; set; }
Los valores de las propiedades se mostrarán en negrita por defecto, podemos indicar un valor por defecto para la propiedad con el atributo DefaultValue, cuando la propiedad tenga el valor indicado en el atributo, el texto aparecerá en letra normal, y solo aparecerá en negrita cuando el usuario lo cambie por otro, lo cual le puede servir de ayuda para saber que valores ha modificado:
[Category("Nombre de la categoría")]
[DisplayName("Nombre para mostrar al usuario")]
[Description("Texto de ayuda")]
[DefaultValue(0)]
public int IntProperty { get; set; }
En ocasiones, puede ser necesario que el usuario vea el valor de una propiedad pero no pueda cambiarlo. Para esto tenemos el atributo ReadOnly:
[ReadOnly(true)]
public string ReadOnlyProperty { get; set; }
También puede interesarnos que los valores de las propiedades del control se refresquen cuando el usuario modifique el valor de alguna propiedad determinada. Para conseguirlo, existe el atributo RefreshProperties, que debemos aplicar a la/s propiedad/es cuyo cambio de valor debe provocar el refresco de todo el control:
[Category("Nombre de la categoría")]
[DisplayName("Nombre para mostrar al usuario")]
[Description("Texto de ayuda")]
[RefreshProperties(RefreshProperties.All)]
public Size SizeProperty { get; set; }
Ahora, para que el control PropertyGrid muestre las propiedades del objeto, simplemente debemos asignárselo a la propiedad SelectedObject:
pgControl.SelectedObject = new DemoObject();
También existe la propiedad SelectedObjects que nos permite asignar al control una colección de objetos de manera que todas las propiedades comunes se pueden editar al mismo tiempo, asignándoles a todas el mismo valor al mismo tiempo cuando el usuario las modifica.
Edición de las propiedades en el control PropertyGrid
La mayoría de los tipos básicos de la plataforma .NET cuentan con editores por defecto apropiados para el tipo en cuestión, el más simple de los cuales es un editor de cadenas de texto, similar a un TextBox, con el que se editarán por ejemplo las propiedades de tipo string y todos los tipos numéricos en general.
Pero existen editores por defecto mucho más sofisticados para determinados tipos. Por ejemplo, una propiedad de tipo Color presenta una lista desplegable que nos permite seleccionar colores de forma similar al cuadro de diálogo ColorDialog:
Las propiedades de tipo enum presentarán una lista desplegable con los posibles valores de la enumeración:
Las estructuras y clases permiten expandir sus miembros públicos para que sean editados uno por uno:
Y las colecciones presentan un cuadro de diálogo que nos permite seleccionar y editar los objetos que contienen también mediante un PropertyGrid:
Un pequeño problema que presenta este control es que, al realizarse la configuración mediante atributos, no podemos cambiar el idioma en el que se presentan los textos al usuario. Para terminar este artículo, os voy a mostrar cómo podemos solucionar esto mediante el uso de atributos personalizados, derivando clases de los atributos básicos Category, DisplayName y Description.
Globalización del control PropertyGrid
Lo primero que tenemos que hacer es añadir un archivo de recursos a nuestro proyecto que será el que contenga las cadenas de texto en el lenguaje por defecto que prefiramos. En él definiremos todos los nombres de categoría, propiedades y textos de ayuda dándoles un nombre de recurso, de esta forma:
Las clases derivadas se encuentran en el archivo GlobalizedAttributes del proyecto, en ellas, el constructor acepta también una cadena de texto como argumento, pero en este caso se tratará del nombre del recurso que representa el texto a mostrar, en lugar del texto mismo. Después, solo hay que reemplazar el miembro apropiado para que lea la cadena desde el archivo de recursos de la aplicación. Por ejemplo, con el atributo Description, hacemos lo siguiente:
public class DescriptionGlobalAttribute : DescriptionAttribute
{
private bool m_bTranslate = true;
public DescriptionGlobalAttribute(string id)
: base(id)
{
}
public override string Description
{
get
{
if (m_bTranslate)
{
DescriptionValue =
Resources.ResourceManager.GetString(base.Description,
Thread.CurrentThread.CurrentCulture);
m_bTranslate = false;
}
return DescriptionValue;
}
}
}
Con esto, solo hay que cambiar los atributos estándar con los nuevos atributos globalizados y sustituir los textos por los nombres de los recursos:
[CategoryGlobal("CAT_SIMPLEPROPERTIES")]
[DisplayNameGlobal("NAME_STRINGPROPERTY")]
[DescriptionGlobal("DESC_STRINGPROPERTY")]
public string StringProperty { get; set; }
Una vez que hayamos terminado la aplicación, solo hay que crear un nuevo archivo de recursos para cada uno de los lenguajes. El archivo debe llamarse del mismo modo que el archivo de recursos por defecto, y se le debe añadir el nombre del lenguaje de los recursos que contiene, de esta manera:
Resources.resx (los recursos en el lenguaje por defecto)
Resources.es.resx (los recursos en español, por ejemplo)
etc.
Moveremos el archivo a la carpeta Properties, pegaremos todas las cadenas de texto del archivo de recursos por defecto y las traduciremos al lenguaje que corresponda.
Hay que tener en cuenta que los atributos se traducen solo la primera vez que se instancia la clase, por lo que si queremos cambiar el idioma una vez realizada la traducción, habrá que reiniciar la aplicación para que surta efecto el cambio (podemos guardar el nuevo lenguaje en el archivo .config y realizar el cambio al iniciar la aplicación leyéndolo desde el mismo), por ejemplo:
Thread.CurrentThread.CurrentCulture = new CultureInfo("es");
En el siguiente artículo os mostraré algunos eventos y editores de propiedades personalizados del control PropertyGrid.