Compartir a través de


Controles personalizados

Esta sección contiene información sobre los controles personalizados o definidos por la aplicación.

Se tratan los temas siguientes.

Crear controles de Owner-Drawn

Se pueden crear botones, menús, controles de texto estáticos, cuadros de lista y cuadros combinados con una marca de estilo dibujada por el propietario. Cuando un control tiene el estilo dibujado por el propietario, el sistema controla la interacción del usuario con el control como de costumbre, realizando tareas como detectar cuando un usuario ha elegido un botón y notifica al propietario del botón del evento. Sin embargo, dado que el control es dibujado por el propietario, la ventana primaria del control es responsable de la apariencia visual del control. La ventana primaria recibe un mensaje cada vez que se debe dibujar el control.

En el caso de los botones y los controles de texto estáticos, el estilo dibujado por el propietario afecta a la forma en que el sistema dibuja todo el control. En el caso de los cuadros de lista y los cuadros combinados, la ventana primaria dibuja los elementos dentro del control y el control dibuja su propio contorno. Por ejemplo, una aplicación puede personalizar un cuadro de lista para que muestre un mapa de bits pequeño junto a cada elemento de la lista.

En el código de ejemplo siguiente se muestra cómo crear un control de texto estático dibujado por el propietario. Supongamos que se define Unicode.

// g_myStatic is a global HWND variable.
g_myStatic = CreateWindowEx(0, L"STATIC", L"Some static text", 
            WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, 
            25, 125, 150, 20, hDlg, 0, 0, 0);

En el ejemplo siguiente, desde el procedimiento de ventana para el cuadro de diálogo que contiene el control creado en el ejemplo anterior, el mensaje de WM_DRAWITEM se controla mostrando el texto en un color personalizado, utilizando la fuente predeterminada. Tenga en cuenta que no es necesario llamar a BeginPaint y EndPaint al controlar WM_DRAWITEM.

case WM_DRAWITEM:
{
    LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
    if (pDIS->hwndItem == g_myStatic)
    {
        SetTextColor(pDIS->hDC, RGB(100, 0, 100));
        WCHAR staticText[99];
        int len = SendMessage(myStatic, WM_GETTEXT, 
            ARRAYSIZE(staticText), (LPARAM)staticText);
        TextOut(pDIS->hDC, pDIS->rcItem.left, pDIS->rcItem.top, staticText, len);
    }
    return TRUE;
}

Para obtener más información sobre los controles dibujados por el propietario, vea Crear un cuadro de lista dibujado por el propietario y cuadros combinados dibujados por el propietario.

Subclase de la clase Window de un control existente

La subclases de un control existente es otra manera de crear un control personalizado. El procedimiento de subclase puede modificar los comportamientos seleccionados del control mediante el procesamiento de los mensajes que afectan a los comportamientos seleccionados. Todos los demás mensajes pasan al procedimiento de ventana original para el control . Por ejemplo, una aplicación puede mostrar un pequeño mapa de bits junto al texto en un control de edición de solo lectura y de una sola línea mediante la subclase del control y el procesamiento del mensaje WM_PAINT . Para obtener más información, vea Acerca de los procedimientos de ventana y los controles de subclases.

Implementación de una clase de ventana de Application-Defined

Para crear un control que no se base explícitamente en un control existente, la aplicación debe crear y registrar una clase de ventana. El proceso para registrar una clase de ventana definida por la aplicación para un control personalizado es el mismo que registrar una clase para una ventana normal. Para crear un control personalizado, especifique el nombre de la clase de ventana en la función CreateWindowEx o en una plantilla de cuadro de diálogo. Cada clase debe tener un nombre único, un procedimiento de ventana correspondiente y otra información.

Como mínimo, el procedimiento de ventana dibuja el control . Si una aplicación usa el control para permitir que el usuario escriba información, el procedimiento de ventana también procesa los mensajes de entrada desde el teclado y el mouse y envía mensajes de notificación a la ventana primaria. Además, si el control admite mensajes de control, el procedimiento de ventana procesa los mensajes enviados por la ventana primaria u otras ventanas. Por ejemplo, los controles suelen procesar el mensaje WM_GETDLGCODE enviado por cuadros de diálogo para dirigir un cuadro de diálogo para procesar la entrada del teclado de una manera determinada.

El procedimiento de ventana de un control definido por la aplicación debe procesar cualquier mensaje de control predefinido en la tabla siguiente si el mensaje afecta al funcionamiento del control.

