Procesar los mensajes procedentes de controles Scrollbar

Cada vez que el usuario realiza alguna acción en un control Scrollbar se envía un mensaje WM_HSCROLL o WM_VSCROLL, dependiendo del tipo de control, a la ventana donde está insertado. En realidad pasa algo análogo con todos los controles, pero en el caso de los Scrollbars, es imprescindible que el programa procese algunos de esos mensajes adecuadamente.

Estos mensajes entregan distintos valores en la palabra de menor peso del parámetro wParam, según la acción del usuario sobre el control. En la palabra de mayor peso se incluye la posición actual y en lParam el manipulador de ventana del control.

De modo que nuestra rutina para manejar los mensajes de los scrollbars debe ser capaz de distinguir el control del que procede el mensaje y el tipo de acción, para actuar en consecuencia.

En nuestro caso es irrelevante la orientación de la barra de scroll, podemos distinguirlos por el identificador de ventana, de todos modos procesaremos cada uno de los dos mensajes con una rutina distinta.

Para no recargar en exceso el procedimiento de ventana del diálogo, crearemos una función para procesar los mensajes de las barras de scroll. Y la llamaremos al recibir esos mensajes:

    switch (msg)                  /* manipulador del mensaje */
    {
...
        case WM_HSCROLL:
           ProcesarScrollH(hDlg, (HWND)lParam, 
              (int)LOWORD(wParam), (int)HIWORD(wParam));
           return FALSE;
        case WM_VSCROLL:
           ProcesarScrollV(hDlg, (HWND)lParam, 
              (int)LOWORD(wParam), (int)HIWORD(wParam));
           return FALSE;
...

Los códigos que tenemos que procesar son los siguientes:

  • SB_BOTTOM desplazamiento hasta el principio de la barra, en verticales arriba y en horizontales a la izquierda.
  • SB_TOP: desplazamiento hasta el final de la barra, en verticales abajo y en horizontales a la derecha.
  • SB_LINERIGHT y SB_LINEDOWN: desplazamiento una línea a la derecha en horizontales o abajo en verticales.
  • SB_LINELEFT y SB_LINEUP: desplazamiento un línea a la izquierda en horizontales o arriba en verticales.
  • SB_PAGERIGHT y SB_PAGEDOWN: desplazamiento de una página a la derecha en horizontales y abajo en verticales.
  • SB_PAGELEFT y SB_PAGEUP: desplazamiento un párrafo a la izquierda en horizontales o arriba en verticales.
  • SB_THUMBPOSITION: se envía cuando el thumb está en su posición final.
  • SB_THUMBTRACK: el thumb se está moviendo.
  • SB_ENDSCROLL: el usuario a liberado el thumb en una nueva posición.

En nuestro caso, no haremos que la variable asociada se actualice hasta que pulsemos el botón de aceptar, pero actualizaremos la posición del thumb y el valor del control edit asociado a cada scrollbar.

Veamos por ejemplo la rutina para tratar el scroll horizontal:

void ProcesarScrollH(HWND hDlg, HWND Control, int Codigo, int Posicion)
{
   int Pos = GetScrollPos(Control, SB_CTL);
   
   switch(Codigo) {
      case SB_BOTTOM:
         Pos = 0;
         break;
      case SB_TOP:
         Pos = 100;
         break;
      case SB_LINERIGHT:
         Pos++;
         break;
      case SB_LINELEFT:
         Pos--;
         break;
      case SB_PAGERIGHT:
         Pos += 5;
         break;
      case SB_PAGELEFT:
         Pos -= 5;
         break;
      case SB_THUMBPOSITION:
      case SB_THUMBTRACK:
         Pos = Posicion;
      case SB_ENDSCROLL:
         break;
   }
   if(Pos < 0) Pos = 0;
   if(Pos > 100) Pos = 100;
   SetDlgItemInt(hDlg, ID_EDITH, (UINT)Pos, FALSE);
   SetScrollPos(Control, SB_CTL, Pos, TRUE);          
}

Como puede observarse, actualizamos el valor de la posición dependiendo del código recibido. Nos aseguramos de que está dentro de los márgenes permitidos y finalmente actualizamos el contenido del control edit. La función para el scrollbar vertical es análoga, pero cambiando los identificadores de los códigos y los valores de los límites.

Hemos usado una función nueva, GetScrollPos para leer la posición actual del thumb.

Procesar mensajes de scrollbar usando SCROLLINFO

Como comentamos antes, a partir de la versión 4.0 de Windows existe otro mecanismo para inicializar los scrollbars. También tiene la doble forma de mensaje y función. Su uso se recomienda en lugar de GetScrollRange, que sólo se conserva por compatibilidad con Windows 3.x.

Usando GetScrollInfo, la función para procesar el mensaje del scrollbar horizontal quedaría así:

void ProcesarScrollH(HWND hDlg, HWND Control, int Codigo, int Posicion)
{
   SCROLLINFO si = {
       sizeof(SCROLLINFO), 
       SIF_ALL, 0, 0, 0, 0, 0};
 
   GetScrollInfo(Control, SB_CTL, &si);
   
   switch(Codigo) {
      case SB_BOTTOM:
         si.nPos = si.nMin;
         break;
      case SB_TOP:
         si.nPos = si.nMax;
         break;
      case SB_LINEDOWN:
         si.nPos++;
         break;
      case SB_LINEUP:
         si.nPos--;
         break;
      case SB_PAGEDOWN:
         si.nPos += si.nPage;
         break;
      case SB_PAGEUP:
         si.nPos -= si.nPage;
         break;
      case SB_THUMBPOSITION:
      case SB_THUMBTRACK:
         si.nPos = Posicion;
      case SB_ENDSCROLL:
         break;
   }
   if(si.nPos < si.nMin) si.nPos = si.nMin;
   if(si.nPos > si.nMax-si.nPage+1) si.nPos = si.nMax-si.nPage+1;

   SetScrollInfo(Control, SB_CTL, &si, true);
   SetDlgItemInt(hDlg, ID_EDITH, (UINT)si.nPos, FALSE);
}

Usamos los valores de la estructura para acotar la posición de la caja y para avanzar y retroceder de página, esto hace que nuestra función sea más independiente y que use menos constantes definidas en el fuente.

También se puede usar el mensaje SBM_GETSCROLLINFO, basta con cambiar la línea:

GetScrollInfo(Control, SB_CTL, &si);

por esta otra:

SendDlgItemMessage(hDlg, ID_SCROLLH,
   SBM_GETSCROLLINFO, 0, (LPARAM)&siv);

Devolver valores a la aplicación

Cuando el usuario ha decidido que los valores son los adecuados pulsará el botón de Aceptar. En ese momento deberemos capturar los valores de los controles scroll y actualizar la estructura de parámetros.

También en este caso podemos usar un mensaje o una función para leer la posición del thumb del scrollbar. La función ya la hemos visto un poco más arriba, se trata de GetScrollPos. El mensaje es SBM_GETPOS, ambos devuelven el valor de la posición actual del thumb del control.

Usaremos los dos métodos, uno con cada control. El lugar adecuado para leer esos valores sigue siendo el tratamiento del mensaje WM_COMMAND:

     case WM_COMMAND:
           switch(LOWORD(wParam)) {
              case IDOK:
                 Datos->ValorH = GetScrollPos(GetDlgItem(hDlg, ID_SCROLLH), SB_CTL);
                 Datos->ValorV = SendDlgItemMessage(hDlg, ID_SCROLLV, 
                    SBM_GETPOS, 0, 0);
                 EndDialog(hDlg, FALSE);
                 return TRUE;
              case IDCANCEL:
                 EndDialog(hDlg, FALSE);
                 return FALSE;

Ejemplos 11 y 12


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 11 win011.zip 2004-05-17 3524 bytes 416


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 12 win012.zip 2004-05-17 3616 bytes 338

Comentarios de los usuarios (2)

Diegomm
2011-06-20 12:02:31

Me he dado cuenta, de que el ejemplo 12, a diferencia del 11, cuando mueves el scrollbar y le das a cancelar no vuelve al valor anteriormente almacenado, sino que guarda el que se descarta.

No me he parado a ver el código para ver donde falla, pero la cuestión es que existe este error.

Un saludo

diegomm
2011-06-20 13:19:26

Despues de pararme un rato a ver que era lo que pasaba, me he dado cuenta que el error se ha producido al definir el fichero de recursos,ya que el codigo ofrecido en el ejemplo win012.zip, devuelve IDOK tanto el boton de aceptar como el de cancelar.

Bastará solo sustituir IDOK por IDCANCEL en el fichero de recursos win012.rc

Un saludo