42 Control Static avanzado
En el capítulo 10 ya vimos la mayor parte de lo que se puede decir sobre los controles estáticos. En este capítulo veremos que estos controles no son tan poco interactivos como parecen, y aunque no pueden ser seleccionados, ni pueden recibir el foco, ni responden al teclado; sí puede recibir algunos mensajes de notificación, si se definen con el estilo adecuado.
Insertar controles estáticos durante la ejecución
Empezaremos por ver cómo insertar controles estáticos durante la ejecución. Esto ya no es nada nuevo, el proceso es similar al de los controles que ya hemos visto, la única diferencia es que en este caso se trata de ventanas de la clase STATIC. Para insertar el control también usaremos las funciones CreateWindow y CreateWindowEx.
HWND hctrl; ... hctrl = CreateWindowEx( 0, "STATIC", /* Nombre de la clase */ "Texto", /* Texto del título */ SS_SIMPLE | WS_CHILD | WS_VISIBLE, /* Estilo */ 5, 5, /* Posición */ 55, 55, /* Tamaño */ hwnd, /* Ventana padre */ (HMENU)ID_ESTAT, /* Identificador del control */ hInstance, /* Instancia */ NULL); /* Sin datos de creación de ventana */
En aquellos controles estáticos que visuelicen texto, el parámetro del título se usará para indicar el texto. En otros casos podrá ser un nombre de fichero, y en el resto se ignora.
El identificador del control se suministra a través del parámetro hMenu, por lo que será necesario hacer un casting.
Sin embargo, con frecuencia no será necesario procesar mensajes de estos controles, ni enviárselos durante la ejecución, de modo que muchas veces podemos usar el mismo identificador para todos ellos. En esos casosno existe ningún valor en especial para usar como identificador, aunque suele usarse el valor -1.
Cambiar fuente
También es posible modificar la fuente de un control estático enviando un mensaje WM_SETFONT. El lugar apropiado es, por supuesto, al procesar el mensaje WM_INITDIALOG en diálogos o al iniciar la ventana, al procesar el mensaje WM_CREATE.
En el parámetro wParam pasamos un manipulador de fuente, y usaremos la macro MAKELPARAM para crear un valor LPARAM, en el que especificaremos la opción de repintar el control, que se almacena en la palabra de menor peso de LPARAM.
Esto nos permite modificar la fuente durante la ejecución, reflejando los cambios en pantalla.
static HFONT hfont; ... hfont = (HFONT)GetStockObject( DEFAULT_GUI_FONT ); SendMessage(hctrl, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
Cambiar colores
Análogamente a lo que hemos visto con otros controles, también existe un mensaje que nos permite modificar el color de los controles de tipo estático.
Se trata del mensaje WM_CTLCOLORSTATIC, que se envía a la ventana propietaria del control cuando debe ser dibujado.
En el parámetro wParam recibiremos un manipulador de contexto de dispositivo del control, y en el parámetro lParam el manipulador ventana del control. Podemos cambiar el color de fondo y el del texto, y cuando se procese este mensaje, y deberemos retornar un manipulador de pincel, que se usará para pintar el fondo:
static HBRUSH pincel; ... case WM_CREATE: pincel = CreateSolidBrush(RGB(0,255,0)); ... case WM_CTLCOLORSTATIC: SetTextColor((HDC)wParam, RGB(0,0,255)); SetBkColor((HDC)wParam, RGB(0,255,0)); return (LRESULT)pincel; case WM_DESTROY: DeleteObject(pincel); ...
Estilos estáticos gráficos
Recordemos ahora los diferentes estilos de controles estáticos, y aprovechemos para ver algunos que no vimos anteriormente:
Marcos
- SS_BLACKFRAME: marco de color negro.
- SS_GRAYFRAME: marco de color gris.
- SS_WHITEFRAME: marco de color blanco.
Rectángulos
- SS_BLACKRECT: rectángulo negro.
- SS_GRAYRECT: rectángulo gris.
- SS_WHITERECT: rectángulo blanco.
Ranurados
- SS_ETCHEDHORZ: ranura horizontal.
- SS_ETCHEDVERT: ranura vertical.
- SS_ETCHEDFRAME: rectángulo ranurado.
Ejemplos
Más sobre los ranurados
Los ranurados se consiguen mediante el uso de la función DrawEdge, que también está disponible para usarse en la decoración de ventanas y cuadros de diálogo.
No se trata de una función del GDI, sino de una función de user32, por lo que este es, probablemente, el mejor sitio para comentar su uso.
Esta función tiene cuatro parámetros:
- El primero es un manipulador del DC, donde se trazará el ranurado.
- El segundo es un puntero a una estructura RECT con las coordenadas lógicas del rectángulo a ranurar.
- El tercero sirve para especificar qué tipo de borde queremos trazar para el interior y exterior. Hay dos opciones para el borde interior: BDR_RAISEDINNER y BDR_SUNKENINNER (hacia afuera y hacia adentro, respectivamente); y dos opciones para el exterior: BDR_RAISEDOUTER y BDR_SUNKENOUTER. Se debe combiar una opción interior con una exterior, de modo que, hay cuatro posibilidades, para las que también existen macros definidas: EDGE_BUMP, EDGE_ETCHED, EDGE_RAISED y EDGE_SUNKEN, (reborde, ranurado, elevado y hundido).
- El cuarto parámetro sirve para indicar el tipo de borde, y también pueden ser combinados
para obtener más tipos. Para ver cada tipo individual se puede usar el programa de ejemplo.
Algunos de los tipos se refieren a los lados del rectángulo que se van a dibujar, por ejemplo, BF_RIGHT, BF_LEFT, BF_TOP y BF_BOTTOM, o combinaciones de ellos, como BF_BOTTOMLEFT, BF_BOTTONRIGHT, BF_TOPLEFT y BF_TOPRIGHT. Estos estilos se pueden combinar entre si para obtener las combinaciones que faltan, usando el operador de bits |. También se puede usar BF_RECT para mostrar los cuatro lados del rectángulo.
Otros tipos permiten trazar diagonales: BF_DIAGONAL, BF_ENDTOPRIGHT, etc.
El resto permite definir diferentes aspectos, por ejemplo, BF_FLAT crea ranurados planos, que no dan sensación de relieve; BF_MIDDLE muestra la superficie interior del rectángulo a un nivel intermedio de profundidad, usando un tono de gris intermedio; MF_MONO elimina por completo el relieve 3D; BF_SOFT da un acabado blando, redondeado; BF_ADJUST crea el ranurado de modo que se deje libre el área interna del rectángulo para que la use el cliente.
Estilos estáticos de texto
Entre los estilos para controles estáticos de texto tenemos los siguientes:
- SS_SIMPLE: la forma simple alinéa el texto a la izquierda, y descartará la parte que no quepa en el rextángulo especificado.
- SS_LEFTNOWORDWRAP: igual que el estilo SS_SIMPLE, salvo que los caracteres de tabulación se expanden.
- SS_LEFT: igual que el estilo SS_LEFTNOWORDWRAP, salvo que el texto se dividirá en tantas líneas como sea necesario para visualizarlo por completo.
- SS_CENTER: igual que SS_LEFT, salvo que cada línea de texto se centrará horizontalmente en el rectángulo dado.
- SS_RIGHT: igual que SS_LEFT, salvo que las líneas se alinéan a la derecha.
Se puede modificar el texto dentro de un control estático mediante la función SetWindowText o mediante el mensaje WM_SETTEXT.
Recordemos que podemos enviar mensajes a un control mediante las funciones SendDlgItemMessage, conociendo el identificador del control, o usando SendMessage, si disponemos del manipulador de ventana del control. Este manipulador se puede conseguir también mediante la función GetDlgItem, conociendo el identificador del control.
Estas tres líneas de código son, por lo tanto, equivalentes:
SetWindowText(GetDlgItem(hwnd, ID_EST), "Nuevo texto"); SendDlgItemMessage(hwnd, ID_EST, WM_SETTEXT, 0, (LPARAM)"Nuevo texto"); SendMessage(GetDlgItem(hwnd, ID_EST), WM_SETTEXT, 0, (LPARAM)"Nuevo texto");
El modificador de estilo SS_NOPREFIX se puede aplicar a cualquiera de los estilos anteriores. Cuando se hace, el resultado es que los caracteres & no se interpretan como prefijos de aceleradores. Por defecto, el carácter & no se muestra, sino que provoca que el siguiente carácter se subraye, y la tecla correspondiente a ese carácter funcione como un acelerador. Cuando este acelerador se usa, el foco pasa al siguiente control que admita el foco del teclado.
Imágenes
Existen tres estilos básicos para crear controles estáticos con imágenes:
- SS_BITMAP: se mostrará un mapa de bits. Si se hace desde un fichero de recursos, el texto del control corresponde con un recurso de mapa de bits definido en algún lugar del fichero de recursos. Si se inserta directamente en la ventana o diálogo, será necesario asignar un mapa de bits.
- SS_ICON:se mostrará un icono. Si se hace desde un fichero de recursos, el texto del control corresponde con un recurso de icono definido en algún lugar del fichero de recursos. Si se inserta directamente en la ventana o diálogo, será necesario asignar un icono.
- SS_ENHMETAFILE: se mostrará un metafichero mejorado. Si se hace desde un fichero de recursos, el texto del control corresponde con un recurso de metafichero definido en algún lugar del fichero de recursos. Si se inserta directamente en la ventana o diálogo, será necesario asignar un metafichero. Noveremos este estilo por ahora, en el futuro estudiaremos los metaficheros y los metaficheros mejorados.
Mensajes para asignar imágenes
Podemos usar el mensaje STM_SETIMAGE para asignar un mapa de bits, un icono, un cursor o un metafichero a un control estático. El parámetro wParam contendrá una constante que indica el tipo de imagen, y el parámetro lParam un manipulador de imagen:
SendMessage(hctrl, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmap); SendMessage(hctrl, STM_SETIMAGE, IMAGE_ICON, (LPARAM)Icono);
De forma simétrica, el mensaje STM_GETIMAGE permite obtener un manipulador de la imagen asociada a un control estático. Igual que antes, el parámetro wParam contendrá una constante que indica el tipo de imagen.
Además, existen otros mensajes equivalentes para controles estáticos con el estilo SS_ICON. Así, el mensaje STM_SETICON permite asignar un icono a un control estático, y el mensaje STM_GETICON recuperar un manipulador del icono actual del control. Estas dos líneas son equivalentes:
SendMessage(hctrl, STM_SETICON, (WPARAM)Icono, 0); SendMessage(hctrl, STM_SETIMAGE, IMAGE_ICON, (LPARAM)Icono);
Modificadores de estilo
Podemos aplicar algunos modificadores de estilo a los tres descritos anteriormente. Si no se aplica ningún modificador, el tamaño del control se calcula a partir del tamaño de la imagen, y se ignora el especificado al crear el control.
- SS_CENTERIMAGE: si se especifica, el control tendrá el tamaño especificado, el gráfico se mostrará centrado y la parte no usada se rellena con el color del pixel de la esquina superior izquierda de la imagen.
- SS_REALSIZEIMAGE: cuando se usa, el gráfico se escalará si el tamaño del control aumenta o disminuye, de modo que ocupe toda su área de cliente.
- SS_RIGHTJUST: hasta la fecha ignoro para qué sirve esta opción. :-)
Modificador de hundido
Todos los estilos anteriores se pueden combinar con un modificador, el estilo SS_SUNKEN, que crea la apariencia de que el control está ligeramente hundido con respecto al resto del área de cliente de la ventana:
Mensajes de notificación
La clase Static dispone de varios mensajes de notificación, pero para que se envíen se debe activar el estilo SS_NOTIFY. En realidad los controles static no están diseñados para ser interactivos, de modo que esto es algo lógico.
Como en todos los casos, los mensajes de notificación se envían a la ventana padre en la palabra de mayor peso del parámetro wParam, mediante un mensaje WM_COMMAND. Los mensajes de notificación son:
- STN_CLICKED: se envía cuando el usuario hace clic sobre el control.
- STN_DBLCLK: se envía cuando el usuario hace soble clic sobre el control.
- STN_DISABLE: se envía cuando el control es deshabilitado.
- STN_ENABLE: se envía cuando el control es habilitado.
Por ejemplo:
case WM_COMMAND: switch(LOWORD(wParam)) { case ID_EST14: switch(HIWORD(wParam)) { case STN_CLICKED: MessageBox(hwnd, "Clic", "Controles estáticos", MB_OK); break; case STN_DBLCLK: MessageBox(hwnd, "Doble clic", "Controles estáticos", MB_OK); break; case STN_DISABLE: MessageBox(hwnd, "Disable", "Controles estáticos", MB_OK); break; case STN_ENABLE: MessageBox(hwnd, "Enable", "Controles estáticos", MB_OK); break; } break; } break;
Controles estáticos owner-draw
Los controles estáticos también disponen de un estilo owner-draw.
Para crear un control estático owner-draw se debe activar el estilo SS_OWNERDRAW. De este modo nuestra aplicación se hará cargo de mostrar el control, y seremos los únicos responsables de su aspecto gráfico.
Lo primero es crear el control con el estilo SS_OWNERDRAW:
HWND hctrl; ... hctrl = CreateWindowEx( 0, "STATIC", /* Nombre de la clase */ "Texto", /* Texto del título */ SS_OWNERDRAW | WS_CHILD | WS_VISIBLE, /* Estilo */ 9, 49, /* Posición */ 95, 24, /* Tamaño */ hwnd, /* Ventana padre */ (HMENU)ID_STATIC,/* Identificador del control */ hInstance, /* Instancia */ NULL); /* Sin datos de creación de ventana */
Cuando existen controles con el estilo owner-draw, la ventana padre del control recibirá un mensaje WM_DRAWITEM cada vez que el control deba ser redibujado. Este mensaje se recibe para todos los controles owner-draw existentes, de modo que deberemos distinguir a qué control en concreto se refiere el mensaje. Esta es la parte sencilla, ya que en el parámetro wParam de este mensaje recibiremos el identificador del control.
Por otra parte, en el parámetro lParam, recibiremos un puntero a una estructura DRAWITEMSTRUCT, que nos proporciona información sobre todos los detalles necesarios para decidir el modo en que debemos dibujar el control.
Por una parte, el campo CtlType contendrá el valor ODT_STATIC, indicando que el control es un control estático. El campo CtlID contiene el identificador del control. El campo itemID no tiene ninguna información en el caso de un control estático. El campo itemAction contiene un valor que especifica el tipo de acción de dibujo requerido. Sólo puede ser ODA_DRAWENTIRE, si se necesita actualizar el control completo, ya que estos controles no pueden tener el foco ni ser seleccionados.
El campo itemState especifica el estado del control. En el caso de los controles estáticos, el valor de este campo sólo puede ser: ODS_DISABLED, si el control está deshabilitado.
El campo hwndItem contiene el manipulador de ventana, hDC contiene el manipulador de contexto de dispositivo del control. El campo rcItem contiene el rectángulo que define los límites de la zona a dibujar. E itemData no contiene nada válido en el caso de los controles estáticos.
Este ejemplo visualiza un control estático con forma elíptica:
LPDRAWITEMSTRUCT lpdis; case WM_DRAWITEM: lpdis = (LPDRAWITEMSTRUCT)lParam; SendDlgItemMessage(hwnd, (UINT)wParam, WM_GETTEXT, 128, (LPARAM)cad); GetClientRect(lpdis->hwndItem, &re); SetBkMode(lpdis->hDC, TRANSPARENT); FillRect(lpdis->hDC, &re, (HBRUSH)GetStockObject(LTGRAY_BRUSH)); SelectObject(lpdis->hDC, (HBRUSH)GetStockObject(GRAY_BRUSH)); SelectObject(lpdis->hDC, (HPEN)GetStockObject(WHITE_PEN)); Ellipse(lpdis->hDC, re.left, re.top, re.right, re.bottom); TextOut(lpdis->hDC, re.left+12, re.top+2, cad, strlen(cad)); return 0;
Ejemplo 70
Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
---|---|---|---|---|---|
Ejemplo 70 | win070.zip | 2007-03-15 | 6554 bytes | 653 |