51 Control Tooltip

Tooltip
Tooltip

Los controles Tooltip son pequeñas ventanas que aparecen sobre otros controles, generalmente llamados herramientas, y que desaparecen al cabo de un tiempo corto. Normalmente se usan para mostrar pistas o ayudas al usuario sobre la tarea asignada al control, o para mostrar el texto completo de un ítem de una lista o árbol cuando parte de ese texto queda oculto tras el borde del control.

Cuando veamos las barras de herramientas veremos que se trata de conjuntos de botones, cada uno de los cuales indica la acción mediante un icono. Esto hace que a menudo no sea evidente qué hace cada herramienta. En estos casos, los tooltips serán especialmente útiles.

De nuevo estamos ante controles estáticos, en el sentido de que no sirven para obtener datos por parte del usuario, sino para mostrarle información.

De forma similar a lo que pasa con las listas de imágenes, veremos que estos controles se usan conjuntamente con otros que veremos en próximos capítulos, como list-views, tree-views, barras de herramientas, etc.

Creación de tooltip

Los controles tooltip se crean usando la función CreateWindowEx, indicando como tipo de ventana la constante TOOLTIPS_CLASS, el estilo extendido WS_EX_TOOLWINDOW, al menos el estilo WS_POPUP, más los estilos específicos de controles tooltip que queramos.

En realidad, los controles tooltip siempre tienen los estilos WS_EX_TOOLWINDOW y WS_POPUP, aunque no se especifiquen de forma explícita.

Para las coordenadas y dimensiones usaremos la constante CW_USEDEFAULT, puesto que la posición dependerá de la posición del ratón y del control al que pertenezca el tooltip, y su tamaño del contenido que le asignemos.

           hwndTip = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLTIPS_CLASS, NULL,
                              WS_POPUP | TTS_ALWAYSTIP |  TTS_BALLOON,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              hwnd, NULL,
                              hInstance, NULL);

Inicialmente, el control tooltip estará oculto, por eso no especificamos el estilo WS_VISIBLE. De todos modos, aunque lo indiquemos, no se activará.

