Capítulo 44 Control scrollbar avanzado

Terminamos el repaso de los controles más comunes del API de Windows hablando del control de barra de desplazamiento. Vamos a completar el contenido del capítulo 12 con las explicaciones de todos los mensajes y características que no se mencionaron entonces.

Controles de barra de desplazamiento y barras estándar

Las barras de desplazamiento estándar son las que suelen aparecer en la zona derecha e inferior de algunas ventanas.

Aunque aparentemente se trata de cosas similares, existen algunas diferencias entre los controles de barra de desplazamiento y las barras de desplazamiento estándar.

Para empezar, las barras estándar están siempre fuera del área de cliente, mientras que los controles son ventanas que están situadas dentro del área de cliente.

Otra diferencia es que las barras estándar están diseñadas para permitir desplazar el contenido del área de cliente, y para crearlas basta con indicar los estilos de ventana WS_HSCROLL, WS_VSCROLL o ambos. Los controles de barra de desplazamiento se crean como el resto de los controles, ya sea mediante un fichero de recursos o bien insertados durante la ejecución, mediante las funciones CreateWindow o CreateWindowEx.

Otra diferencia es que los controles de barra de desplazamiento, como ventanas que son, pueden recibir el foco del teclado, cosa que no sucede con las barras estándar.

Los controles de barra de desplazamiento también tienen un interfaz de teclado interno.

Insertar controles scrollbar durante la ejecución

Evidentemente, también es posible insertar controles de barra de desplazamiento durante la ejecución. En este caso tendremos que insertar una ventana de la clase "SCROLLBAR". Para insertar el control también usaremos las funciones CreateWindow y CreateWindowEx.

    HWND hctrl;
...
        case WM_CREATE:
           hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
           /* Insertar control Edit */
           hctrl = CreateWindowEx(
              0,
              "SCROLLBAR",    /* Nombre de la clase */
              NULL,           /* Texto del título */
              SBS_HORZ |
              WS_CHILD | WS_VISIBLE | WS_TABSTOP, /* Estilo */
              5, 5,            /* Posición */
              120, 85          /* Tamaño */
              hwnd,            /* Ventana padre */
              (HMENU)ID_SCROLL,/* Identificador del control */
              hInstance,       /* Instancia */
              NULL);           /* Sin datos de creación de ventana */

Como vemos, usamos los mismos valores que en el fichero de recursos: un identificador, la clase de ventana (en este caso "SCROLLBAR"), una combinación de estilos, la posición y las dimensiones.

Al igual que en los demás controles, el identificador del control se suministra a través del parámetro hMenu, por lo que será necesario hacer un casting a HMENU.

Cambiar colores

No tenemos demasiado control sobre los colores de los controles de barra de desplazamiento. Sólo podemos modificar el color de la zona sobre la que se deplaza el cursor de la barra.

Para ello debemos procesar el mensaje WM_CTLCOLORSCROLLBAR. Como en otros casos, podemos cambiar el color del fondo, usando la función SetBkColor, y debemos devolver un pincel del color usado para pintar ese fondo:

        case WM_CTLCOLORSCROLLBAR:
           SetBkColor((HDC)wParam, RGB(0,255,0));
           return (LRESULT)pincel;

Estilos de scrollbar

En el capítulo 12 sólo mencionamos los estilos básicos SBS_HORZ y SBS_VERT, pero existen algunos más que veremos a continuación.

Estilos de orientación

Los controles de barra de desplazamiento pueden tener dos orientaciones: horizontal o vertical. El estilo SBS_HORZ sirve para crear un control de barra de desplazamiento horizontal. El estilo SBS_VERT, uno vertical.

En el primer caso, si no se especifican los estilos SBS_BOTTOMALIGN o SBS_TOPALIGN, la barra tendrá la altura, anchura y posición especificados por los parámetros usados en la función CreateWindow o CreateWindowEx.

