Capítulo 49 Ventana de estado

Las ventanas de estado son ventanas hijas que generalmente se colocan en la parte inferior de las ventanas, y que ocupan todo el ancho de la ventana padre. Se usan para mostrar mensajes de estado o de ayuda de la aplicación. Se trata de un control estático, ya que sólo se usa para mostrar información, y no para recogerla del usuario.

Cómo crear ventanas de estado

Hay dos formas de crear ventanas de estado. La más sencilla es usar la función CreateStatusWindow a la que proporcionaremos cuatro parámetros. El primero es una combinación de estilos de ventana, entre los que debe aparecer WS_CHILD, y por supuesto, WS_VISIBLE, si queremos que la ventana se muestre. El segundo es el texto que queremos que aparezca, y que seguramente varíe a lo largo de la ejecución del programa. El tercero es un manipulador de la ventana padre y el cuarto un identificador del control, que usaremos para enviarle mensajes a la ventana de estado.

           CreateStatusWindow(WS_CHILD|WS_VISIBLE, "Texto de prueba", hwnd, ID_STATUS);

La segunda forma es usar la función CreateWindowEx, especificando como clase de ventana el valor STATUSCLASSNAME. Las coordenadas y dimensiones de la ventana serán ignoradas, de modo que pueden ser cero.

           CreateWindowEx(0, STATUSCLASSNAME, "Texto de prueba", WS_CHILD|WS_VISIBLE, 
		       0, 0, 0, 0, hwnd, (HMENU)ID_STATUS, hInstance, 0);

Como en todos los controles comunes que estamos viendo, hay que asegurarse de que la DLL ha sido cargada invocando a la función InitCommonControls.

Estilos

Por defecto, las ventanas de estado se colocan en la parte inferior de la ventana padre, es decir, estas ventanas tienen activo por defecto el estilo CCS_BOTTOM. También se puede especifiar el estilo CCS_TOP, que la coloca en la parte superior, aunque no es nada habitual.

También se puede usar el estilo SBARS_SIZEGRIP para incluir un mapa de bits a la derecha de la barra que se usa para redimensionar la ventana. Aunque ese estilo se puede combinar con CCS_TOP, carece de sentido hacerlo, ya que no funcionará.

Cuando se usan barras de estado es importante que el procedimiento de ventana de la ventana padre procese el mensaje WM_SIZE, de modo que cada vez que la ventana padre cambie de tamaño, se envíe el mensaje a la ventana de estilo para que se adapte al nuevo tamaño de su ventana padre.

        case WM_SIZE:
           SendDlgItemMessage(hwnd, ID_STATUS, WM_SIZE, 0, 0);
           break;

Ayuda para menús

Una de las aplicaciones de la ventana de estado es mostrar ayudas contextuales en función de la opción de menú actualmente seleccionada.

Para ello se usa la función MenuHelp. Según la documentación del API, esta función procesa los mensajes WM_MENUSELECT y WM_COMMAND.

Hay que pasar siete parámetros a la función, el primero es el mensaje, WM_MENUSELECT o WM_COMMAND, seguido de los parámetros wParam y lParam del mensaje. El cuarto es un manipulador del menú d ela ventana. El Quinto un manipulador de la instancia desde la que se cargarán las cadenas con los mensajes de ayuda. El sexto es un manipulador de la ventana de estado y el séptimo y último es un array de enteros con los desplazamientos de los identificadores de las cadenas con respecto a los identificadores de comandos asociados al menú.

En un menú sencillo, con un único nivel de submenús, el primer valor en el array de desplazamientos es el desplazamiento entre los identificadores de ítem de menú y sus cadenas de ayuda. Por ejemplo, si los identificadores de menú empiezan en 100, y los de las cadenas asociadas en 200, el primer valor del array será 100 (200-100). El segundo valor se usa para las cadenas de los popups, que no tienen identificador de menú. En su lugar se usa la posición, 0 para el primero, 1 para el segundo, etc. De modo que si el primer identificador de cadena para los menús popup es 800, ese será el valor del desplazamiento almacenado en el array de desplazamientos.

El array de desplazamientos contiene parejas de enteros sin signo, de modo que debe tener un número par de valores. Además, el final del array se marca con dos valores cero.

    static UINT desplazamientos[] = {
        100, 800,
        0, 0
    };