También es conveniente hacer que la ventana tooltip sea la "topmost", de modo que nunca sea ocultada por otras ventanas o controles:

           SetWindowPos(hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

Es convieniente conservar el manipulador de ventana del tooltip en una variable estática, sobre todo si necesitamos enviarle mensajes después de crear la ventana.

Esto es necesario porque al tratarse de una ventana con el estilo WS_POPUP, el décimo parámetro se trata como un manipulador de menú, y no como un identificador de control, por lo que debe ser cero. Esto hace que no podamos enviarle mensajes usando SendDlgItemMessage, y necesitarmos el manipulador de ventana del tooltip.

Como con el resto de los controles comunes, es necesario asegurarse de que la DLL ha sido cargada mediante una llamada a la función InitCommonControlsEx indicando el valor de bandera ICC_TAB_CLASSES en el miembro dwICC de la estructura INITCOMMONCONTROLSEX que pasaremos como parámetro.

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

Estilos

Los controles tooltip tienen varios estilos propios que controlan varios aspectos gráficos:

  • TTS_ALWAYSTIP: el control tooltip se activa aunque la ventana padre no tenga el foco.
  • TTS_BALLOON: tooltip en forma de globo, con las esquinas redondeadas y un saliente que apunta al control al que pretenece.
  • TTS_CLOSE: añade un botón de cerrar, es necesario que también esté activo el estilo TTS_BALLOON y que se haya asignado un título, además deben estar activos los estilos visuales.
  • TTS_NOANIMATE: desactiva la animación inicial del control, que hace que parezca que la ventana se desenrolla.
  • TTS_NOFADE: desactiva la animación final, que hace que la ventana se desvanezca.
  • TTS_NOPREFIX: evita que se eliminen los caracteres '&' y que la cadena se de por terminada cuando se encuentre el primer carácter tabulador.
  • TTS_USEVISUALSTYLE: está relacionado con hiperenlaces, y no lo veremos en este capítulo.

Activar y desactivar tooltips

Por defecto, una vez creado, el tooltip quedará activado, pero podemos desactivarlo y volverlo a activar mediante el mensaje TTM_ACTIVATE, indicando en wParam un valor TRUE para activarlo o FALSE para desactivarlo.

    SendMessage(hwndTip, TTM_ACTIVATE, (WPARAM)TRUE, 0);

Cambios de color

Es posible modificar los colores del fondo y de los caracteres de un control tooltip, usando los mensaje TTM_SETTIPBKCOLOR y TTM_SETTIPTEXTCOLOR, respectivamente. En ambos casos, el color deseado se especifica en el parámetro wParam.

    SendMessage(hwndTip, TTM_SETTIPTEXTCOLOR, (WPARAM)RGB(255,0,0), 0);
    SendMessage(hwndTip, TTM_SETTIPBKCOLOR, (WPARAM)RGB(240,255,255), 0);

Hay que tener en cuenta que, como pasaba con las barras de progreso, si se activan los controles visuales, estos mensajes no tendrán efecto, y se usarán siempre los colores del tema.

Asignar título e icono

A cada ventana tooltip se le puede asociar un icono y un título. Hay que asignar ambos o ninguno, los dos van unidos, aunque el icono que indiquemos puede ser nulo.

Para hacerlo disponemos del mensaje TTM_SETTITLE, en el que usaremos el parámetro wParam para indicar el icono, y el parámetro lParam para indicar el texto del título:

           HICON hIcon = LoadIcon(hInstance, "Icono");
           SendMessage(hwndTip, TTM_SETTITLE, (WPARAM)hIcon, (LPARAM)"Ejemplo 087");
           DestroyIcon(hIcon);

Como icono podemos usar uno de los iconos predefinidos, para lo que disponemos de varias constantes:

  • TTI_NONE: Sin icono.
  • TTI_INFO: Icono de informacion.
  • TTI_WARNING: Icono de aviso.
  • TTI_ERROR: Icono de error.
  • TTI_INFO_LARGE: Icono de información grande.
  • TTI_WARNING_LARGE: Icono de aviso grande.
  • TTI_ERROR_LARGE: Icono de error grande.

Los iconos grandes sólo están disponibles si se activan los estilos visuales, con el estilo clásico sólo se pueden especificar los iconos pequeños, o TTI_NONE.

Si se activan los estilos visuales, en wParam podemos especificar cualquier icono, mediante su manipulador.

Nota:

la versión actual de "commctrl.h" cuando escribo este capítulo tiene algunos errores con respecto a este tema concreto. No están definidas las constantes "TTI_", ni el mensaje "TTM_SETTITLE". Para poder utilizar títulos de tooltips en nuestros programas deberemos añadir las siguientes definiciones:

   #ifndef TTM_SETTILE
   #ifdef UNICODE
   #define TTM_SETTITLE TTM_SETTITLEW
   #else
   #define TTM_SETTITLE TTM_SETTITLEA
   #endif
   #endif

   #ifndef TTI_NONE
   // ToolTip Icons (Set with TTM_SETTITLE)
   #define TTI_NONE                0
   #define TTI_INFO                1
   #define TTI_WARNING             2
   #define TTI_ERROR               3
   #if (_WIN32_WINNT >= 0x0600)
   #define TTI_INFO_LARGE          4
   #define TTI_WARNING_LARGE       5
   #define TTI_ERROR_LARGE         6
   #endif  // (_WIN32_WINNT >= 0x0600)
   #endif
   

Limitar anchura

Podemos limitar la anchura máxima del control tooltip, de modo que el texto que incluye se fragmente en distintas líneas si supera la anchura establecida. Para limitar la anchura máxima se usa el mensaje TTM_SETMAXTIPWIDTH, indicando el lParam la anchura máxima en pixels, o -1 para permitir cualquier anchura.

        SendMessage(hwndTip, TTM_SETMAXTIPWIDTH, 0, 150);

Asignar a herramienta

Cada control tooltip puede ser asignado a ninguna, a una o a varias herramientas, o controles. Además, una vez asignada una herramienta, tambien puede ser eliminada.

Para añadir herramientas se usa el mensaje TTM_ADDTOOL, indicando en lParam un puntero a una estructura TOOLINFO, que contiene los datos necesarios para añadir un control o un rectángulo a un tooltip.

Algunos de los campos de la estructura TOOLINFO es obligatorio especificarlos. Principalmente, el primero de ellos, cbSize que contiene el tamaño de la estructura. A ese campo le asignaremos el valor sizeof(TOOLINFO).

El campo uFlags debe contener algunas banderas que indiquen, por lo menos, el tipo de contenido de otros campos:

  • TTF_IDISHWND: indica que el campo uId contiene el manipulador de ventana de un control. Si no se especifica, uId debe contener un identificador de herramienta, pero esto sólo se aplica a herramientas pertenecientes a barras de herramientas. uId también puede ser cero, si usamos un rectángulo para asociarle un tooltip.
  • TTF_SUBCLASS: para que el procedimiento de ventana del tooltip intercepte algunos mensajes del ratón, esto nos evita tener que usar el mensaje TTM_RELAYEVENT. Normalmente no será necesario controlar los mensajes de ratón para los tooltips, de modo que es más sencillo dejar que ellos los traten internamente.

El resto de las banderas no tiene uso con controles y rectángulos, y los veremos en otros capítulos.

El campo hwnd debe contener el manipulador de la ventana padre del tooltip.

uId debe contener el manipulador de ventana del control que queremos añadir al tooltip, o cero, si estamos añadiendo un área rectángular.

El campo rect indica un área rectangular a la que se asocia el tooltip. Para que se use este rectángulo uId debe ser cero, y no debe especificarse la bandera TTF_IDISHWND. Si uId contiene un identificador o un manipulador de ventana, este campo debe ser cero.

El campo hinst se usará sólo si el campo lpszText contiene un identificador de recurso de cadena.

El campo lpszText puede contener una cadena o un identificador de recurso de cadena. También puede tomar el valor LPSTR_TEXTCALLBACK, para indicar que la ventana padre debe suministrar una cadena cuando reciba el mensaje de notificación TTN_GETDISPINFO. Esta cadena es el texto que se mostrará en el tooltip.

El campo lParam sólo se usa con herramientas de barras de herramientas.

El campo lpReserved no se usa.

Asignar tooltip a un control

Para añadir un tooltip a un control (o más propiamente, un control a un tooltip, puesto que el mismo tooltip atiende a varios controles), tenemos que iniciar los miembros cbSize, hwnd, uFlags, uId y lpszText de una estructura TOOLINFO con los valores adecuados, y enviar el mensaje TTM_ADDTOOL.

TOOLINFO toolInfo = { 0 };
...

	toolInfo.cbSize = sizeof(toolInfo);
	toolInfo.hwnd = hwnd;
	toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
	toolInfo.uId = (UINT_PTR)GetDlgItem(hwnd, CM_PRUEBA);
	toolInfo.lpszText = "Este botón emite un 'beep'.";
	SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);

