31 Propiedades
Una propiedad es una pareja etiqueta = valor. Lo más habitual suele ser agrupar las propiedades en cuadrículas, llamadas cuadrículas de propiedades (property grids) o en hojas de propiedades (property sheets), y se suelen usar para la configuración de aplicaciones o documentos. Por ejemplo, Code::BLocks las usa para establecer las opciones de compilación de cada proyecto. También es frecuente encontrarlas en exploradores de archivos, para consultar o modificar opciones de ficheros, o para metadatos de diferentes tipos de documentos o imágenes.
Por supuesto, existen muchos tipos de propiedades: texto, números, fechas, imágenes, etc.
wxWidgets proporciona clases para varios tipos de propiedades, y además nos permite crear nuestras propias clases para manejar tipos de propiedades propios.
Estos controles se encapsulan clases derivadas de wxPGProperty, pero para usarlos también será necesario usar otras clases como wxPropertyGrid, wxPropertyGridPage o wxPropertyGridManager y las clases de que se derivan, como wxPropertyGridInterface
Además de las clases para añadir categorías o establecer una estructura en árbol.
Hay varias formas de agrupar propiedades. Las categorías se usan para agrupar propiedades por temas. Se muestran como una línea más de propiedades, pero con sólo la etiqueta y un botón a la izquierda que permite colapsar o expandir las propiedades que agrupa.
También se pueden agrupar por páginas. Esto es más útil cuando hay que editar muchas propiedades y no caben todas en un wxPropertyGrid.
Gráficamente, las etiquetas y los valores de todas las propiedades están separadas en dos columnas, y entre ellas hay un separador o "splitter", que permite al usuario desplazar esa separación para repartir el espacio entre ambas.
Se trata de un control complejo, ya que dispone de muchas opciones y funcionalidades. No mostraremos todas las opciones disponibles, sino únicamente las más útiles. Para más detalle puedes consultar la documentación de cada clase.
Tabla de propiedades
El control más simple para manejar propiedades es wxPropertyGrid, que agrupa las propiedades en una única página.
Estilos
Además de los estilos de las clases de las que deriva, como wxWindow o wxControl, estos controles también disponen de estilos propios, definidos en el tipo enumerado wxPG_WINDOW_STYLES.
El valor del estilo por defecto, wxPG_DEFAULT_STYLE, depende de la plataforma, no tiene un valor fijo.
- El estilo wxPG_AUTO_SORT ordena las propiedades para cada categoría, alfabéticamente, en lugar de en el orden por defecto, que es en que se añaden.
- El estilo wxPG_HIDE_CATEGORIES oculta las etiquetas de las categorías.
- wxPG_ALPHABETIC_MODE combina los dos estilos anteriores.
- Puede resultar muy útil usar el estilo wxPG_BOLD_MODIFIED, que resalta en negrita las propiedades que han sido modificadas por el usuario.
- Si queremos que el separador se sitúe siempre en el centro cuando cambie el tamaño del control tenemos que usar el estilo wxPG_SPLITTER_AUTO_CENTER.
- Con wxPG_STATIC_SPLITTER el usuario no podrá mover el separador.
- Activando el estilo wxPG_HIDE_MARGIN se ocultarán lo botones que permiten expandir y contraer categorías y propiedades compuestas.
- El estilo wxPG_STATIC_LAYOUT combina los dos estilos anteriores, por lo que el usuario no podrá modificar el diseño del control.
- wxPG_TOOLTIPS (completar).
- wxPG_LIMITED_EDITING desactiva la edición de texto si la propiedad se puede editar de otro modo. Por ejemplo, para editar colores se puede hacer mediante un control de selección de color, o editando la cadena, ya sea usando componentes RGB o con alguno de los colores predefinidos. Si se usa este estilo no se podrá editar la cadena.
También existen estilos extendidos que se activan mediante el método SetExtraStyle.
- wxPG_EX_HELP_AS_TOOLTIPS: muestra las cadenas de ayuda de las propiedades como tooltips en lugar de como texto en la barra de estado.
- wxPG_EX_NATIVE_DOUBLE_BUFFERING: permite confiar en el doble búfer nativo.
- wxPG_EX_AUTO_UNSPECIFIED_VALUES: permite al usuario establecer valores de propiedades en un estado no especificado. Equivale a establecer la bandera wxPGFlags::AutoUnspecified para todas las propiedades.
- wxPG_EX_WRITEONLY_BUILTIN_ATTRIBUTES: si se establece, los atributos integrados (como wxPG_FLOAT_PRECISION y wxPG_STRING_PASSWORD) no se almacenan como atributos de la propiedad (por lo que no son legibles).
hay otros estilos extendidos que no trataremos en este capítulo. Puedes consultarlos aquí.
Insertar el control PropertyGrid
Como en la mayor parte de los controles que ya hemos visto, el constructor tiene los parámetros habituales: ventana padre, identificador, la posición, tamaño, estilos, un nombre opcional.
wxPropertyGrid *prop = new wxPropertyGrid(this, idPropiedades, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE);
Será necesario insertar al menos una propiedad, ya que un control wxPropertyGrid sin propiedades no sirve de mucho.
Como comentamos más arriba, existen varias clases de propiedades, que veremos más abajo, pero todas derivan de la clase base wxPGProperty. Muchos de los métodos que veremos usan como argumento un puntero a un objeto de esta clase, o lo que es lo mismo, un parámetro del tipo wxPGPropArg, de forma polimórfica.
Categorías
El uso de categorías es opcional. Puede ser útil cuando tenemos que establecer el valor de varias propiedades, y algunas de ellas pueden agruparse según algún concepto. Las propiedades pertenecientes a una categoría pueden colapsarse o expandirse por el usuario o por código. Esto nos permite, por ejemplo, colapsar aquellas que normalmente no necesitarán ser modificadas, y mostrar en detalle sólo aquellas que más probablemente requieran atención por parte del usuario.
La clase wxPropertyCategory, que se usa para manejar categorías, deriva de wxPGProperty, aunque en rigor no se trata de una propiedad, puesto que sólo tiene etiqueta y no un valor.
Para añadir una categoría se usa el método Append usando como parámetro un puntero a un objeto de la clase wxPropertyCategory.
prop->Append( new wxPropertyCategory(_T("Datos personales")));
O también, si necesitamos mantener un puntero a la categoría:
wxPGProperty* cat = new wxPropertyCategory(_T("Datos personales"));
prop->Append(cat);
Cualquier propiedad que se inserte después de crear una categoría pertenecerá a esa categoría, hasta que se cree una nueva.
Podemos usar el método Collapse para colapsar una categoría, CollapseAll para colapsarlas todas, Expand para expandir una o ExpandAll para expandir todas.
Propiedades de cadena
Para las propiedades cuyo valor sea una cadena de texto usaremos la clase wxStringProperty. Todas las propiedades se añaden del mismo modo que las categorías, mediante el método Append:
prop->MAppend(new wxStringProperty(_T("NombrePila"), "nombre", "Fulanito"););
Como norma general, el segundo parámetro de Append será el nombre de la propiedad, que usaremos más adelante para recuperar el valor de cada una de las propiedades.
Si se usa la constante wxPG_LABEL, el valor del nombre será el mismo que se usa para la etiqueta, que es el primer parámetro.
Probablemente sea buena idea usar cadenas diferentes para la etiqueta y el nombre, sobre todo porque en español usaremos frecuentemente caracteres no ASCII, como acentos o eñes o con espacios, y el código será más fácil de leer si los nombres son cadenas C estándar.
Además, si decidimos que nuestra aplcación esté disponible en varios idiomas, las traducciones sólo afectarán a las etiquetas, y no a los nombres de las propiedades.
prop->MAppend(new wxStringProperty(_T("Nombre"), "nombre", _T("Fulanito")));
Propiedades compuestas
Una variante de este tipo de propiedades son las propiedades compuestas. Se crean a partir de una propiedad de cadena, pero en lugar de usar un valor inicial concreto, se usa la cadena especial "<composed>", para después añadir subpropiedades, que no tienen que ser necesariamente cadenas. Este tipo de propiedad puede ser editada por el usuario directamente, o mediante las subpropiedades. El valor de la propiedad compuesta se genera a partir de los valores de las subpropiedades, separadas con ';'.
Si se especifica el estilo wxPG_LIMITED_EDITING, sólo se podrán editar las subpropiedades.
Para añadir las subpropiedades se usa el método AppendIn, que funciona igual que Append , pero se añade un parámetro inicial con un puntero a la propiedad compuesta. Por ejemplo:
wxPGProperty* nombre = prop->Append( new wxStringProperty(_T("Nombre"), "nombrecompleto", "<composed>"));
prop->AppendIn(nombre, new wxStringProperty(_T("Apellido"), "apellido", "de Tal"));
prop->AppendIn(nombre, new wxStringProperty(_T("Nombre de pila"), "nombre", "Fulanito"));
De este modo, el valor que se muestra para la propiedad compuesta es "de Tal; Fulanito".
Atributos
Para establecer atributos se utilizan cadenas, aunque existen macros definidas para cada una de ellas, que es lo que usaremos.
Las propiedades de cadena disponen de un par de atributos propios:
wxPG_STRING_PASSWORD, o lo que es lo mismo, la cadena "Password", que si se establece con el valor "true" sustituye el valor del atributo por asteriscos, tanto al mostrarlo como al editarlo. Evidentemente, su propósito es almacenar contraseñas.
Para establecer el valor del atributo se usa el método SetAttribute.
wxStringProperty* pila = prop->AppendIn(nombre, new wxStringProperty(_T("Nombre"), "nombre", _T("Fulanito")));
pila->SetAttribute(wxPG_STRING_PASSWORD, true);
También se puede establecer el atributo wxPG_ATTR_AUTOCOMPLETE, que permite autocompletar el contenido de la propiedad a partir de un array de cadenas:
wxStringProperty* pila = prop->AppendIn(nombre, new wxStringProperty(_T("Nombre"), wxPG_LABEL, _T("Fulanito")));
wxArrayString opciones;
opciones.Add("Fulanito");
opciones.Add("Menganito");
opciones.Add("Tulanito");
opciones.Add("Pentanito");
pila->SetAttribute(wxPG_ATTR_AUTOCOMPLETE, opciones);
Propiedades numéricas
Para propiedades con valores numéricos se usa una de las clases derivadas de wxNumericProperty. Esta clase es abstracta, de modo que no se puede usar directamente, pero contiene algunos métodos comunes a todas las propiedades numéricas.
Hay tres clases predefinidas derivadas de ella:
- wxFloatProperty para números en coma flotante.
- wxIntProperty para números enteros con signo.
- wxUIntProperty para números enteros sin signo.
Para crear una propiedad de cualquiera de estas clases usaremos, como siempre, el método Append, o AppendIn si pertenece a una propiedad compuesta:
prop->Append(new wxIntProperty(_T("Número de zapato"), "zapato", 41));
Control SpinCtrl
Además del control de texto para editar el valor de la propiedad, podemos usar un control SpinCtrl, que son dos botones que permiten incrementar o decrementar el valor de la propiedad. Para activar este control es necesario registrar los editores adicionales, mediante una llamada al método estático RegisterAdditionalEditors, y activar el editor para el control, mediante el método SetPropertyEditor:
wxPropertyGrid::RegisterAdditionalEditors();
wxIntProperty* num = new wxIntProperty(_T("Número de zapato"), "zapato", 41);
prop->SetPropertyEditor( num, wxPGEditor_SpinCtrl );
prop->Append(num);
Atributos
Este tipo de propiedades tiene algunos atributos cuyos valores se pueden establecer, aunque algunos sólo tienen sentido si se usa el editor SpinCtrl:
- wxPG_ATTR_MIN para especificar el valor mínimo.
- wxPG_ATTR_MAX para especificar el valor máximo.
- wxPG_ATTR_SPINCTRL_STEP para establecer cuánto cambia el número cuando se pulsa el botón del editor SpinCtrl (o las teclas arriba/abajo del teclado). Cuando se usen esas teclas es conveniente pulsar también la tecla de mayúsculas.
- wxPG_ATTR_SPINCTRL_WRAP, si se establece a true, cuando se incremente y se sobrepase el valor máximo, se volverá al inicio del rango. Del mismo modo, cuando se decremente el valor, y se sobrepase el valor mínimo, se volverá al final del rango. El valor false impide no permite sobrepasar los límites.
- wxPG_ATTR_SPINCTRL_MOTION establece si el valor también se puede cambiar con el editor SpinCtrl moviendo el ratón mientras se mantiene pulsado el botón izquierdo. (Al menos en Linux esto no parece funcionar).
- wxPG_FLOAT_PRECISION permite establecer la precisión máxima del valor mostrado, es decir, el número de decimales. Por supuesto, sólo se aplica a la clase wxFloatProperty.
- Las propiedades de enteros sin signo admiten también el atributo wxPG_UINT_BASE para establecer la base de numeración a utilizar para mostrar y editar el valor, que admite los valores wxPG_BASE_OCT, wxPG_BASE_DEC, wxPG_BASE_HEX y wxPG_BASE_HEXL, para usar la base 8, 10, 16 ó 16 con minúsculas, respectivamente.
- Dado que se puede seleccionar la base para propiedades enteras sin signo, también se pueden seleccionar diferentes prefijos para indicar la base. Esto se hace mediante el atributo wxPG_UINT_PREFIX, que admite los valores wxPG_PREFIX_NONE, wxPG_PREFIX_0x y wxPG_PREFIX_DOLLAR_SIGN, para no usar prefijo, usar "0x" o "$", respectivamente. Para las bases 8 y 10 sólo se admite wxPG_PREFIX_NONE.
La forma de establecer estos atributos es similar en todos las propiedades, mediante el método SetAttribute:
wxPropertyGrid::RegisterAdditionalEditors();
wxIntProperty* num = new wxIntProperty(_T("Número de zapato"), "zapato", 41);
prop->SetPropertyEditor( num, wxPGEditor_SpinCtrl );
num->SetAttribute(wxPG_ATTR_MIN, 25);
num->SetAttribute(wxPG_ATTR_MAX, 52);
num->SetAttribute(wxPG_ATTR_SPINCTRL_STEP, 2);
num->SetAttribute(wxPG_ATTR_SPINCTRL_WRAP, true);
num->SetAttribute(wxPG_ATTR_SPINCTRL_MOTION, true);
prop->Append(num);
wxFloatProperty* flo = new wxFloatProperty(_T("Tolerancia"), wxPG_LABEL, 10.05);
prop->SetPropertyEditor( flo, wxPGEditor_SpinCtrl );
flo->SetAttribute(wxPG_FLOAT_PRECISION, 2);
flo->SetAttribute(wxPG_ATTR_SPINCTRL_STEP, 0.05);
prop->Append(flo);
wxUIntProperty* sinsig = new wxUIntProperty(_T("Sin signo"), wxPG_LABEL, 0x20);
sinsig->SetAttribute(wxPG_UINT_BASE, wxPG_BASE_HEX);
sinsig->SetAttribute(wxPG_UINT_PREFIX, wxPG_PREFIX_0x);
prop->Append(sinsig);
Propiedades de bit
Cuando una propiedad sólo pueda tomar dos valores se puede usar la clase wxBoolProperty.
Por defecto se muestra el texto "true" o "false", y para cambiar el valor se despliega un control choice.
Se añaden igual que todas las propiedades:
prop->Append(new wxBoolProperty(_T("Notificar"), "notificar", false));
Las variables booleanas pueden usarse para almacenar cualquier tipo de dato que sólo pueda tomar dos valores, no necesariamente verdadero/false. Si queremos usar cadenas diferentes a "true/false", podemos usar el método SetBoolChoices. Como este método pertenece a wxPropertyGridInterface, este cambio afectará a todas las propiedades de tipo booleano del wxPropertyGrid.
Atributos
Estas propiedades tienen dos atributos propios:
- wxPG_BOOL_USE_CHECKBOX: si se usa el valor true se usará un control checkbox en lugar de choice.
- wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING: si se asigna el valor true se puede usar doble clic para alternar los valores true y false..
Estos dos atributos son excluyentes, es decir, sólo uno o ninguno de ellos puede ser verdadero.
Propiedades de banderas
Es habitual que ciertos valores booleanos se almacenen agrupados como bits individuales en un entero. Para editar este tipo de propiedades se utiliza la clase wxFlagsProperty.
Estos controles pueden manejar tantas banderas como bits contenga un entero largo.
Para crearlos será necesario usar dos arrays, uno con las etiquetas correspondientes a cada bit, y otro con los valores. Por ejemplo, si queremos que se puedan editar los bits 2, 4, 5 y 7, tendríamos que usar algo como esto:
wxArrayString bits = {"Bit 2", "Bit 4", "Bit 5", "Bit 7"};
wxArrayInt vals = {2, 8, 16, 64};
prop->Append(new wxFlagsProperty(_T("Banderas"), "banderas", bits, vals, 66));
Ya que el valor del bit 2 es 2, el del bit 4 es 8, el del bit 5 es 16 y el del bit 7 el 64. El valor inicial de 66 corresponde a los bits 2 y 7 activos.
De forma automática se crea una subpropiedad de tipo wxBoolProperty para cada bit. de modo que se facilita mucho la edición de cada bandera.
Atributos
Esta clase de propiedades no tienen atributos propios. Para cada bit, la propiedad wxBoolProperty creada se comporta como si tuviera el atributo wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING activo, es decir, se puede alternar el valor de cada bit haciendo doble clic sobre él.
Propiedades con enumerados
La clase abstracta wxEnumProperty permite crear propiedades para las que el número de valores posibles sea predecible y relativamente pequeño, es decir, que el usuario tenga que elegir entre un conjunto de valores predefinidos.
Existen varias clases derivadas de wxEnumProperty que se pueden utilizar.
Selección de cursor
La clase wxCursorProperty permite elegir un cursor a partir de la lista de cursores predefinidos.
prop->Append(new wxCursorProperty(_T("Cursor"), "cursor"));
Selección de color de sistema
La clase wxSystemColourProperty permite seleccionar un color a partir de los colores del sistema. Este conjunto de colores es limitado, es el que se usa para puntar el texto, el fondo, los bordes de las ventanas, etc.
prop->Append(new wxSystemColourProperty(_T("SysColor"), "syscolor"));
Selección de color
La clase wxColourProperty permite seleccionar cualquier color. En este caso no se selecciona un color a partir de una lista, sino que se muestra un diálogo de selección de color.
prop->Append(new wxColourProperty(_T("Color"), "color"));
Atributos
Estas dos últimas propiedades admiten dos atributos propios.
- El atributo wxPG_COLOUR_ALLOW_CUSTOM, si se establece a verdadero, permite seleccionar un color personalizado. Este atributo sólo tiene sentido para la propiedad de color de sistema, ya que para la otra propiedad siempre se elige un color personalizado, y si se establece el valor, se ignora.
- El atributo wxPG_COLOUR_HAS_ALPHA, si se establece a verdadero, permite añadir la componente de transparencia, además de los componentes rojo, verde y azul.
prop->Append(new wxSystemColourProperty(_T("SysColor"), "syscolor"));
prop->GetProperty("syscolor")->SetAttribute(wxPG_COLOUR_ALLOW_CUSTOM, true);
prop->Append(new wxColourProperty(_T("Color"), "color"));
prop->GetProperty("color")->SetAttribute(wxPG_COLOUR_HAS_ALPHA, true);
Crear una propiedad enumerada
Las propiedades enumeradas funcionan de forma parecida a las de banderas.
Hay varias variantes que podemos usar, dependiendo de qué constructor usemos para crear la propiedad.
Array de cadenas
En la primera crearemos un array con las etiquetas de cada opción, y lo pasaremos como tercer parámetro al constructor, dejando el cuarto parámetro como un puntero nulo nullptr, o como un wxIntArray vacío. De este modo, el valor de la propiedad será el índice de la etiqueta seleccionada.
wxArrayString reinos;
reinos.Add(_T("Animal"));
reinos.Add(_T("Vegetal"));
reinos.Add(_T("Mineral"));
prop->Append(new wxEnumProperty(_T("Reino"), "reino", reinos, wxIntArray(), 1));
Para las etiquetas es indiferente si el array es un wxArrayString o un array C estándar.
Array de cadenas y de enteros
Si nos interesa que el entero asociado a cada etiqueta no sea su índice, sino otro valor numérico, podemos añadir un segundo array de enteros con los valores correspondientes para cada etiqueta.
wxArrayString letras;
letras.Add(_T("A"));
letras.Add(_T("B"));
letras.Add(_T("C"));
letras.Add(_T("D"));
wxArrayInt valor;
valor.Add(25);
valor.Add(32);
valor.Add(45);
valor.Add(56);
prop->Append(new wxEnumProperty(_T("Letra"), "letra", letras, valor, 45));
Usar wxPGChoices
La clase wxPGChoices encapsula los dos arrays, etiquetas y valores enteros, en un único objeto.
Hay varias formas de crear uno de estos objetos:
- Crear el objeto pasando como parámetros los dos arrays, de etiquetas y valores. Los arrays pueden ser arrays C o wxArrayString para etiquetas y wxArrayInt para valores.
- Podemos crear el objeto vacío , usando el método Add, añadir todas las opciones, o añadirlas una a una.
Si optamos por añadir las opciones una a una, de nuevo tenemos varias opciones:
- Usar la etiqueta y un valor opcional. Si se omite el valor se usará el correspondiente al orden en que se añadan las etiquetas.
- Usar un parámetro de la clase wxPGChoiceEntry, que contiene una etiqueta y un valor.
- Usar la variante que permite establecer una etiqueta, un mapa de bits y opcionalmente, un valor.
wxPGChoices choices;
choices.Add(_T("A"), wxBitmap(green_xpm), 25);
choices.Add(_T("B"), wxBitmap(blue_xpm), 32);
choices.Add(_T("C"), wxBitmap(green_xpm), 45);
choices.Add(_T("D"), wxBitmap(blue_xpm), 56);
prop->Append(new wxEnumProperty(_T("Otra letra"), "otraletra", choices));
Propiedad de fecha
Para establecer propiedades de tipo fecha se usa la clase wxDateProperty. Estas propiedades nos permiten establecer una fecha, ya sea mediante un editor de texto, o, si lo desplegamos con el botón a su derecha, mediante un control wxCalendarCtrl.
prop->Append( new wxDateProperty(_T("Fecha de nacimiento"), "fecha", wxDateTime(25, wxDateTime::Jul, 1996)));
Es muy probable que el formato de fecha utilizado en la cadena no sea el adecuado en nuestro caso, ya que por defecto se usa el formato "mm/dd/aaaa". Para cambiar este formato al que se use localmente según la configuración del sistema operativo tendremos que añadir algunas sentencias a nuestro método OnInit de nuestra clase derivada de wxApp. Por ejemplo, para establecer las opciones locales del sistema operativo bastará con invocar el método estático UseDefault de wxUILocale:
#include <wx/uilocale.h>
...
bool miApp::OnInit()
{
miFrame* frame = new miFrame(0L, _("Título"));
...
wxUILocale::UseDefault();
frame->Show();
return true;
}
Esto modificará además las cadenas usadas para los nombres de los meses y los días de la semana, entre otras cosas.
Atributos
Estas propiedades admiten dos atributos propios.
El atributo wxPG_DATE_FORMAT permite modificar el formato en el que se muestra la fecha. La cadena de formato se construye del mismo modo que con la función ANSI C strftime.
El atributo wxPG_DATE_PICKER_STYLE permite modificar el estilo del control que permite especificar la fecha. No todos los estilos están soportados en todas las plataformas, de modo que hay que ser cuidadoso si se quiere escribir un código portable.
#include <wx/datectrl.h>
...
prop->Append( new wxDateProperty(_T("Fecha de nacimiento"), "fecha", wxDateTime(25, wxDateTime::Jul, 1996)));
prop->GetProperty("fecha")->SetAttribute(wxPG_DATE_FORMAT, "%a %d %b %Y");
prop->GetProperty("fecha")->SetAttribute(wxPG_DATE_PICKER_STYLE,
(long)(wxDP_DEFAULT | wxDP_SHOWCENTURY) );
Propiedades con botón de despliegue de diálogo
La clase abstracta wxEditorDialogProperty se usa como clase base para aquellas propiedades que requieran mostrar un cuadro de diálogo para establecer su valor.
Contiene algunos métodos comunes para cosas como establecer el título del diálogo, o mostrarlo.
wxWidgets nos proporciona algunas clases derivadas de ella para manejar ciertas propiedades.
Atributos
Hay un atributo común a todas las clases derivadas de wxEditorDialogProperty.
El atributo wxPG_DIALOG_TITLE permite cambiar el título del cuadro de diálogo que se mostrará al editar la propiedad. Si no se establece, por defecto el título será la etiqueta de la propiedad.
Propiedades de array de cadenas
La clase wxArrayStringProperty permite establecer como valor de una propiedad una lista de cadenas:
wxArrayString lista;
lista.Add(_T("Uno"));
lista.Add(_T("Dos"));
lista.Add(_T("Tres"));
prop->Append( new wxArrayStringProperty(_T("Lista"), "lista", lista));
En la parte del valor se mostrará la lista separando las cadenas con un delimitador, que por defecto es la coma.
Si se pulsa en el botón de la derecha se mostrará un cuadro de diálogo con un control wxEditableListBox, que nos permitirá añadir, eliminar, editar o cambiar el orden de los elementos de la lista.
Atributos
Esta clase de propiedades dispone de un atributo propio, además del que comparte con el resto de clases derivadas de wxEditorDialogProperty.
wxPG_ARRAY_DELIMITER permite modificar el valor del carácter que se usará como delimitador entre cadenas. Si se usa un carácter comilla, ya sea sencilla o doble, las cadenas se mostrarán entre comillas, usando el carácter indicado.
prop->GetProperty("lista")->SetAttribute(wxPG_ARRAY_DELIMITER, '\'');
prop->GetProperty("lista")->SetAttribute(wxPG_DIALOG_TITLE, "Lista de cadenas");
Propiedad de directorio
La clase wxDirProperty permite establecer un directorio existente o crear uno nuevo, como valor de una propiedad.
prop->Append( new wxDirProperty(_T("Carpeta"), "carpeta", _T("/home/salvador")));
prop->GetProperty("carpeta")->SetAttribute(wxPG_DIALOG_TITLE, "Directorio de trabajo");
Propiedad de fichero
Para establecer el valor de una propiedad a un nombre de fichero se usa la clase wxFileProperty.
prop->Append( new wxFileProperty(_T("Fichero"), "fichero", _T("/home/salvador/temp.txt")));
Atributos
Esta clase, y sus derivadas, tienen algunos atributos propios:
- wxPG_FILE_DIALOG_STYLE permite establecer estilos wxFileDialog para el cuadro de diálogo, por ejemplo, wxFD_FILE_MUST_EXIST.
- wxPG_FILE_WILDCARD establece un comodín, por ejemplo "Ficheros de texto (*.txt)|*.txt"). Ver wxFileDialog para más detalles.
- wxPG_FILE_SHOW_FULL_PATH si es true, por defecto, muestra la ruta completa del fichero. Cuando escribo esto, la versión actual tiene un bug y este atributo no funciona. En su lugar se puede activar la bandera wxPGFlags::ShowFullFileName.
- wxPG_FILE_INITIAL_PATH establece la ruta inicial donde buscar los archivos. Si no se especifica, la ruta es la del ejecutable
- wxPG_FILE_SHOW_RELATIVE_PATH cuando se establece una ruta, el nombre del archivo se mostrará en relación con esa ruta.
prop->Append( new wxFileProperty(_T("Fichero"), "fichero", _T("temp.txt")));
prop->GetProperty("fichero")->SetAttribute(wxPG_DIALOG_TITLE, "Fichero de salida");
prop->GetProperty("fichero")->SetAttribute(wxPG_FILE_DIALOG_STYLE, wxFD_FILE_MUST_EXIST);
prop->GetProperty("fichero")->SetAttribute(wxPG_FILE_WILDCARD, _T("Ficheros de texto (*.txt)|*.txt"));
prop->GetProperty("fichero")->SetAttribute(wxPG_FILE_SHOW_RELATIVE_PATH, _T("/home/salvador/Programas"));
prop->GetProperty("fichero")->SetAttribute(wxPG_FILE_SHOW_FULL_PATH, false); // BUG
prop->GetProperty("fichero")->ChangeFlag(wxPGFlags::ShowFullFileName, true); // alternativa
prop->GetProperty("fichero")->SetAttribute(wxPG_FILE_INITIAL_PATH, _T("/home"));
Propiedad de fichero de imagen
La clase wxImageFileProperty está derivada de wxFileProperty, por lo que todo lo explicado para esa clase se puede aplicar a esta. La única diferencia es que permite mostrar la imagen a la izquierda del nombre cuando se establece un valor.
wxImage::AddHandler(new wxPNGHandler);
wxImage::AddHandler(new wxJPEGHandler);
...
prop->Append( new wxImageFileProperty(_T("Imagen"), "imagen"));
prop->GetProperty("imagen")->SetAttribute(wxPG_FILE_WILDCARD, wxT("PNG or JPG files (*.png;*.jpg)|*.png;*.jpg") );
Por supuesto, será necesario añadir los gestores de imágenes que queramos utilizar, en este ejemplo el de los formatos PNG y JPEG.
Atributos
Esta clase soporta los mismos atributos que wxFileProperty.
Propiedad de fuente
La clase wxFontProperty permite seleccionar una fuente de caracteres, así como sus características: tamaño, nombre, estilo, peso, subrallado y familia.
Se trata de una propiedad compuesta, es decir, se crea una subpropiedad para cada una de las características, que se pueden editar de forma separada, o en conjunto, en cuyo caso se abre un diálogo wxFontDialog.
prop->Append( new wxFontProperty(_T("Fuente"), "fuente"));
prop->GetProperty(("fuente")->SetAttribute(wxPG_DIALOG_TITLE, "Seleccionar una fuente");
Propiedad de cadena larga
La clase wxLongStringProperty permite introducir como valor de la propiedad un texto largo, que puede contener retornos de línea.
prop->Append( new wxLongStringProperty(_T("Texto"), "texto"));
prop->GetProperty("texto")->SetAttribute(wxPG_DIALOG_TITLE, "Escribe un texto");
Este tipo de propiedad sólo soporta el atributo wxPG_DIALOG_TITLE.
Propiedad de opciones múltiples
La clase wxMultiChoiceProperty es útil cuando el usuario debe poder elegir varias opciones de una lista.
Para insertar una de estas propiedades usaremos el método Append, especificando como tercer parámetro un array de cadenas con las opciones, y como cuarto parámetro, un array con el valor actual de la propiedad.
wxArrayString paises;
paises.Add(_T("España"));
paises.Add(_T("Portugal"));
paises.Add(_T("Italia"));
paises.Add(_T("Suiza"));
wxArrayString seleccion;
seleccion.Add(_T("Francia"));
seleccion.Add(_T("Italia"));
prop->Append( new wxMultiChoiceProperty(_T("Paises"), "paises", paises, seleccion));
prop->GetProperty("paises")->SetAttribute(wxPG_DIALOG_TITLE, _T("Selecciona países"));
prop->GetProperty("paises")->SetAttribute(wxPG_ATTR_MULTICHOICE_USERSTRINGMODE, 2);
Atributos
Además de poder cambiar el título del cuadro de diálogo que permite hacer la selección, wxPG_DIALOG_TITLE, esta propiedad dispone de un segundo atributo.
wxPG_ATTR_MULTICHOICE_USERSTRINGMODE admite tres valores:
- 0, que es el valor por defecto, no permite asignar valores que no estén en la lista de opciones.
- 1, permite valores que no estén en la lista, y los mostrará antes de cualquiera que sí lo esté.
- 2, permite valores que no estén en la lista, y los mostrará a continuación de cualquiera que sí lo esté.
Si se ha especificado el estilo wxPG_LIMITED_EDITING para el wxPropertyGrid, los valores que no estén en la lista no podrán añadirse ni eliminarse por el usuario, sólo se pueden asignar como en el caso de "Francia" en el ejemplo anterior. Si no es así, se podrá modificar el valor de la propiedad añadiendo opciones entre comillas dobles, aunque no estén en la lista de opciones.
Es posible usar un cuadro de diálogo personalizado, derivado de wxPGEditorDialogAdapter, que permite añadir opciones, pero en general esto no será necesario en un uso normal de estos controles.
Asignar un valor a una propiedad
Aunque es posible, como hemos visto, establecer un valor inicial en el constructor, también se puede hacer con posterioridad usando el método SetValue, pasando como primer parámetro el valor a establecer, que puede ser una cadena, un número, un booleano, etc, dependiendo del tipo de propiedad.
Por ejemplo, para una propiedad wxEnumProperty, como "letra" estas dos sentencias son equivalentes:
wxArrayString letras;
...
wxArrayInt valor;
...
wxEnumProperty* let = new wxEnumProperty(_T("Letra"), "letra", letras, valor);
prop->Append(let);
let->SetValue(45); // El entero asociado
let->SetValue(_T("C")); // La etiqueta
Para una propiedad de cadena se tendrá que pasar una cadena, para una numérica un número, etc.
Dado que el parámetro que usamos para establecer el valor es un wxVariant, la conversión al tipo adecuado es automática.
Si no hemos conservado un puntero a la propiedad, podemos obtenerlo mediante el método GetProperty:
prop->GetProperty("Letra")->SetValue(32);
El método SetValueFromString es equivalente a usar una cadena como parámetro en SetValue.
Por otra parte, el método SetValueFromInt, aunque pueda parecer que es equivalente, dependerá del tipo de propiedad. Para propiedades que manejen un valor entero, como wxUIntProperty o wxFlagsProperty, el comportamiento es similar, pero para propiedades como wxEnumProperty, el entero se refiere al índice y no al valor entero asociado. Esto es, para la propiedad "letra", el equivalente al ejemplo sería:
let->SetValue(2); // El índice asociado
Para propiedades con opciones también se puede usar SetChoiceSelection, indicando el índice a seleccionar.
El método SetValueToUnspecified establece el valor a nulo. No es lo mismo que asignar una cadena vacía o un valor cero. Esto tiene consecuencias diferentes en cada tipo de propiedad, así que hay que usarlo con cuidado. Por ejemplo, para propiedades wxFlagsProperty asignar ese valor impide activar banderas individuales.
Valores por defecto
A veces, cuando el usuario no establece un valor para una propiedad puede ser interesante establecer un valor por defecto válido.
Para ello disponemos del método SetDefaultValue, o lo que es lo mismo, establecer el atributo "DefaultValue", o wxPG_ATTR_DEFAULT_VALUE.
La idea es que si el usuario no ha seleccionado ninguna opción ni introducido ningún valor, se asigne a la propiedad el valor por defecto. Para recuperar ese valor se usa el método GetDefaultValue.
wxVariant defLetra(45);
prop->GetProperty("otraletra")->SetDefaultValue(defLetra);
...
valor = prop->GetProperty("otraletra")->GetValue();
if(valor.GetInteger()==0) {
valor = prop->GetProperty("otraletra")->GetDefaultValue();
}
wxMessageBox(valor, _T("Otra letra"));
Valores comunes
Esta parte de wxWidgets aún no está completa, por lo que no está documentada.
Tan sólo comentar que el método EnableCommonValue, permite añadir la opción "unspecifed" a ciertas propiedades.
Si se ha invocado a ese método para una propiedad, y el usuario selecciona esa opción, el método GetCommonValue retornará el valor 0, en cualquier otro caso retornará -1.
Obtener el valor de una propiedad
Por supuesto, en algún momento deberemos recuperar los valores de las propiedades y almacenarlos adecuadamente.
La forma más simple es usar el método GetValue para cada propiedad.
El valor devuelto por este método es un wxVariante, es decir, un objeto que permite almacenar cualquier tipo definido en wxWidgets.
wxVariant dispone de métodos para averiguar el tipo de objeto y para recuperar el valor en un objeto del tipo que más nos convenga.
wxVariant valor;
valor = prop->GetProperty("zapato")->GetValue();
static long v=valor.GetLong();
Si queremos obtener el valor de una propiedad subordinada de una compuesta, tendremos que usar, además de su nombre, el de sus antecesores, separados por puntos. Por ejemplo, para obtener el apellido deberemos usar la cadena "nombrecompleto.apellido":
wxVariant nombrecompleto = prop->GetProperty("nombrecompleto")->GetValue();
wxMessageBox(nombrecompleto, _T("Nombre"));
wxVariant apellido = prop->GetProperty("nombrecompleto.apellido")->GetValue();
wxMessageBox(apellido, _T("Apellido"));
Hay métodos específicos pensados para algunos tipos de propiedades.
Por ejemplo, para leer directamente el valor como una cadena, usando el método GetValueAsString
wxString nombre = prop->GetProperty("nombre")->GetValueAsString();
Este método siempre devuelve una cadena, haciendo al conversión adecuada, independientemente del tipo del valor. Es el que se usa internamente para mostrar el valor de la propiedad en pantalla.
El método GetChoiceSelection devuelve el índice de la opción seleccionada en una propiedad de tipo choice.
Podemos averiguar el tipo de una propiedad mediante el método GetValueType.
No especificado
Si la propiedad lo permite y no se ha especificado un valor, el método IsValueUnspecified retornará el valor true.
Para las propiedades wxIntProperty, wxUIntProperty, wxFloatProperty, wxEditEnumProperty se puede usar el método SetAutoUnspecified, con un parámetro true, para que el usuario pueda dejar el valor a "no especificado", borrando ese valor. No es lo mismo un valor no especificado que un valor cero.
Pistas
Se pueden establecer textos con pistas sobre el valor de una propiedad, que se mostrarán en gris cuando esta no tenga un valor asignado.
Para hacerlo hay que establecer el atributo wxPG_ATTR_HINT, indicando la cadena que queremos usar como pista.
prop->GetProperty("paises")->SetAttribute(wxPG_ATTR_HINT, _T("\"España\""));
Texto de ayuda
Hay dos formas de establecer el texto de ayuda: usando el método SetPropertyHelpString, indicando el identificador o puntero a la propiedad, y el texto a utilizar, o mediante el método SetHelpString, indicando sólo el texto de ayuda:
wxPGProperty* cat=prop->Append( new wxPropertyCategory(_T("Datos personales")));
prop->SetPropertyHelpString(cat, _T("Datos personales del usuario actual"));
cat->SetHelpString(_T("Datos personales del usuario actual")); // Estas dos líneas son equivalentes
Ese texto se mostrará por defecto en la barra de estado, si existe.
Si se utiliza el estilo wxPG_EX_HELP_AS_TOOLTIPS, se mostrará como un tooltip. Los estilos wxPG_EX* se activan mediante el método SetExtraStyle.
prop->SetExtraStyle( wxPG_EX_HELP_AS_TOOLTIPS );
Ocultar propiedades
Es posible que en determinadas situaciones queramos ocultar o mostrar determinadas propiedades. Para ello disponemos del método Hide, indicando como primer parámetro true para ocultar y false para mostrar la propiedad, y como segundo parámetro algunos indicadores de tipo wxPGPropertyValuesFlags, aunque sólo algunos de esos indicadores se pueden aplicar a este método. Lo más habitual es que usemos el valor por defecto para ese segundo parámetro, Recurse.
El método HideProperty es equivalente.
let->Hide(true);
prop->HideProperty(let, true); // Es equivalente
Habilitar o inhibir propiedades
De forma similar podemos habilitar o deshabilitar una propiedad. Una propiedad deshabilitada seguirá siendo visible, pero no podrá ser editada por el usuario.
El método Enable permite habilitar o deshabilitar una propiedad. SI el parámetro es true, la propiedad estará habilitada, si es false se deshabiliatará. Una propiedad deshabilitada se mostrará en gris.
rei->Enable(false);
También disponemos de un método para averiguar si una propiedad está habilitada, IsEnabled.
Expandir y colapsar propiedades
Las categorías o propiedades que tengan hijos se pueden expandir o colapsar mediante código, usando el método SetExpanded.
prop->GetProperty("banderas")->SetExpanded(true);
También podemos averiguar si una propiedad o categoría está expandida mediante IsExpanded.
Marca de modificado
Cada propiedad mantiene una marca que indica si el usuario ha modificado su valor. Si se activa el estilo wxPG_BOLD_MODIFIED para el wxPropertyGrid, este estado se muestra poniendo el valor en negrita.
Podemos activar esa marca desde el programa, aunque el usuario no haya modificado la propiedad mediante el método SetModifiedStatus.
sinsig->SetModifiedStatus(true);
No existen un método específico para saber si el valor de una propiedad ha sido modificado, pero podemos consultar la bandera wxPGFlags::Modified usando el método HasFlag:
if(prop->GetProperty("sinsigno")->HasFlag(wxPGFlags::Modified))
wxMessageBox(_T("Ha sido modificado"), _T("Entero sin signo"));
else
wxMessageBox(_T("No ha sido modificado"), _T("Entero sin signo"));
Iteradores
A la hora de iniciar o recuperar los valores de un control wxPropertyGrid puede ser útil disponer de iteradores que permitan obtener todas las propiedades.
Para ese tipo de tareas disponemos de la clase wxPropertyGridIterator.
Para obtener un iterador usaremos el método GetIterator, que apuntará a la primera propiedad del wxPropertyGrid.
El primer argumento permite especificar algunos indicadores. Por ejemplo, si queremos recuperar las propiedades y las categorías podemos usar el valor wxPG_ITERATE_CATEGORIES|wxPG_ITERATE_PROPERTIES.
Esto nos permite crear un bucle que finalizará cuando su método AtEnd devuelva true.
wxString cad;
wxString tipo;
wxString nombre;
wxString val;
wxPGProperty* p;
wxPropertyGridIterator it;
for ( it = prop->GetGrid()->GetIterator(wxPG_ITERATE_CATEGORIES|wxPG_ITERATE_PROPERTIES); !it.AtEnd(); ++it )
{
p = *it;
nombre = p->GetName();
if(p->IsCategory()) {
cad = _T("Categoría: ");
cad << nombre;
wxMessageBox(cad, _T("Propiedad"));
} else {
tipo = p->GetValueType();
val = p->GetValueAsString();
cad.Empty();
cad << _T("Nombre: ") << nombre << "\n";
cad << _T("Tipo: ") << tipo << "\n";
cad << _T("Valor: ") << val << "\n";
if(p->HasFlag(wxPGFlags::Modified)) cad << _T("Modificado");
wxMessageBox(cad, _T("Propiedad"));
}
}
El método GetGrid, que hemos usado para obtener el iterador, lo único que hace es obtener un puntero al wxPropertyGrid, de modo que en este caso es redundante. Está pensado para que se pueda obtener un iterador tanto en wxPropertyGrid como en wxPropertyGridManager, que veremos más abajo.
Datos de cliente
A la hora de manejar controles con muchas propiedades puede ser muy interesante añadir datos de cliente a cada propiedad. De este modo pueden simplificarse las tareas para recuperar o modificar valores.
Ya hemos usado este tipo de datos de cliente en otros controles. No hay nada nuevo al respecto para las propiedades.
Podemos usar un dato de cliente de tipo void*, mediante el método SetClientData, teniendo en cuenta que la aplicación será la responsable de liberar la memoria usada por esos datos.
Para recuperar ese valor se usa GetClientData.
Sin embargo es más útil usar un objeto de cliente, ya que nos permite definir métodos para acceder a los datos de cliente y además el control obtiene su propiedad, por lo que no tendremos que preocuparnos por liberar la memoria que ocupan.
Para asignar un objeto de cliente a una propiedad se usa el método SetClientObject y para recuperarlo GetClientObject. Por ejemplo, para añadir como dato de cliente el nombre de la propiedad sería algo así:
class propertyClientData : public wxClientData {
public:
propertyClientData(wxString n) : m_nombre(n) {}
const wxString& GetData() const { return m_nombre; }
void SetData(wxString n) { m_nombre=n; }
private:
wxString m_nombre;
};
...
wxString name;
name = "nombrecompleto";
wxPGProperty* nombre = prop->Append( new wxStringProperty(_T("Nombre"), name, "<composed>"));
nombre->SetClientObject(new propertyClientData(name));
prop->SetPropertyHelpString(nombre, _T("Nombre completo"));
name = "apellido";
wxPGProperty* apellido = prop->AppendIn(nombre, new wxStringProperty(_T("Apellido"), name, "de Tal"));
apellido->SetClientObject(new propertyClientData(name));
name = "nombre";
wxStringProperty* pila = new wxStringProperty(_T("Nombre de pila"), name, "Fulanito");
pila->SetClientObject(new propertyClientData(name));
pila->SetValue(_T("Menganito"));
...
propertyClientData* data = static_cast<propertyClientData*>(p->GetClientObject());
Validadores
Podemos añadir validadores a las propiedades del mismo modo que lo hemos hecho con otros controles.
No será necesario reimplementar los métodos TransferToWindow y TransferFromWindow, tan sólo Validate.
Por ejemplo, para validar números de teléfono, podemos verificar que el número sólo contiene dígitos, y que contiene un número concreto de ellos.
class TelefonoValidator : public wxValidator
{
public:
TelefonoValidator( int n=9 ) : wxValidator(), m_digitos(n) {}
virtual wxObject* Clone() const { return new TelefonoValidator(m_digitos); }
virtual bool Validate(wxWindow* parent) {
wxVariant val;
try {
wxTextCtrl* tc = dynamic_cast<wxTextCtrl*>(GetWindow());
val = tc->GetValue();
}
catch (...) {
wxFAIL_MSG( _T("TelefonoValidator sólo funciona con wxTextCtrl"));
return false;
}
bool retval = true;
wxString tel = val.GetString();
wxString msg;
if(tel.Len()!=m_digitos) {
msg = _T("El número de teléfono debe tener ");
msg << m_digitos << _T(" dígitos");
retval = false;
} else {
for(size_t i=0; i<tel.Len(); i++) {
if(!isdigit(tel[i])) {
msg = _T("Caraćter no permitido");
retval = false;
continue;
}
}
}
if(!retval) wxMessageBox(msg, _T("Fallo de validación"));
return retval;
}
private:
size_t m_digitos;
};
Posteriormente podemos asignar un validador a una propiedad mediante el método SetValidator:
prop->GetProperty("telefono")->SetValidator(TelefonoValidator(9));
Ejemplo 31a
| Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
|---|---|---|---|---|---|
| Ejemplo 31a | wx031a.zip | 2026-04-20 | 10137 bytes | 9 |
Tabla de propiedades multipágina
Cuando sea necesario editar gran cantidad de propiedades podemos usar un control wxPropertyGridManager, que es una versión multipágina de wxPropertyGrid, a la que se pued añadir de forma opcional una barra de herramientas para la selección de modo y página, un cuadro de texto de ayuda y un encabezado.
Estos controles están compuestos de páginas, que a su vez contienen propiedades.
Las páginas están encapsuladas en la clase wxPropertyGridPage.
Estilos
Algunos de los estilos definidos en el tipo enumerado wxPG_WINDOW_STYLES son exclusivos para wxPropertyGridManager:
- wxPG_TOOLBAR: muestra una barra de herramientas que permite seleccionar el modo y la página.
- wxPG_DESCRIPTION: se añade un cuadro de texto con la descripción o el texto de ayuda de la propiedad seleccionada actualmente, si está definido.
- wxPG_NO_INTERNAL_BORDER: oculta el borde interno alrededor de la cuadrícula de propiedades.
También hay estilos extendidos exclusivos para este tipo de controles:
- wxPG_EX_NO_FLAT_TOOLBAR: configura la barra de herramientas para que no utilice el estilo plano.
- wxPG_EX_MODE_BUTTONS: muestra los botones del modo alfabético/categórico en la barra de herramientas.
- wxPG_EX_INIT_NOCAT: acelera el cambio al modo wxPG_HIDE_CATEGORIES.
- wxPG_EX_HIDE_PAGE_BUTTONS: oculta los botones de selección de página de la barra de herramientas.
- wxPG_EX_TOOLBAR_SEPARATOR: muestrar un separador debajo de la barra de herramientas.
Insertar el control PropertyGridManager
El constructor tiene los parámetros habituales: ventana padre, identificador, la posición, tamaño, estilos, un nombre opcional.
Es importante añadir el estilo wxPG_TOOLBAR, ya que de otro modo el usuario no podrá cambiar de página fácilmente.
wxPropertyGridManager* prop = new wxPropertyGridManager(this, idPropiedades,
wxDefaultPosition, wxDefaultSize,
wxPGMAN_DEFAULT_STYLE|wxPG_TOOLBAR);
Añadir páginas
Un PropertyGridManager está compuesto por páginas, y cada página se comporta, en la práctica, como un PropertyGrid.
Las páginas se encapsulan en la clase wxPropertyGridPage, aunque rara vez tendremos que usar métodos de esa clase, salvo para añadir propiedades.
Para añadir una página se usa el método AddPage de wxPropertyGridManager, indicando como primer parámetro el texto de la etiqueta, como segundo parámetro un mapa de bits que se usará en la barra de herramientas, y como tercer parámetro un puntero a un objeto wxPropertyGridPage previamente creado.
Si se omite el tercer parámetro, o se usa el valor nullptr, se creará una página. Si se omite el segundo se usará una imagen integrada.
wxPropertyGridPage* page;
page = prop->AddPage(_T("Usuario"), wxBitmap(user_xpm));
Una vez insertada la página podemos insertar en ella las propiedades que queramos, igual que hicimos con los PropertyGrid, pero usando el método Append de wxPropertyGridPage, heredado de wxPropertyGridInterface.
page->Append( new wxPropertyCategory("Datos de usuario") );
page->Append( new wxStringProperty(_T("Nombre"), "nombre"));
page->Append( new wxStringProperty(_T("Email"), "email"));
page->Append( new wxDateProperty(_T("Fecha de nacimiento"), "nacimiento"));
Cabecera
Opcionalmente podemos mostrar una cabecera entre la barra de herramientas y las propiedades. Esta cabecera permite mostrar etiquetas para cada columna de las propiedades. Por defecto esas etiquetas son "Property" y "Value", pero se pueden cambiar.
Para que se muestre la cabecera usaremos el método ShowHeader, indicando como parámetro el valor por defecto, true, se mostrará y con el valor false se ocultará.
Para cambiar el texto de las etiquetas usaremos el método SetColumnTitle
prop->ShowHeader(true);
prop->SetColumnTitle(0, _T("Propiedad"));
prop->SetColumnTitle(1, _T("Valor"));
Cuadro de descripción
Si se especifica el estilo wxPG_DESCRIPTION se mostrará en la parte inferior del wxPropertyGridManager un área con una descripción de la etiqueta seleccionada, si es que se ha establecido.
La descripción para cada propiedad se establece mediante el texto de ayuda, y se mostrará también en la barra de estado, si existe.
prop->GetProperty("nombre")->SetHelpString(_T("Nombre completo con dos apellidos"));
Se puede establecer la distancia del divisor entre la zona de propiedades y el cuadro de descripción mediante SetDescBoxHeight. Esto puede ser útil si los textos de descripción son muy largos, pero en general, para una línea de texto, el valor por defecto suele ser el correcto.
El texto de la descripción se actualiza automáticamente al cambiar la propiedad seleccionada, pero también se puede establecer mediante el método SetDescription. Esto puede resultar útil en determinadas circunstancias, si procesamos eventos de propiedades.
Selección de página
Podemos obtener la página actualmente seleccionada mediante el método GetCurrentPage, que devuelve un puntero al objeto wxPropertyGridPage, o mediante GetSelectedPage, que devuelve el índice de la página seleccionada.
Para seleccionar una página desde código se usa el método SelectPage, indicando el índice de la página a seleccionar.
prop->SelectPage(1);
Obtener páginas
Para obtener el objeto wxPropertyGridPage correspondiente a una página disponemos del método GetPage, que está sobrecargado de modo que podemos usar como parámetro indistintamente su índice o su nombre.
El método GetPageByName nos devuelve el índice de una página a partir de su nombre.
De igual modo, el método GetPageName nos devuelve el nombre de una página a partir de su índice.
Para obtener el número de páginas se usa GetPageCount.
Y para saber si en una página se ha modificado alguna propiedad usaremos IsPageModified. Esto puede ser útil si se usa antes de guardar las propiedades, ya que si no hay cambios no será necesario hacerlo.
Estado de página
Cada página tiene asociado un estado: propiedades, anchuras de columnas, etc.
Aunque esto probablemente tenga una utilidad limitada en la práctica, podemos recuperar el estado de una página mediante el método GetStatePtr, que devuelve un puntero a un objeto de la clase wxPropertyGridPageState.
Posteriormente podemos recuperar una página con el mismo estado usando el método GetPageByState, usando como parámetro un puntero al estado previamente guardado.
Obtener la cuadricula
Algunos métodos que están disponibles para wxPropertyGrid no se pueden aplicar directamente a un wxPropertyGridManager. El método GetGrid obtiene un puntero al wxPropertyGrid asociado al wxPropertyGridManager, de modo que podamos acceder a esos métodos, como GetIterator o MakeColumnEditable.
Algo modificado
El método IsAnyModified nos indica si cualquiera de las propiedades del control ha sido modificada de algún modo.
Modificar celdas
Es posible modificar la apariencia de ciertas celdas, para cambiar textos, colores o fuentes.
Por ejemplo, para cambiar el color de fondo y del texto de una celda con una etiqueta:
wxPGCell& celda = prop->GetProperty("banderas")->GetOrCreateCell(0);
celda.SetBgCol(wxColour("#80d080"));
celda.SetFgCol(wxColour("#101010"));
Primero tendremos que obtener una referencia a la celda que nos interese. El método GetCell o GetOrCreateCell, indicando como parámetro la columna, se encarga de ello. Si la celda existe previamente, obtiene una referencia a ella, si no existe la crea y devuelve una referencia.
La columna 0 corresponde a la etiqueta y la 1 al valor.
La clase wxPGCell contiene la información y los métodos para acceder a los parámetros de una celda.
Podemos obtener los colores de primer plano y de fondo: GetFgCol y GetBgCol, la fuente GetFont, el texto, GetText, o el mapa de bits asociado, GetBitmap.
El método HasText devuelve true si la celda contiene texto.
Podemos obtener toda la información de una vez mediante GetData, en un objeto wxPGCellData.
También, por supuesto, existen los métodos que permiten establecer cualquiera de esos valores: SetFgCol, SetBkCol, SetFont, SetText, SetBitmap y SetData.
wxPGCell& celda = prop->GetProperty("banderas")->GetCell(0);
celda.SetBgCol(wxColour("#80d080"));
celda.SetFgCol(wxColour("#101010"));*
Esto mismo se puede hacer directamente con el método SetPropertyCell.
prop->SetPropertyCell(prop->GetProperty("banderas"), 0, wxString(), wxBitmapBundle(), wxColour("#101010"), wxColour("#80d080"));
El objeto wxPGCell también se puede usar para establecer la apariencia de las celdas con un valor no especificado. Para ello usaremos el método SetUnspecifiedValueAppearance:
wxPGCell celda;
celda.SetText(_T("No especificado"));
celda.SetFgCol(wxColour("#D02020"));
prop->GetGrid()->SetUnspecifiedValueAppearance(celda);
Añadir columnas
Aunque esto contradice la regla de que una propiedad es una pareja etiqueta=valor, es posible añadir más columnas a una cuadrícula de propiedades. Para ello basta con usar el método SetColumnCount indicando el valor deseado de columnas como parámetro.
Pero añadir columnas no añade funcionalidades directamente. No podremos editar los valores de esas columnas, ni tenerlos en cuenta a la hora de recuperarlos.
Una primera aplicación podría ser añadir un texto con las unidades en que se debe expresar el valor de la propiedad, para aquellas propiedades para las que tenga sentido. Por ejemplo:
page->Append( new wxIntProperty(_T("Kilómetros"), "km"));
prop->SelectPage(1);
prop->GetGrid()->SetColumnCount(3);
wxPGCell& unidades = prop->GetProperty("km")->GetOrCreateCell(2);
unidades.SetText(_T("km"));
En el caso de controles wxPropertyGridmanager, el número de columnas se modifica en cada página, por eso es importante seleccionar la página antes de modificar el número de columnas, Además, es necesario utilizar el método GetGrid ya que ese método pertenece a la clase wxPropertyGrid.
Editar columnas extra
Si además queremos que se puedan editar columnas distintas de la segunda podemos usar el método MakeColumnEditable, indicando el número de la columna. El valor 0 corresponde a la etiqueta. Este método no se debe usar con la columna 1.
Aunque usar este método nos permitirá editar celdas de la columna indicada, el valor editado sólo se establecerá si la celda existe. Por ejemplo, si hemos añadido la columna de unidades anterior, y hacemos editable la tercera columna, al pulsar sobre cualquier celda de de esa columna se creará un control de edición, pero el valor sólo se modificará para la propiedad de "km".
prop->GetGrid()->MakeColumnEditable(2);
Si queremos que una celda de la tercera columna de una propiedad pueda ser editable tendremos que crearla, usando el método GetOrCreateCell para la propiedad requerida.
prop->GetProperty("color")->GetOrCreateCell(2).SetText("");
Guardar y recuperar el estado
Durante la edición de propiedades el usuario puede modificar ciertos parámetros, como la propiedad seleccionada, las que están colapsadas o expandidas, la página sleccionada, etc.
Cuando termina de editar las propiedades y se cierra el diálogo que las contiene, todas esas modificaciones se pierden, y si posteriormente se vuelven a editar, el control vuelve al estado inicial.
Podemos evitar este comportamiento guardando el estado del control, usando el método SaveEditableState(), para recuperarlo posteriormente, usando RestoreEditableState().
El estado se almacena en una cadena, de modo que si queremos conservar ese estado entre diferentes ejecuciones de la aplicación, tendremos que guardar esa cadena en un fichero o base de datos, para poder recuperarla posteriormente.
Eventos
Existe la posibilidad de responder a varios tipos de eventos relacionados con las propiedades.
El procedimiento es el habitual:
- Crear un método para gestionar el evento.
- Añadir el evento a la tabla de eventos de de la ventana o marco que contiene el control.
class wxMiFrame: public wxFrame
{
public:
wxMiFrame(wxFrame *frame, const wxString& title);
~wxMiFrame();
...
void OnSelect(wxPropertyGridEvent& event);
DECLARE_EVENT_TABLE()
};
...
BEGIN_EVENT_TABLE(wxMiFrame, wxFrame)
...
EVT_PG_SELECTED(idPropiedades, wxMiFrame::OnSelect)
END_EVENT_TABLE()
...
void wxMiFrame::OnSelect(wxPropertyGridEvent& event) {
wxString nombre = event.GetProperty()->GetName();
wxMessageBox(nombre, "Seleccionado");
}
Los controles wxPropertyGrid y wxPropertyGridManager nos permiten detectar varios tipos de eventos:
- EVT_PG_SELECTED: cuando cambia la propiedad seleccionada.
- EVT_PG_CHANGED: cuando el usuario ha modificado el valor de una propiedad.
- EVT_PG_CHANGING: cuando el usuario está modificando el valor de una propiedad.
- EVT_PG_HIGHLIGHTED: cuando el cursor del ratón está sobre el área del control.
- EVT_PG_RIGHT_CLICK: el usuario ha pulsado el botón derecho sobre una propiedad.
- EVT_PG_DOUBLE_CLICK: el usuario ha hecho un doble clic izquierdo sobre una propiedad.
- EVT_PG_ITEM_COLLAPSED: cuando el usuario colapsa una propiedad compuesta o una categoría.
- EVT_PG_ITEM_EXPANDED: cuando el usuario expande una propiedad compuesta o una categoría.
- EVT_PG_LABEL_EDIT_BEGIN y EVT_PG_LABEL_EDIT_ENDING: cuando se comienza y termina de editar una etiqueta.
- EVT_PG_COL_BEGIN_DRAG, EVT_PG_COL_DRAGGING y EVT_PG_COL_END_DRAG: cuando se mueve el separador entre etiquetas y valores.
Estas notificaciones usan objetos de la clase wxPropertyGridEvent, a partir de los cuales podemos obtener un puntero ala propiedad, su nombre, su valor, su antecesor, cuando se trate de una propiedad compuesta o perteneciente a una categoría, entre otras cosas.
Ejemplo 31b
| Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
|---|---|---|---|---|---|
| Ejemplo 31b | wx031b.zip | 2026-04-20 | 11635 bytes | 11 |
Librería propgrid
El código correspondiente al manejo de propiedades está en una librería de enlace dinámico separada, salvo que se trate de una versión monolítica.
Por ese motivo será necesario modificar las opciones del enlazador para que se incluya esa librería al crear el ejecutable. En lugar de usar la cadena `wx-config --libs`, tendremos que usar `wx-config --libs std,propgrid` en "otras opciones del enlazador".