Mensaje Recomendación
WM_GETDLGCODE Procesar si el control usa las teclas ENTRAR, ESC, TAB o flecha. La función IsDialogMessage envía este mensaje a los controles de un cuadro de diálogo para determinar si procesar las claves o pasarlas al control.
WM_GETFONT Procese si también se procesa el mensaje de WM_SETFONT .
WM_GETTEXT Procese si el texto del control no es el mismo que el título especificado por la función CreateWindowEx .
WM_GETTEXTLENGTH Procese si el texto del control no es el mismo que el título especificado por la función CreateWindowEx .
WM_KILLFOCUS Procesar si el control muestra un símbolo de intercalación, un rectángulo de foco u otro elemento para indicar que tiene el foco de entrada.
WM_SETFOCUS Procesar si el control muestra un símbolo de intercalación, un rectángulo de foco u otro elemento para indicar que tiene el foco de entrada.
WM_SETTEXT Procese si el texto del control no es el mismo que el título especificado por la función CreateWindowEx .
WM_SETFONT Procesar si el control muestra texto. El sistema envía este mensaje al crear un cuadro de diálogo que tenga el estilo DS_SETFONT.

 

Los mensajes de control definidos por la aplicación son específicos del control especificado y se deben enviar explícitamente al control mediante una función SendMessage o SendDlgItemMessage . El valor numérico de cada mensaje debe ser único y no debe entrar en conflicto con los valores de otros mensajes de ventana. Para asegurarse de que los valores de mensaje definidos por la aplicación no entren en conflicto, una aplicación debe crear cada valor agregando un número único al valor de WM_USER .

Envío de notificaciones desde un control

Es posible que se requieran controles personalizados para enviar notificaciones de eventos a la ventana primaria para que la aplicación host pueda responder a estos eventos. Por ejemplo, una vista de lista personalizada podría enviar una notificación cuando el usuario selecciona un elemento y otra notificación cuando se hace doble clic en un elemento.

Las notificaciones se envían como mensajes WM_COMMAND o WM_NOTIFY . WM_NOTIFY mensajes llevan más información que WM_COMMAND mensajes.

El identificador de control es un número único que la aplicación usa para identificar el control que envía el mensaje. La aplicación establece el identificador de un control cuando crea el control. La aplicación especifica el identificador en el parámetro hMenu de la función CreateWindowEx o en el miembro id de la estructura DLGITEMTEMPLATEEX .

Dado que el propio control no establece el identificador de control, el control debe recuperar el identificador para poder enviar mensajes de notificación. Un control debe usar la función GetDlgCtrlID para recuperar su propio identificador de control. Aunque el identificador de control se especifica como identificador de menú cuando se crea el control, no se puede usar la función GetMenu para recuperar el identificador. Como alternativa, un control puede recuperar el identificador del miembro hMenu en la estructura CREATESTRUCT mientras procesa el mensaje WM_CREATE .

Los ejemplos siguientes, donde hwndControl es el identificador de la ventana de control y CN_VALUECHANGED es una definición de notificación personalizada, muestran las dos maneras de enviar una notificación específica del control.

 // Send as WM_COMMAND.
SendMessage(GetParent(hwndControl), 
    WM_COMMAND,
    MAKEWPARAM(GetDlgCtrlID(hwndControl), CN_VALUECHANGED),
    (LPARAM)hwndControl);

// Send as WM_NOTIFY.           
NMHDR nmh;
nmh.code = CN_VALUECHANGED;
nmh.idFrom = GetDlgCtrlID(hwndControl);
nmh.hwndFrom = hwndControl;
SendMessage(GetParent(hwndControl), 
    WM_NOTIFY, 
    (WPARAM)hwndControl, 
    (LPARAM)&nmh);

Tenga en cuenta que la estructura NMHDR puede formar parte de una estructura más grande definida por el control que contiene información adicional. En el ejemplo, los valores antiguos y nuevos del control pueden estar contenidos en esta estructura. (Estas estructuras extendidas se usan con muchas notificaciones estándar; por ejemplo, vea LVN_INSERTITEM, que usa la estructura NMLISTVIEW ).

Accesibilidad

Todos los controles comunes admiten la accesibilidad activa de Microsoft (MSAA), que permite el acceso mediante programación mediante aplicaciones tecnológicas accesibles, como lectores de pantalla. MSAA también permite que Automatización de la interfaz de usuario, una tecnología más reciente, interactúe con los controles.

Los controles personalizados deben implementar la interfaz IAccessible (para admitir MSAA) o las interfaces de Automatización de la interfaz de usuario, o ambas. De lo contrario, los productos de tecnología accesible podrán obtener solo información muy limitada sobre la ventana de control, no tendrán acceso a las propiedades del control y no podrán desencadenar eventos en el control.

Para obtener más información sobre cómo hacer que el control sea accesible, consulte API de Automatización de Windows.

Conceptual

Referencia de control general

Personalización de la apariencia de un control mediante dibujo personalizado

Mensajes de control

Uso de estilos visuales con controles Owner-Drawn