En el segundo caso, si no se especifican los estilos SBS_RIGHTALIGN o SBS_LEFTALIGN, la barra tendrá la altura, anchura y posición especificados por los parámetros usados en la función CreateWindow o CreateWindowEx.

Alineamiento con los bordes

Existen cuatro estilos para alinear el control con cada uno de los cuatro bordes del rectángulo indicado en la función CreateWindow o CreateWindowEx:

SBS_BOTTOMALIGN para alinear un control vertical con el borde superior del rectángulo.

SBS_TOPALIGN para alinear un control vertical con el borde superior del rectángulo.

SBS_LEFTALIGN para alinear un control horizontal con el borde izquierdo del rectángulo.

SBS_RIGHTALIGN para alinear un control horizontal con el borde derecho del rectángulo.

Si se usa cualquiera de estos estilos, el control de barra de desplazamiento tendrá la anchura por defecto para las barras de desplazamiento del sistema, independientemente del tamaño del rectángulo usado en la función.

Opciones para cajas de tamaño

Las cajas de tamaño son barras de desplazamiento con un aspecto diferente. Seguro que las conoces, son los controles que nos permiten cambiar el tamaño de una ventana, y que suelen encontrarse en la esquina inferior derecha de las ventanas que pueden cambiar de tamaño. A veces, estos controles son invisibles, y sólo se distinguen porque cambia el cursor del ratón, mostrando dos flechas con la orientación arriba-izquierda y abajo-derecha. En otras ocasiones son visibles mediante un mapa de bits consistente en tres barras diagonales y paralelas.

Nosotros podemos usar este tipo de controles (aunque su utilidad es francamente limitada), mediante barras de desplazamiento con los estilos siguientes:

SBS_SIZEBOX crea un control de caja de tamaño. Si no se especifican ninguno de los estilos SBS_SIZEBOXBOTTOMRIGHTALIGN o SBS_SIZEBOXTOPLEFTALIGN, el control tendrá la altura, anchura y posición especificados por los parámetros de la llamada a la función CreateWindow o CreateWindowEx.

SBS_SIZEGRIP lo mismo, pero el control se muestra con un borde realzado (sólo funciona correctamente en Windows 95).

Alineamiento de cajas de tamaño

SBS_SIZEBOXBOTTOMRIGHTALIGN alinea la esquina inferior derecha del control con la esquina inferior derecha del rectángulo definido por los parámetros de la función CreateWindow o CreateWindowEx.

SBS_SIZEBOXTOPLEFTALIGN alinea la esquina superior izquierda del size box con la esquina superior izquierda del rectángulo definido por los parámetros de la función CreateWindow o CreateWindowEx.

Si se usa cualquiera de estos dos estilo, la caja de tamaño tendrá el tamaño por defecto para las cajas de tamaño del sistema.

Estos estilos se deben usar conjuntamente con SBS_SIZEBOX o SBS_SIZEGRIP.

Mostrar u ocultar barras de desplazamiento

En ocasiones nos puede interesar inhibir u ocultar barras de desplazamiento. Esto es frecuente con las barras estándar, por ejemplo, cuando todo el contenido a mostrar en una ventana cabe en el área de cliente, podemos optar por inhibir las barras de desplazamiento, de modo que sigan siendo visibles, aunque no respondan al usuario, o podemos optar por ocultarlas, dejando que el espacio que ocupan pueda ser usado por el área de cliente.

Con nuestros controles de barra de desplazamiento podemos hacer lo mismo, para ello disponemos de dos funciones:

La función ShowScrollBar nos permite mostrar u ocultar un control de barra de desplazamiento o una barra estándar.

