Capítulo 57 Control Trackbar

Ejemplo de trackbar

podemos considerar el control de trackbar como una variante del control scrollbar. Su funcionamiento es muy parecido, aunque en general se orientan a otro tipo de entradas de datos por parte del usuario, como asignar posiciones; también sirven para indicar el estado de progreso de una tarea o proceso.

El ejemplo más claro es en la reproducción de archivos multimedia, ya sean de sonido o de video. Este tipo de controles nos indica el punto en que se encuentra la reproducción, pero también permite elegir el punto en que queremos continuar la reproducción, avanzar o retroceder ciertos valores, etc.

Como en todos los controles comunes que estamos viendo, hay que asegurarse de que la DLL ha sido cargada invocando a la función InitCommonControlsEx indicando el valor de bandera ICC_BAR_CLASSES en el miembro dwICC de la estructura INITCOMMONCONTROLSEX que pasaremos como parámetro.

  INITCOMMONCONTROLSEX iCCE;
...
  iCCE.dwSize = sizeof(INITCOMMONCONTROLSEX);
  iCCE.dwICC = ICC_BAR_CLASSES;
  InitCommonControlsEx(&iCCE);

Insertar durante la ejecución

Esto ya es rutina. Como todos los controles, los trackbar también se pueden insertar en una ventana o diálogo durante la ejecución. Tan sólo tendremos que usar las funciones CreateWindow o CreateWindowEx e indicar en la clase de ventana el valor TRACKBAR_CLASS:

    hFont = CreateFont(-14, 0, 0, 0, 0, FALSE, FALSE, FALSE, 1, 0, 0, 0, 0, ("Ms Shell Dlg"));
    CreateWindowEx(0, TRACKBAR_CLASS, NULL,
        WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_HORZ,
        10,10,300,50,
        hwnd, (HMENU)ID_TRACKBAR,
        hInstance, NULL);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(FALSE, 0));

Hay varios estilos específicos para este tipo de control, pero los más básicos son los que afectan a la orientación: horizontal (TBS_HORZ) o vertical (TBS_VERT), aunque si no se especifica ninguno, el valor por defecto es horizontal.

En el parámetro hMenu, como siempre, indicaremos el identificador del control.

En principio, no sería necesario modificar la fuente, ya que este control no muestra ningún texto. (Ya veremos si esto es así). En cualquier caso, si se usa una fuente hay que recordar liberar el recurso antes de terminar el programa, usando DeleteObject.

Insertar desde fichero de recursos

Se usa un control general CONTROL, con la clase TRACKBAR_CLASS, y los estilos generales y específicos que queramos aplicar.

LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG1 DIALOG 0, 0, 341, 159
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "Ms Shell Dlg"
{
    CONTROL         "", 0, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS | TBS_BOTH | TBS_TOOLTIPS, 21, 20, 304, 15, WS_EX_LEFT
    PUSHBUTTON      "Cancel", IDCANCEL, 285, 138, 50, 14, 0, WS_EX_LEFT
    DEFPUSHBUTTON   "OK", IDOK, 285, 121, 50, 14, 0, WS_EX_LEFT
}

Partes del control trackbar

Partes de un trackbar

Partes de un trackbar

El deslizador es el cursor que desplaza el usuario para elegir un valor. El canal es la ranura sobre la que se desplaza el deslizador o thumb. Las marcas (o tics) son una escala de ayuda o guía para seleccionar valores.

Establecer rango de valores

Si no se especifica, el rango de valores por defecto es de 0 a 100. Es decir, el valor mínimo del deslizador correponde al valor 0, y el máxima al valor 100.

Este rango se puede modificar mediante el mensaje TBM_SETRANGE. En wParam indicaremos si el control debe redibujarse, y en lParam, en la palabra de menor peso el límite inferior y en la palabra de mayor peso el límite superior.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETRANGE, (WPARAM)FALSE, MAKELPARAM(100, 3000));

Al usar un DWORD para establecer ambos límites, el rango máximo que se puede establecer con este mensaje es de 0 a 35535, es decir, 16 bits.

Si necesitamos establecer valores para cualquiera de los extremos del margen mayores del ámbito de un WORD, disponemos de la pareja de mensaje TBM_SETRANGEMIN y TBM_SETRANGEMAX. De nuevo, en wParam indicaremos si queremos redibujar el control, y en lParam el límite inferior o superior del rango, respectivamente.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETRANGEMIN, (WPARAM)FALSE, (LPARAM)45000);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETRANGEMAX, (WPARAM)FALSE, (LPARAM)50000);