Asignar tooltip a un rectángulo

Si se trata de añadir un área rectángular, además hay que iniciar el miembro rect, y dejar uId a cero:

TOOLINFO toolInfo = { 0 };
...

	toolInfo.cbSize = sizeof(toolInfo);
	toolInfo.uFlags = TTF_SUBCLASS;
	toolInfo.hwnd = hwnd;
	GetClientRect(hwnd, &toolInfo.rect);
	toolInfo.lpszText = "Esta es la ventana del ejemplo 87";
	SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);

En este ejemplo hemos iniciado el miembro rect con el rectángulo que define el área de cliente de la ventana padre. Si la ventana cambia de tamaño, el rectángulo asociado no lo hará, por lo que tendremos que procesar el mensaje WM_SIZE si queremos adaptar el área del tooltip al nuevo tamaño.

Eliminar un control de un tooltip

También hay que usar una estructura TOOLINFO en la que inicialemos los miembros sbSize, hwnd y uId. A continuación enviaremos el mensaje TTM_DELTOOL, con un puntero a la estructura TOOLINFO.

TOOLINFO toolInfo = { 0 };
...

	toolInfo.cbSize = sizeof(toolInfo);
	toolInfo.hwnd = hwnd;
	toolInfo.uId = (UINT_PTR)GetDlgItem(hwnd, CM_PRUEBA);
	SendMessage(hwndTip, TTM_DELTOOL, 0, (LPARAM)&toolInfo);