Si se trata de una barra estándar, en el primer parámetro indicaremos la ventana a la que pertenece mediante su manipulador. En el segundo parámetro indicaremos qué barra o barras queremos ocultar o mostrar: SB_HORZ para la horizontal, SB_VERT para la vertical o SB_BOTH para ambas. En el tercer parámetro indicaremos el valor TRUE para hacer visible la barra o FALSE para ocultarla:

  ShowScrollBar(hWnd, SB_VERT, FALSE); // ocultar barra estándar vertical
  ShowScrollBar(hWnd, SB_HORZ, TRUE);  // mostrar barra estándar horizontal

Si se trata de un control de barra de desplazamiento, en el primer parámetro deberemos indicar el manipulador del control. En el segundo parámetro usaremos el valor SB_CTL. El tercero tiene el mismo significado que en el caso anterior:

  ShowScrollBar(GetDlgItem(hwnd, ID_SCR1), SB_CTL, FALSE); // ocultar control de barra de desplazamiento

Deshabilitar o habilitar un control de barra de desplazamiento

Como con cualquier otro control, es posible habilitar o deshabilitar cualquier control de barra de desplazamiento mediante una llamada a la función EnableWindow:

  EnableWindow(GetDlgItem(hwnd, ID_SCR1), TRUE);  // Habilitar
  EnableWindow(GetDlgItem(hwnd, ID_SCR1), FALSE); // Deshabilitar

Deshabilitar o habilitar flechas

Por último, también podemos habilitar o deshabilitar una o ambas flechas de un control de barra de desplazamiento o de una barra de desplazamiento estándar. Bueno, en realidad, deshabilitar ambas flechas equivale a deshabilitar el control completo, pero esto nos da dos formas diferentes de conseguir el mismo resultado.

Usando funciones

Para habilitar o deshabilitar las flechas de una barra de desplazamiento se usa la función EnableScrollBar.

Si se trata de una barra estándar, el primer parámetro debe contener el manipulador de la ventana a la que pertenece la barra. En caso de tratarse de un control, debe ser el manipulador de ventana del propio control.

Para barras estándar, el segundo parámetro puede ser uno de los valores: SB_HORZ, SB_VERT o SB_BOTH, para referirse a la barra estándar horizontal, vertical o ambas, respectivamente. En caso de un control, el valor debe ser SB_CTL.

El tercer parámetro puede tener uno de los valores siguientes:

  • ESB_DISABLE_UP: para deshablitar la flecha hacia arriba en una barra vertical.
  • ESB_DISABLE_LEFT: para deshablitar la flecha hacia la izquierda en una barra horizontal.
  • ESB_DISABLE_LTUP: equivale a las dos anteriores.
  • ESB_DISABLE_DOWN: para deshablitar la flecha hacia abajo en una barra vertical.
  • ESB_DISABLE_RIGHT: para deshablitar la flecha hacia hacia la derecha en una barra horizontal.
  • ESB_DISABLE_RTDN: equivale a las dos anteriores.
  • ESB_DISABLE_BOTH: para deshabilitar ambas flechas.
  • ESB_ENABLE_BOTH: para habilitar ambas flechas.
   EnableScrollBar(hwnd, SB_HORZ, ESB_DISABLE_RIGHT);
   EnableScrollBar(GetDlgItem(hwnd, ID_SCR1), SB_CTL, ESB_DISABLE_UP);

Ya hemos comentado que deshabilitar ambas flechas equivale a deshabilitar la barra completa. Esto es tanto así que para habilitar un control deshabilitado por completo se puede usar tanto la función EnableScrollBar con el valor ESB_ENABLE_BOTH como la función EnableWindow con el valor TRUE.

De forma simétrica, deshabilitar ambas flechas mediante la función EnableScrollBar, ya sea una después de otra o ambas a la vez, equivale a usar la función EnableWindow con el valor FALSE.

Usando mensajes

Además de la función EnableScrollBar, también podemos habilitar o deshabilitar flechas de barras de desplazamiento usando el mensaje SBM_ENABLE_ARROWS.

En este caso, el parámetro lParam no se usa, y debe valer 0. En el parámetro wParam usaremos el mismo valor que en el tercer parámetro de la función EnableScrollBar.

Este mensaje sólo sirve para habilitar o deshabilitar flechas en un control de barra de desplazamiento, y no en una barra estándar. Por supuesto, se debe enviar a una ventana, usando el manipulador de ventana del control.

Como siempre, podemos usar dos funciones para enviar mensajes a un control: SendMessage o SendDlgItemMessage. La primera función la usaremos cuando conozcamos el manipulador de la ventana que debe recibir el mensaje, aunque podríamos usar la función GetDlgItem para obtener ese manipulador, cuando no lo tengamos será más sencillo usar la segunda función. De este modo, estas tres funciones son equivalentes:

   HWND hctl = GetDlgItem(hwnd, ID_SCR1);
   SendMessage(hctl, SBM_ENABLE_ARROWS, ESB_DISABLE_UP, 0);
   SendMessage(GetDlgItem(hwnd, ID_SCR1), SBM_ENABLE_ARROWS, ESB_DISABLE_UP, 0);
   SendDlgItemMessage(hwnd, ID_SCR1, SBM_ENABLE_ARROWS, ESB_DISABLE_UP, 0);

También hemos comentado con anterioridad que a pesar del nombre, la función SendDlgItemMessage sirve para enviar mensajes a controles que estén tanto en cuadros de diálogo como en ventanas normales.

Mensajes de barras de desplazamiento

Los controles de barra de desplazamiento no envían mensajes de notificación. Al contrario que otros controles, todas las barras de desplazamiento envían mensajes WM_HSCROLL o WM_VSCROLL, dependiendo de si se trata de barras horizontales o verticales, respectivamente.

Todo esto se comentó con suficiente detalle en el capítulo 12, por lo que no abundaremos en este tema. Tan sólo añadir un gráfico con los códigos de notificación asociados a cada parte de la barra de desplazamiento. Recordemos que estos códigos se reciben en la palabra de menor peso del parámetro wParam de los mensajes WM_HSCROLL y WM_VSCROLL.

Mensajes de barras de desplazamiento

Mensajes de barras de desplazamiento

Respuesta al teclado

Los controles de barra de desplazamiento tienen un interfaz de teclado predefinido, de modo que cuando tienen el foco, ciertas pulsaciones de teclas se convierten en mensajes WM_HSCROLL o WM_VSCROLL.

En la siguiente tabla vemos las teclas que se procesan, y qué códigos de notificación generan:

Tecla Código de notificación
Vertical Horizontal
Flecha arriba SB_LINEUP SB_LINELEFT
Flecha abajo SB_LINEDOWN SB_LINERIGHT
Flecha derecha SB_LINEDOWN SB_LINERIGHT
Flecha izquierda SB_LINEUP SB_LINELEFT
Página abajo SB_PAGEDOWN SB_PAGERIGHT
Página arriba SB_PAGEUP SB_PAGELEFT
Fin SB_BOTTOM
Inicio SB_TOP

Vemos que en esta tabla aparecen dos códigos de notificación que no se comentaron en el punto anterior: SB_TOP y SB_BOTTOM. Esto se debe a que estos dos códigos sólo pueden ser generados mediante el teclado.

En un control de barra de desplazamiento vertical, SB_TOP indica que el cursor se debe desplazar al tope superior, y SB_BOTTOM al inferior. En controles horizontales indica los extremos izquierdo y derecho del control, respectivamente.

Las barras de desplazamiento estándar no tienen este interfaz de teclado definido, pero se puede hacer algo análogo procesando mensajes WM_KEYDOWN y enviando correspondientes mensajes WM_HSCROLL y WM_VSCROLL a la ventana.

Ejemplo 75

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 75 win075.zip 2007-03-15 3825 bytes 27

Desplazar contenido de ventanas

El uso más común de las barras de desplazamiento estándar es permitir la posibilidad de mostrar distintas áreas de un documento cuando éste no puede ser mostrado íntegramente en el área de cliente.