...
    case WM_MENUSELECT:
           MenuHelp(msg, wParam, lParam, GetMenu(hwnd), hInstance, GetDlgItem(hwnd, ID_STATUS), ids);
           break;

Si tenemos un segundo nivel de menús desplegables, tendremos que añadir una nueva pareja de valores al array de desplazamientos. El primer valor de la pareja será un desplazamiento entre identificadores de menú y de cadena y el segundo el número de orden del submenú.

Esto implica algunos problemas si, por ejemplo, nuestro menú tiene dos opciones de menú horizontal, y en la primera posición del segundo hay un submenú popup. El desplazamiento de ese segundo submenú es 0:

LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
menu MENU
{
    POPUP "Principal"
    {
        MENUITEM "Nuevo", IDM_NUEVO
        MENUITEM "Abrir...", IDM_ABRIR
        MENUITEM "Guardar", IDM_GUARDAR
        MENUITEM "Guardar como...", IDM_GUARDAR_COMO
        MENUITEM SEPARATOR
        MENUITEM "Salir", IDM_SALIR
    }
    POPUP "Ver"
    {
        POPUP "Tamaño de letra"
        {
            MENUITEM "Pequeña", IDM_PEQUE
            MENUITEM "Mediana", IDM_MEDIANA
            MENUITEM "Grande", IDM_GRANDE
        }
        MENUITEM "Barra", IDM_BARRA
    }
}

LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_MODERN
STRINGTABLE
{
    IDS_NUEVO                   "Nuevo fichero"
    IDS_ABRIR                   "Abrir un fichero existente"
    IDS_GUARDAR                 "Guardar fichero actual"
    IDS_GUARDAR_COMO            "Guardar una copia del fichero actual con otro nombre"
    IDS_SALIR                   "Salir de la aplicación"
    IDS_BARRA                   "Mostrar u ocultar barra de estado"
    IDS_PEQUE                   "Establece un tamaño de letra pequeño"
    IDS_MEDIANA                 "Establece un tamaño de letra normal"
    IDS_GRANDE                  "Establece un tamaño de letra grande"
    IDS_PRINCIPAL               "Menú principal"
    IDS_VER                     "Opciones de visualización"
    IDS_TAMANO                  "Opciones para el tamaño de letra"
}

Para esta estructura de menú, los valores del array de desplazamientos son:

    static UINT desplazamientos[] = {
        100, 800,
        802, 0,
        0, 0
    };

Pero, tal como está diseñada la función, se cargará la misma cadena para todos los menús desplegables con desplazamiento cero, es decir, para el menú principal y para el de tamaño de letra.

Aunque hay algunas cosas que podemos hacer para que funcione correctamente con nuestros menús, las soluciones serán engorrosas y posiblemente no funcionen en futuras versiones del API. Lo más habitual es no mostrar textos de ayuda para los menús popup, sino sólo para las opciones de ítems de menú.

Además, esta función tiene otras limitaciones que desaconsejan o limitan su uso en nuestros programas, por ejemplo, no es fácil que funcione correctamente con menús creados en la ejecución, ya que las cadenas se cargan desde recursos de cadena. Todas las cadenas se cargan desde la misma instancia, de modo que para los menús creados desde varias dll no funcionará bien.

Ejemplo 83


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 83 win083.zip 2012-06-15 3327 bytes 197

Tamaño y altura

Generalmente, las dimensiones de las barras de estado se ajustan automáticamente por su procedimiento de ventana, ignorando cualquier tamaño indicado por la aplicación. La anchura es la misma que la del área de cliente de la ventana padre, y la altura se ajusta en función del tamaño de fuente seleccionada por el contexto de dispositivo de la ventana de estado.

Por eso es importante, como ya comentamos antes, que la ventana padre procese el mensaje WM_SIZE, y lo reenvíe a la ventana de estado.

Sin embargo, la aplicación puede asignar la altura mínima de la ventana de estado, o más concretamente, del área útil de la ventana de estado, descontando los bordes. Esto se hace con el mensaje SB_SETMINHEIGHT, indicando en wParam la altura mínima, en pixels. Generalmente esto sólo será necesario en barras de estado owner-draw.

También podemos recuperar la dimensiones de los bordes de una barra de estado mediante el mensaje SB_GETBORDERS. En el parámetro lParam tendremos que pasar la dirección de un array de tres enteros en los que recibiremos las anchuras del borde horizontal, vertical y el la distancia entre los rectángulos interiores que contienen el texto, respectivamente.