Si se trata de un área rectangular usaremos los campos cbSize, hwnd y rect:

TOOLINFO toolInfo = { 0 };
...
	toolInfo.cbSize = sizeof(toolInfo);
	toolInfo.hwnd = hwnd;
	GetClientRect(hwnd, &toolInfo.rect);
	SendMessage(hwndTip, TTM_DELTOOL, 0, (LPARAM)&toolInfo);

Si usamos el mensaje WM_SIZE para adaptar el área del tooltip, deberemos borrarla primero, usando este mensaje y luego volver a crearla.

Usar cadenas de recursos

Cuando añadimos herramientas a un control tooltip podemos especificar cadenas literales, como en los ejemplos anteriores, pero también podemos usar cadenas procedentes de recursos STRINGTABLE. Para ello basta con usar el identificador del recurso de cadena en el campo lpszText de la estructura TOOLINFO, y asignar al campo hinst de esa estructrua un manipulador de la instancia que contiene el recurso.

Usaremos un fichero de recursos para definir las cadenas:

STRINGTABLE
{
    STR_HABILITAR "Este botón habilita los tooltips."
}

E inicializaremos los campos de la estructura TOOLINFO de la forma adecuada:

	TOOLINFO toolInfo = { 0 };

	toolInfo.cbSize = sizeof(toolInfo);
	toolInfo.hwnd = hwnd;
	toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
	toolInfo.uId = (UINT_PTR)GetDlgItem(hwnd, CM_HABILITAR);
	toolInfo.hinst = hInstance;
	toolInfo.lpszText = STR_HABILITAR;
	SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);

Ejemplo 87

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 87 win087.zip 2012-06-15 17549 bytes 773

Notificaciones

Los controles tooltip disponen de algunos mensajes de notificación que nos permiten controlar de forma detallada algunos aspectos, veremos ahora algunos de ellos.

Estos mensajes de notificación vienen el el formato de un mensaje WM_NOTIFY, donde wParam contiene el identificador del control que envía el mensaje, y lParam es un puntero a una estructura NMHDR, o una estructura cuyo primer campo es una estructura NMHDR y otros campos adicionales, que dependen del control que envía el mensaje. Este es el caso de los mensajes de notificación de los tooltips.

Generalmente, el valor de wParam no resulta útil, ya que varios controles pueden tener el mismo identificador. En su lugar se usará alguno de los campos de NMHDR, o de la estructura específica usada por el control.

Mensaje de petición de texto

Nota:

Hay dos mensajes de notificación equivalentes, TTN_GETDISPINFO y TTN_NEEDTEXT. Los dos tienen el mismo valor, pero el primero sustituye al segundo, que probablemente quedará obsoleto en el futuro. Cada mensaje tiene una estructura de datos asociada, el primero NMTTDISPINFO y el segundo TOOLTIPTEXT, de nuevo, el segundo ha sido sustituido, y no lo usaremos.

El funcionamiento de este mensaje es un poco complicado de explicar, veremos si puedo hacerlo claramente.

Existe una tercera forma de asignar textos a un tooltip. Si cuando añadimos un control a un tooltip especificamos la constante LPSTR_TEXTCALLBACK para el campo lpszText, estaremos indicando que el texto del tooltip para ese control se obtendrá de la propia aplicación cada vez que sea necesario.

    TOOLINFO toolInfo = { 0 };

        toolInfo.cbSize = sizeof(toolInfo);
        toolInfo.hwnd = hwnd;
        toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
        toolInfo.uId = (UINT_PTR)GetDlgItem(hwnd, CM_DESHABILITAR);
        toolInfo.lpszText = LPSTR_TEXTCALLBACK; // <- Asignar texto mediante notificación
        SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);