Cuando se responde a códigos de notificación como SB_PAGEUP, SB_PAGEDOWN, SB_TOP, etc, generalmente será necesario generar el contenido completo del área de cliente. Sin embargo, ante códigos como SB_LINEUP o SB_LINERIGHT, sólo una pequeña parte del área de cliente queda invalidada, el resto sigue siendo válida, después de desplazarla algunos pixels en la dirección adecuada.

Estas operaciones de desplazamientos de mapas de bits son tan frecuentes que, inevitablemente, el API dispone de funciones para realizarlas. Se trata de las funciones ScrollWindow y ScrollWindowEx, de las que es recomendable usar la segunda, ya que la primera se mantiene sólo por compatibilidad con versiones anteriores del API.

Esta función tiene bastantes parámetros, pero veremos que algunos de ellos no tendrán mucho uso en la mayor parte de los casos.

El primer parámetro es el manipulador de la ventana cuya área de cliete queremos desplazar.

El segundo indica las únidades lógicas a desplazar horizontalmente. Los valores negativos indican desplazamientos a la izquierda.

El tercer parámetro indica las unidades lógicas a desplazar verticalmente. Los valores negativos son desplazamientos hacia arriba.

El cuarto parámetros es un puntero a una estructrua RECT que indica la zona rectangular, dentro del área de cliete, que queremos desplazar. Si usamos el valor NULL se tomará el rectángulo correspondienta al área de cliente completa.

El quinto parámetro también es un puntero a una estructura RECT que define un área rectangular de recorte. Sólo los puntos dentro de ese área se verán afectados, de modo que los puntos fuera de ella que después del desplazamiento estén dentro se pintarán, pero los que estén dentro que posteriormente queden fuera, no. De nuevo, si se especifica el valor NULL, el rectángulo de recorte coincidirá con el del parámetro anterior.

El sexto parámetro es un manipulador de región. Antes de la llamada, esa región puede contener la zona de actualización actual, y la función la modificará para incluir las nuevas zonas que deben ser actualizadas. Si no nos interesa usar esta región, de nuevo podemos usar el valor NULL.

El séptimo parámetro es un puntero a una estructura RECT, la función actualiza ese rectángulo con la región rectangular invalidada después de realizado el desplazamiento. También podemos ignorar este dato usando el valor NULL.

El octavo y último parámetro es una bandera que puede tomar tres valores diferentes:

  • SW_INVALIDATE: invalida la región identificada por el sexto parámetro una vez realizado el desplazamiento.
  • SW_ERASE: Borra la región invalidada enviando un mensaje WM_ERASEBKGND a la ventana, cuando se especifica junto a valor SW_INVALIDATE.
  • SW_SCROLLCHILDREN: desplaza también las ventanas hijas incluidas en el área a desplazar. Además, se envía un mensaje WM_MOVE a cada una de las ventanas interseccionadas por ese área, aunque no hayan sido movidas.
   ScrollWindowEx(hwnd, 10, 0, &re, NULL, NULL, NULL, SW_INVALIDATE);

El uso de esta función implica, generalmente, procesar el mensaje WM_PAINT.

Otra función útil para desplazar el contenido de una ventana es ScrollDC. Esta función es muy parecida a la anterior, pero en lugar de un manipulador de ventana, se usa un manipulador de contexto de dispositivo, y no dispone del parámetro de banderas.

Existen además dos funciones que pueden resultar útiles para usar conjuntamente, como son UpdateWindow y RedrawWindow. La primera sirve para actualizar el área de cliente. Para ello se envía un mensaje WM_PAINT, si la región de actualización no es nula, evitando la cola, con lo que se procesará inmediatamente, el envío del mensaje se omite si esta región está vacía.

La segunda función es algo más complicada, pero tiene un funcionamiento parecido. También sirve para actualizar una zona de la ventana, pero al contrario que la anterior, podemos especificar un rectángulo o una región de actualización.

El primer parámetro es un manipulador de la ventana que queremos actualizar.

El segundo es un puntero a una estructura RECT que contiene el rectángulo de actualización.

El tercero es un manipulador de una región de actualización, si se especifica una regino en este parámetro, se ignora cualquier valor del anterior. Si ambos son NULL, se actualiza toda el área de cliente.

El último parámetro son banderas que pueden afectar al modo en que trabaja la función, validando o invalidando la región especificada, indicando si debe actualizar o borra el contenido, u opciones sobre las ventanas hijas. Ver la descripción de la función RedrawWindow para más detalles.

  RedrawWindow(hwnd, NULL, NULL, RDW_UPDATENOW | RDW_ALLCHILDREN);

Colores y medidas

Generalmente, usaremos los colores y medidas estándar para las barras de desplazamiento. Ya hemos visto que el único color que podemos personalizar es el del fondo de la zona de desplazamiento en la barra, el resto corresponde con los colores normales de los botones, ya que las flechas y el cursor se comportan, en cieto modo, como tales.

En cuanto a los colores, ya sabemos que podemos usar las funciones GetSysColor y SetSysColors para obtener o modificar, respectivamente, los valores de color del sistema. Concretamente, para este caso, nos interesa el valor de COLOR_SCROLLBAR, que es el color del fondo de la zona de desplazamiento.

Valores de medidas del sistema

Las barras de desplazamiento estándar horizontales tienen definidas unas medidas en el sistema: SM_CXHSCROLL para la anchura y SM_CYHSCROLL para la altura. Del mismo modo, las verticales tienen unas medidas SM_CXVSCROLL para la anchura y SM_CYVSCROLL para la altura.

Para obtener estos valores se puede usar la función GetSystemMetrics, indicando la constante que interese.

   int anchura;

   anchura = GetSystemMetrics(SM_CXHSCROLL);

Los valores que podemos obtener mediante GetSystemMetrics son los siguientes:

Constante Descripción
SM_CXHSCROLL Anchura del mapa de bits de la flela en una barra de desplazamiento horizontal.
SM_CXHTHUMB Anchura de la caja de desplazamiento en una barra horizontal. A partir de la versión 4.0, este valor recupara la anchura de una barra de desplazamiento cuyo tamaño de página es cero.
SM_CXVSCROLL Anchura del mapa de bits de la flecha en una barra de desplazamiento vertical.
SM_CYHSCROLL Altura del mapa de bits de la flecha en una barra de desplazamiento horizontal.
SM_CYVSCROLL Altura del mapa de bits de la flecha en una barra de desplazamiento vertical.
SM_CYVTHUMB Altura de la caja de desplazamiento en una barra vertical. A partir de la versión 4.0, este valor recupera la altura de una barra de desplazamiento cuyo tamaño de página es cero.

Otros mensajes

Además de todos los mensajes comentados en el capítulo 12 y en este, hasta ahora, existen dos de los que no hemos hablado.

El primero de ellos es SBM_GETRANGE, que nos sirve para recuperar los valores mínimo y máximo del rango de un control de barra de desplazamiento.

En el parámetro wParam enviaremos un puntero a un entero, en ese entero recibiremos el valor mínimo del rango. En el parámetro lParam enviaremos otro puntero a un entero, en este recibiremos el valor máximo del rango.

El segundo mensaje es SBM_SETRANGEREDRAW, que se comporta de forma idéntica a SBM_SETRANGE, salvo que además de asignar un rango al control de barra de desplazamiento, también redibuja el control para mostrar su nuevo estado.

Como en el caso de SBM_SETRANGE, en el parámetro wParam pasaremos el valor mínimo del rango, y en lParam, el máximo.

Ejemplo 76

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 76 win076.zip 2007-03-15 258975 bytes 24