Para obtener los valores de los límites del rango actuales de un control trackbar disponemos de dos mensajes. TBM_GETRANGEMIN para obtener el límite inferior, y TBM_GETRANGEMAX para obtener el límite superior.

Modificar y leer posición del deslizador

Para asignar una posición al deslizador disponemos del mensaje TBM_SETPOS indicando en wParam si queremos actualizar el control en pantalla, y en lParam la nueva posición del cursor.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)posicion);

Hay otro mensaje para modificar la posición del deslizador, TBM_SETPOSNOTIFY, aunque en este caso no se usa el parámetro wParam, y se enviará un código de notificación TRBN_THUMBPOSCHANGING.

Para obtener la posición actual del deslizador usaremos el mensaje TBM_GETPOS.

    DWORD pos;
...
    pos = SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETPOS, 0, 0);

Estilos

Podemos agrupar los estilos propios de los controles trackbar en varias categorías:

Que afectan a la orientación

Podemos elegir la orientación del control, horizontalmente con el estilo TBS_HORZ, o verticalmente con el estilo TBS_VERT.

Además de afectar al aspecto visual del control, también influye en el tipo de mensajes que recibiremos cuando el usuario interactúe con él. Los trackbars horizontales recibirán mensaje WM_HSCROLL y los verticales WM_HSCROLL, cuando usemos las teclas del cursor o hagamos click con el ratón en el canal del control.

Relacionados con las marcas

Con el estilo TBS_AUTOTICKS se creará una marca automáticamente para cada incremento de valor.

Por el contrario, el estilo TBS_NOTICKS impide que se muestren marcas, incluyendo la primera y la última.

Para controles trackbar horizontales podemos elegir mostrar las marcas encima, con TBS_TOP o debajo, con TBS_BOTTOM.

De forma análogo, para controles trackbar verticales, podemos elegir mostrar las marcas a la izquierda, con el estilo TBS_LEFT, o a la derecha, con el estilo TBS_RIGHT.

Si queremos mostrar las marcas a ambos lados, independientemente de la orientación, disponemos del estilo TBS_BOTH.

Estos estilos afectan también al aspecto visual del deslizador. Cuando sólo se muestren marcas a uno de los lados, el extremo correspondiente del deslizador acabará en punta. Cuando se muestren a ambos lados ninguno de los extremos tendrá punta.

Colocar marcas

El mensaje TBM_SETTIC nos permite añadir marcas individuales en las posiciones indicadas en lParam. Por ejemplo, el siguiente código introduce dos marcas, una para el valor 25 y otra en el 50:

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)25);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)50);

El mensaje TBM_SETTICFREQ permite situar marcas cada cierto número de valores, indicando el periodo en el parámetro wParam. Por ejemplo, si nuestro trackbar tiene valores entre 0 y 100, e indicamos una frecuencia de 5, se situará una marca cada 5 valores, es decir, 20 marcas equidistantes:

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTICFREQ, (WPARAM)5, 0);

Obtener marcas

El mensaje TBM_GETNUMTICS nos devuelve el número de marcas actualmente en el control trackbar. Esta cuenta incluye las marcas de inicio y final que se insertan por defecto.

Con el mensaje TBM_GETTIC podemos obtener la posición lógica asociada a una marca, indicando en wParam su valor de índice. La posición lógica es el valor asociado a la marca. Este mensaje parece funcionar de forma diferente para marcas insertadas individualmente con TBM_SETTIC y con marcas insertadas automáticamente con TBM_SETTICFREQ.

En el primer caso se ignorarán las marcas de inicio y final, por lo tanto los valores válidos son dos menos que el valor obtenido por el mensaje TBM_GETNUMTICS. Por ejemplo, si hemos insertado tres marcas en las posiciones 25, 50 y 75, el valor obtenido por TBM_GETNUMTICS será 5, pero sólo podremos usar con este mensaje los valore0 0 a 2. Obtendremos los valores 25, 50 y 75 al usar el mensaje TBM_GETTIC.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)25);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)50);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)75);
    printf("%d\n", SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0));
    for(i=0; i < SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0); i++)
    printf("%d - %d\n", i, SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETTIC, (WPARAM)i, 0));

Salida:

5
0 - 25
1 - 50
2 - 75
3 - -1
4 - -1

En el caso de usar TBM_SETTICFREQ sí se tendrán en cuenta las marcas de inicio y final, pero los valores obtenidos no serán los valores lógicos esperados:

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTICFREQ, (WPARAM)25, 0);
    printf("%d\n", SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0));
    for(i=0; i < SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0); i++)
    printf("%d - %d\n", i, SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETTIC, (WPARAM)i, 0));

La salida ahora será:

5
0 - 1
1 - 2
2 - 3
3 - 4
4 - 5

Ignoro si esto es intencionado, pero resulta raro.

El mensaje TBM_GETPTICS nos da exactamente la misma información, pero en lugar de tener que indicar el índice de la marca a recuperar, nos devuelve la dirección de un array con los valores de todas las marcas. Este array será válido mientras no se añadan o eliminen marcas.

    DWORD* pos;
...
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTICFREQ, (WPARAM)25, 0);
    printf("%d\n", SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0));
    pos = SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETPTICS, 0, 0);
    for(i=0; i < SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0); i++)
    printf("%d - %d\n", i, pos[i]);

Dará la salida:

5
0 - 1
1 - 2
2 - 3
3 - 4
4 - 5

Eliminar marcas

Mediante el mensaje TBM_CLEARTICS se eliminarán todas las marcas insertadas, salvo las de inicio y final. En wParam indicaremos si queremos que se actualice el control en pantalla.

Posiciones de marcas

El mensaje TBM_GETTICPOS obtiene la distancia en coordenadas de cliente desde el extremo izquierdo del área de cliente del control.

De nuevo, este mensaje funciona de forma diferente con marcas insertadas manualmente y con las automáticas.

Si las marcas fueron insertadas manualmente, obtendremos desplazamientos en coordendas de cliente:

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)25);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)50);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIC, 0, (LPARAM)75);
    for(i=0; i < SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0); i++)
        printf("%d\n", SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETTICPOS, (WPARAM)i, 0));

La salida tiene la forma:

80
150
219
-1
-1

Por el contrario, con marcas insertadas automáticamente dan una salida de otro tipo:

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTICFREQ, (WPARAM)25, 0);
    for(i=0; i < SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETNUMTICS, 0, 0); i++)
        printf("%d\n", SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETTICPOS, (WPARAM)i, 0));

Da la salida:

14
17
19
22
25
28

Relacionados con los rangos

Trackbar con rango seleccionado

Rango seleccionado

Los controles trackbar también disponen de la opción de seleccinar un rango de valores seleccionados que se mostrarán resaltados, dentro del rango de valores totales disponibles. Para poder seleccionar un rango de valores, el control debe tener el estilo TBS_ENABLESELRANGE. Además, las marcas en las posiciones de principio y final de un rango de seleccionado son mostrados como triángulos, en lugar de líneas verticales.

Para seleccionar un rango disponemos de varios mensajes. El más sencillo es TBM_SETSEL. En wParam indicaremos si queremos que el control se actualice en pantalla para reflejar el rango seleccionado, en la palabra de menor peso de lParam indicaremos el límite inferior y en la de mayor peso el límite superior.

Este mensaje será suficiente en la mayor parte de las situaciones, pero dado que usaremos un valor de 32 bits para indicar los dos extremos, no nos servirá si los valores que queremos resaltar son mayores de un valor de 16 bits.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETSEL, (WPARAM)TRUE, MAKELPARAM(30, 70));

Para solventar esta limitación disponemos de una pareja de mensajes TBM_SETSELSTART y TBM_SETSELEND, el primero para establecer el límite inferior y el segundo para el superior. En wParam indicaremos si queremos que el control se actualice en pantalla, y en el segundo un valor de 32 bits para el límite indicado.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETSELSTART, (WPARAM)TRUE, (LPARAM)30);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETSELEND, (WPARAM)TRUE, (LPARAM)70);

Además, el mensaje TBM_CLEARSEL elimina la selección. En wParam seguiremos indicando si queremos o no actualizar el control.

Para obtener los límites de la selección actual disponemos de la pareja de mensajes TBM_GETSELSTART y TBM_GETSELEND que nos devuelven, respectivamente, el límite inferior y el superior.

Relacionados con el deslizador

Si se usa el estilo TBS_FIXEDLENGTH, podremos modificar el tamaño de deslizador del control trackbar.

Para ello disponemos del mensaje TBM_SETTHUMBLENGTH, al que pasaremos la longitud del deslizador deseada, en pixels, en el parámetro wParam.

Para obtener la longitud actual del deslizador se usa el mensaje TBM_GETTHUMBLENGTH.

El estilo TBS_NOTHUMB oculta el deslizador.

Tooltips

Si se usa el estilo TBS_TOOLTIPS, el control trackbar soportará tooltips. Por defecto, se crea un control tooltip que muestra la posición actual del deslizador.

Con el mensaje TBM_SETTIPSIDE podemos elegir la posición en la que se mostrará el tooltip, indicándolo en wParam. Para los horizontales podemos elegir encima o debajo, TBTS_TOP o TBTS_BOTTOM, y para los verticales izquierda o derecha, TBTS_LEFT o TBTS_RIGHT.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTIPSIDE, (WPARAM)TBTS_BOTTOM, 0);

El mensaje TBM_SETTOOLTIPS se puede usar para asignar un control tooltip previamente creado al control trackbar. Para ello pasaremos en wParam un manipulador de ventana del control tooltip.

    hToolNew = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLTIPS_CLASS, NULL,
            WS_POPUP | TTS_ALWAYSTIP,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            hwnd, NULL, hInstance, NULL);
    if(hToolNew) {
        SetWindowPos(hToolNew, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
        SendMessage(hToolNew, TTM_SETMAXTIPWIDTH, 0, 150);

        toolInfo.cbSize = sizeof(toolInfo);
        toolInfo.hwnd = hCtrl;;
        toolInfo.hinst = hInstance;
        toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
        toolInfo.uId = (UINT_PTR)GetDlgItem(hwnd, ID_TRACKBAR);
        toolInfo.lpszText = LPSTR_TEXTCALLBACK;
        SendMessage(hToolNew, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);

        hToolPrev = SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_GETTOOLTIPS, 0, 0);
        SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETTOOLTIPS, (WPARAM)hToolNew, 0);
    }

Por otra parte, tendremos que procesar la notificación TTN_GETDISPINFO para asignar el texto al tooltip cuando lo requiera:

    case WM_NOTIFY:
        pnmhdr = (LPNMHDR)lParam;
        switch(pnmhdr->code) {
            case TTN_GETDISPINFO:
            pnmttdispinfo = (LPNMTTDISPINFO)pnmhdr;
            if(GetDlgCtrlID((HWND)pnmttdispinfo->hdr.idFrom) == ID_TRACKBAR) {
                sprintf(cad, "Valor: %d", SendMessage(hCtrl, TBM_GETPOS, 0, 0));
                strcpy(pnmttdispinfo->lpszText, cad);
            }
            break;
        }
        break

Estas notificaciones también funcionarán con los tooltips creados por defecto.

Si fuera necesario, podemos recuperar el manipulador de ventana del tooltip actualmente asociado a un control trackbar mediante el mensaje TBM_GETTOOLTIPS.

Otros estilos

Hay otros estilos menos útiles, por ejemplo TBS_REVERSED se usa para trackbars "invertidas", donde un número más pequeño indica "mayor" y un número más grande indica "menor." Esto no afecta al control para nada, es sólo una etiqueta que puede ser consultada para determinar si un trackbar es normal o inverso.

Ejemplo WM_PRINTCLIENT

Ejemplo WM_PRINTCLIENT

Si no se indica el estilo TBS_DOWNISLEFT, en un control trackbar la tecla de dirección 'abajo' equivale a 'derecha' y 'arriba' a 'izquierda'. Utilizar este estilo invierte el comportamiento por defecto de modo que 'abajo' equivale a 'izquierda' y 'arriba' a 'derecha'.

Con el estilo TBS_TRANSPARENTBKGND el responsable de pintar el fondo es la ventana a través de un mensaje WM_PRINTCLIENT.

    static HBITMAP hBitmapRes;
    static HWND hCtrl;
    POINT punto;
    RECT wre;
    HDC memDC;
...
        case WM_CREATE:
            hCtrl = CreateWindowEx(0, TRACKBAR_CLASS, NULL,
                WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_TOOLTIPS | TBS_TRANSPARENTBKGND,
                10,10,300,50,
                hwnd, (HMENU)ID_TRACKBAR,
                hInstance, NULL);
            hBitmapRes = LoadBitmap(hInstance, "rojo");
...
        case WM_PRINTCLIENT:
            // Obtener posición del control en coordenadas de pantalla:
            GetWindowRect(hCtrl, &wre);
            // Calcular tamaño:
            wre.right -= wre.left;
            wre.bottom -= wre.top;
            // Convertir posición a coordenadas de cliente:
            punto.x = wre.left;
            punto.y = wre.top;
            ScreenToClient(hwnd, &punto);
            // Mostrar mapa de bits:
            hdc = (HDC)wParam;
            memDC = CreateCompatibleDC(hdc);
            SelectObject(memDC, hBitmapRes);
            BitBlt(hdc, punto.x, punto.y, wre.right, wre.bottom, memDC, 0, 0, SRCCOPY);
            DeleteDC(memDC);
            break;
...
        case WM_DESTROY:
            DeleteObject(hBitmapRes);
Nota:

El mensaje WM_PRINTCLIENT sólo se envía si están activos los estilos visuales, usando el manifiesto adecuado.

Desplazamientos

Al usar las teclas del cursor para desplazador el deslizador, por defecto, los incrementos y decrementos son de una unidad. Con las teclas de avance y retroceso de página los incrementos y decrementos son de 20 unidades por defecto. Los avances y retrocesos de página también se hacen haciendo click izquierdo sobre el canal.

Estos valores se pueden modificar con el mensaje TBM_GETLINESIZE, para cambiar el tamaño de línea y con TBM_GETPAGESIZE para cambiar el tamaño de página. En los dos mensajes se especifica el nuevo valor en el parámetro lParam.

    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETLINESIZE, 0, (LPARAM)2);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETPAGESIZE, 0, (LPARAM)10);

Para obtener los valores actuales de línea y página se usan los mensajes TBM_SETLINESIZE y TBM_SETPAGESIZE.

Ventanas compañeras

Ventanas compañeras

Ventanas compañeras

Se pueden añadir dos ventanas compañeras al control trackbar. Generalmente serán controles estáticos, pero en realidad puede ser cualquier tipo de ventana.

Para insertar una ventana compañera se usa el mensaje TBM_SETBUDDY. En wParam indicaremos un valor TRUE para insertar la ventana a la izquierda, si es un trackbar horizontal, o encima, si se trata de un trackbar vertical, o un valor FALSE para insertarla a la derecha o debajo, respectivamente.

En este ejemplo insertamos un botón como ventana compañera a la izquierda, y un texto estático a la derecha. Como siempre que insertamos controles en ejecución, si queremos modificar las fuentes deberemos enviar un mensaje WM_SETFONT.

    HWND hctrl;
    HFONT hFont;
...

    hFont = CreateFont(-14, 0, 0, 0, 0, FALSE, FALSE, FALSE, 1, 0, 0, 0, 0, ("Ms Shell Dlg"));
    CreateWindowEx(0, TRACKBAR_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_TOOLTIPS, 
                110,10,300,50, hwnd, (HMENU)ID_TRACKBAR, hInstance, NULL);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETLINESIZE, 0, (LPARAM)2);
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETPAGESIZE, 0, (LPARAM)10);
    hctrl = CreateWindowEx(0, "BUTTON", (LPSTR)"Botón", WS_CHILD | WS_VISIBLE, 10, 10, 60, 24, hwnd,
                          (HMENU)(ID_BOTON), hInstance, NULL);
    SendMessage(hctrl, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(FALSE, 0));
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETBUDDY, (WPARAM)TRUE, (LPARAM)hctrl);
    hctrl = CreateWindowEx(0, "STATIC", "derecha", WS_CHILD | WS_VISIBLE | SS_SIMPLE, 0,0,60,20, hwnd, NULL, hInstance, NULL);
    SendMessage(hctrl, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(FALSE, 0));
    SendDlgItemMessage(hwnd, ID_TRACKBAR, TBM_SETBUDDY, (WPARAM)FALSE, (LPARAM)hctrl);

El mensaje TBM_GETBUDDY sirve para obtener el manipulador de ventana de la compañera indicada en wParam, TRUE para la izquierda o superior, o false para la derecha o inferior, según se trate de un control trackbar horizontal o vertical, respectivamente.

Delimitadores de áreas

Disponemos de un par de mensajes para obtener las áreas delimitadoras de ciertas partes de los controles trackbar.

El mensaje TBM_GETCHANNELRECT recupera un puntero con el rectángulo delimitador del canal de control en lParam.

El mensaje TBM_GETTHUMBRECT recupera un puntero con el rectángulo delimitador del deslizador en lParam.

Formato de caracteres

Se puede modificar durante la ejecución el juego de caracteres usado por un control trackbar mediante el mensaje TBM_SETUNICODEFORMAT, indicando en wParam un valor distinto de cero para usar caractere Unicode, o cero para usar caracteres ANSI.

Para recuperar el juego de caracteres Unicode usado por un control trackbar se usa el mensaje TBM_GETUNICODEFORMAT.

Ignoro en qué influye usar uno u otro juego de caracteres en un control que no muestra texto, pero las opciones existen.

Notificaciones

Si el control tiene el estilo TBS_NOTIFYBEFOREMOVE se enviará al procedimiento de ventana de la ventana padre un código de notificación TRBN_THUMBPOSCHANGING cada vez que el usuario modifique la posición de deslizador, y antes de que el control se actualice. Esto permite a la aplicación evitar que los cambios de posición se consoliden según nos convenga.

En lParam recibiremos un puntero a una estructura NMTRBTHUMBPOSCHANGING con la información necesaria sobre la nueva posición del deslizador, en el miembro dwPos y el modo en que se ha modificado en el miembro nReason.

El miembro nReason puede ser uno de los siguientes valores, dependiendo de cómo se haya modificado la posición del deslizador:

  • TB_LINEUP: Se ha pulsado la tecla de o .
  • TB_LINEDOWN: Se ha pulsado la tecla de dirección o .
  • TB_PAGEUP: Se ha pulsado la tecla de RePág o se ha hecho click en el canal a la derecha o debajo de la posición actual del deslizador.
  • TB_PAGEDOWN: Se ha pulsado la tecla de AvPág o se ha hecho click en el canal a la izquierda o encima de la posición actual del deslizador.
  • TB_THUMBTRACK: Se está moviendo el deslizador con el ratón.
  • TB_TOP: Se ha pulsador la tecla Inicio.
  • TB_BOTTOM: Se ha pulsador la tecla Fin.
  • TB_THUMBPOSITION: Este código está relacionado con la captura del ratón.
  • TB_ENDTRACK: Este código está relacionado con la captura del ratón.

No hay notificación si el cambio de posición se produce como consecuencia de acciones de la rueda del ratón.

    LPNMHDR pnmhdr;
    NMTRBTHUMBPOSCHANGING* pnmtpc;
    char cad[100];
    char* szReason[] = {
        "TB_LINEUP",
        "TB_LINEDOWN",
        "TB_PAGEUP",
        "TB_PAGEDOWN",
        "TB_THUMBPOSITION",
        "TB_THUMBTRACK",
        "TB_TOP",
        "TB_BOTTOM",
        "TB_ENDTRACK"
    };
...
    case WM_CREATE:
        CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE | SS_SIMPLE, 10,70,220,20, 
            hwnd, (HMENU)(ET_TRACKBAR), hInstance, NULL);
...
    case WM_NOTIFY:
        pnmhdr = (LPNMHDR)lParam;
        switch(pnmhdr->code) {
            case TRBN_THUMBPOSCHANGING:
                pnmtpc = (NMTRBTHUMBPOSCHANGING*)pnmhdr;
                sprintf(cad, "%100s", " ");
                SetWindowText(GetDlgItem(hwnd, ET_TRACKBAR), cad);
                sprintf(cad, "%-25s -> %8d\n", szReason[pnmtpc->nReason], pnmtpc->dwPos);
                SetWindowText(GetDlgItem(hwnd, ET_TRACKBAR), cad);
                break;
Nota:

El código de notificación TRBN_THUMBPOSCHANGING sólo se envía si están activos los estilos visuales, usando el manifiesto adecuado y si el control tiene el estilo TBS_NOTIFYBEFOREMOVE.

Trackbar custom draw

Si queremos personalizar el aspecto de nuestro control trackbar deberemos procesar el código de notificación NM_CUSTOMDRAW. En lParam recibiremos un puntero a una estructura NMCUSTOMDRAW con la información necesaria para mostrar el control según nuestras preferencias.

El miembro dwItemSpec de esa estructura contendrá uno de los valores de Custom Draw Values, indicando qué parte del control se ha de actualizar.

Ejemplo 99

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 99 win099.zip 2021-10-31 12189 bytes 17