Cuando se vaya a mostrar un tooltip para un control para el que se ha especificado la constante LPSTR_TEXTCALLBACK para el campo lpszText, se envía un mensaje de notificación TTN_GETDISPINFO, a travé de un mensaje WM_NOTIFY a la ventana padre del tooltip, que deberemos procesar. Cuando recibamos un mensaje de notificación WM_NOTIFY, generalmente podremos ignorar el parámetro wParam, y en un primer lugar, tomaremos el parámetro lParam como un puntero a una estructura NMHDR, ya que no sabemos qué tipo de control ha enviado la notificación, y por lo tanto, no sabemos que estructura viene apuntada por ese parámetro.

    LPNMHDR pnmhdr;
...
        case WM_NOTIFY:
           pnmhdr = (LPNMHDR)lParam;
           switch(pnmhdr->code) {
...

Una vez que sabemos qué tipo de mensaje de notificación hemos recibido, también podremos determinar qué tipo de estructura viene apuntada por lParam, y, en el caso concreto del mensaje de notificación TTN_GETDISPINFO, sabremos que se trata de una estructura TOOLTIPTEXT. Por lo tanto, trataremos a ese parámetro como un puntero a una de esas estructuras.

    LPNMHDR pnmhdr;
    LPNMTTDISPINFO pnmttdispinfo;
...
        case WM_NOTIFY:
           pnmhdr = (LPNMHDR)lParam;
           switch(pnmhdr->code) {
             case TTN_GETDISPINFO:
                pnmttdispinfo = (LPNMTTDISPINFO)pnmhdr;

A continuación tendremos que identificar el control concreto para el que se ha generado la notificación del tooltip. Para ello tendremos que consultar el miembro uFlags de la estructura NMTTDISPINFO, para ver si contiene la bandera TTF_IDISHWND. En ese caso, el campo idFrom de la estructura NMHDR que es el primer campo de NMTTDISPINFO contendrá un manipulador de la ventana. Esto será lo normal, ya que nosotros habremos especificado esa bandera al añadir el control al tooltip, pero no está de más verificarlo.

Como ese campo es un manipulador de ventana, podremos obtener el identificador del control mediante una llamada a la función GetDlgCtrlID:

    LPNMHDR pnmhdr;
    LPNMTTDISPINFO pnmttdispinfo;
...
        case WM_NOTIFY:
           pnmhdr = (LPNMHDR)lParam;
           switch(pnmhdr->code) {
             case TTN_GETDISPINFO:
                pnmttdispinfo = (LPNMTTDISPINFO)pnmhdr;
                if (pnmttdispinfo->uFlags & TTF_IDISHWND) {
                    switch(GetDlgCtrlID((HWND)pnmttdispinfo->hdr.idFrom)) {
                       case CM_DESHABILITAR:
...

Ahora tenemos varias formas de indicar el texto para el tooltip, elegiremos la que más nos convenga:

  • Copiar una cadena de 80 caracteres o menos (incluido en nulo terminador), en el campo szText de la estructura NMTTDISPINFO.
    strcpy(pnmttdispinfo->szText, "Deshabilita los tooltips.");
    
  • Asignar la dirección de una cadena al campo lpszText de la estructrua NMTTDISPINFO.
    pnmttdispinfo->lpszText = "Deshabilita los tooltips.";
    
  • Asignar un identificador de recurso de cadena campo lpszText y un manipulador de instancia al miembro hinst de la estructrua NMTTDISPINFO.
    pnmttdispinfo->lpszText = STR_DESHABILITAR;
    pnmttdispinfo->hinst = hInstance;
    

Si la cadena no va a modificarse a lo largo de la vida de la aplicación, podemos añadir la bandera TTF_DI_SETITEM al campo uFlags de la estructura NMTTDISPINFO. Esto hará que el tooltip almacene el texto y no vuelva a generar una notificación para el mismo control. Si por el contrario, el texto es variable, podemos omitir esa bandera y volveremos a recibir notificaciones para el mismo control cada vez que sea necesario visualizar su tooltip.

El ejemplo completo queda así:

    LPNMHDR pnmhdr;
    LPNMTTDISPINFO pnmttdispinfo;
...
        case WM_NOTIFY:
           pnmhdr = (LPNMHDR)lParam;
           switch(pnmhdr->code) {
             case TTN_GETDISPINFO:
                pnmttdispinfo = (LPNMTTDISPINFO)pnmhdr;
                if (pnmttdispinfo->uFlags & TTF_IDISHWND) {
                    switch(GetDlgCtrlID((HWND)pnmttdispinfo->hdr.idFrom)) {
                       case CM_DESHABILITAR:
                          pnmttdispinfo->uFlags |= TTF_DI_SETITEM;
                          strcpy(pnmttdispinfo->szText, "Deshabilita los tooltips.");
                          break;
                       case CM_HABILITAR:
                          pnmttdispinfo->uFlags |= TTF_DI_SETITEM;
                          pnmttdispinfo->lpszText = "Habilita los tooltips.";
                          break;
                    }
                }
                break;
           }
           break;

Ejemplo 88

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 88 win088.zip 2012-06-15 3613 bytes 785

Notificaciones de mostrar y ocultar

Cada vez que una ventana tooltip vaya a ser mostrada se envía un mensaje de notificación TTN_SHOW y cuando va a ser ocultada, uno TTN_POP. Ambos se envían mediante un mensaje WM_NOTIFY, y en ambos casos, el parámetro lParam contiene un puntero a una estructura NMHDR.

No he encontrado ninguna utilidad a estos mensajes. El único ejemplo que se me ha ocurrido es contar cada vez que se muestra u oculta el tooltip, y mostrar esa cuenta en la ventana. También se podría usar esta notificación para visualizar un texto en la barra de estado.

        case WM_NOTIFY:
           pnmhdr = (LPNMHDR)lParam;
           switch(pnmhdr->code) {
             case TTN_SHOW:
                mostrado++;
                InvalidateRect(hwnd, NULL, TRUE);
                break;
             case TTN_POP:
                ocultado++;
                InvalidateRect(hwnd, NULL, TRUE);
                break;
			}
			break;
...
        case WM_PAINT:
           hdc = BeginPaint(hwnd, &ps);
           SetBkMode(hdc, TRANSPARENT);
           sprintf(cad, "Mostrado=%d  Ocultado=%d", mostrado, ocultado);
           TextOut(hdc, 10, 40, cad, strlen(cad));
           EndPaint(hwnd, &ps);
           break;

Personalización

Es posible personalizar un tooltip, cambiando la fuente, los colores o encargando a la aplicación el dibujo de algunos detalles del tooltip.

El mensaje de notificación NM_CUSTOMDRAW se envía para muchos controles, incluídos algunos que ya hemos visto, como por ejemplo, los botones.

En el caso de los tooltips, los cambios de fuente que hagamos sólo tendrán enfecto en el estilo clásico. Si activamos los estilos visuales, los cambios de fuente serán ignorados.

En el caso que nos ocupa recibiremos un mensaje de notificación NM_CUSTOMDRAW justo antes de que se empiece a pintar el control, con el código de etapa de dibujo CDDS_PREPAINT. Esto nos permite tomar varias acciones:

  • Modificar la fuente, en cuyo caso debemos retornar el valor CDRF_NEWFONT. Esto indica al procedimiento de ventana del control que debe recalcular el tamaño del texto, y por lo tanto, el del control.
  • Modificar el color del texto, mediante la función SetTextColor. En ese caso podemos retornar CDRF_DODEFAULT, para que el procedimiento siga su curso normnal.
  • Retornar el valor CDRF_NOTIFYPOSTPAINT, para que volvamos a recibir un mensaje de notificación cuando el tooltip haya sido dibujado del todo. En este caso, el código de etapa será CDDS_POSTPAINT.

El resto de los códigos de retorno no se aplican a los tooltips, o al menos no parece que funcionen. En muchos casos porque los tooltips no tienen ítems ni subítems.

Si hemos retornado CDRF_NOTIFYPOSTPAINT recibiremos un mensaje de notificación con el código CDDS_POSTPAINT. Podemos aprovechar esta notificación para hacer retoques en el tooltip, y digo retoques, porque el tooltip ya estará pintado. Por supuesto, esos "retoques" pueden incluir borrar el contenido completo del tooltip y volver a pintarlo.

En este ejemplo procesamos los mensaje WM_NOTIFY para que el tooltip del control identificado con CM_BOTON3 se muestre con otra fuente, en color rojo y con un borde grueso.

    LPNMHDR pnmhdr;
    LPNMTTCUSTOMDRAW pnmttcustomdraw;
...
        case WM_CREATE:
...
           hFont = CreateFont(-30, 0, 0, 450, 300, FALSE, FALSE, FALSE,
                DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
                PROOF_QUALITY, DEFAULT_PITCH | FF_ROMAN, "Times New Roman");
           hPen = CreatePen(PS_SOLID, 4, RGB(255,0,0));
...
        case WM_NOTIFY:
           pnmhdr = (LPNMHDR)lParam;
           switch(pnmhdr->code) {
             case TTN_SHOW:
                mostrado++;
                InvalidateRect(hwnd, NULL, TRUE);
                break;
             case TTN_POP:
                ocultado++;
                InvalidateRect(hwnd, NULL, TRUE);
                break;
             case NM_CUSTOMDRAW:
                pnmttcustomdraw = (LPNMTTCUSTOMDRAW)pnmhdr;
                if(pnmttcustomdraw->nmcd.hdr.hwndFrom == hwndTip &&
                   GetDlgCtrlID((HWND)pnmttcustomdraw->nmcd.hdr.idFrom) == CM_BOTON3 &&
                   pnmttcustomdraw->nmcd.dwDrawStage == CDDS_PREPAINT) {
                    SelectObject(pnmttcustomdraw->nmcd.hdc, hFont);
                    SetTextColor(pnmttcustomdraw->nmcd.hdc, RGB(255,0,0));
                    return CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT;
                } else
                if(pnmttcustomdraw->nmcd.hdr.hwndFrom == hwndTip &&
                   GetDlgCtrlID((HWND)pnmttcustomdraw->nmcd.hdr.idFrom) == CM_BOTON3 &&
                   pnmttcustomdraw->nmcd.dwDrawStage == CDDS_POSTPAINT) {
                    SelectObject(pnmttcustomdraw->nmcd.hdc, GetStockObject(HOLLOW_BRUSH));
                    SelectObject(pnmttcustomdraw->nmcd.hdc, hPen);
                    Rectangle(pnmttcustomdraw->nmcd.hdc,
                              pnmttcustomdraw->nmcd.rc.left,
                              pnmttcustomdraw->nmcd.rc.top,
                              pnmttcustomdraw->nmcd.rc.right,
                              pnmttcustomdraw->nmcd.rc.bottom);
                    return CDRF_SKIPDEFAULT;
                } else
                return CDRF_DODEFAULT;
                break;
           }
           break;
...
        case WM_DESTROY:
           DeleteObject(hFont);
           DeleteObject(hPen);
...

Ejemplo 89

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 89 win089.zip 2012-06-15 4478 bytes 677

Otros mensajes

Los tooltips disponen de muchos otros mensajes, algunos de los cuales los explicaremos cuando veamos otros controles comunes, como tree-views y list-views, y otros que problemente no veamos, porque tienen poca utilidad.

Esta es una lista del resto de los mensajes que no hemos visto en este capítulo: