Compartir a través de


TN062: Reflexión de mensajes para controles de Windows

Nota:

La nota técnica siguiente no se ha actualizado desde que se incluyó por primera vez en la documentación en línea. Como resultado, algunos procedimientos y temas podrían estar obsoletos o ser incorrectos. Para obtener información más reciente, se recomienda buscar el tema de interés en el índice de la documentación en línea.

En esta nota técnica se describe la reflexión de mensajes, una nueva característica de MFC 4.0. También contiene instrucciones para crear un control reutilizable sencillo que usa la reflexión de mensajes.

Esta nota técnica no analiza la reflexión de mensajes, ya que se aplica a los controles ActiveX (anteriormente denominados controles OLE). Consulte el artículo Controles ActiveX: subclase de un control de Windows.

¿Qué es la reflexión de mensajes?

Windows controla los mensajes de notificación enviados con frecuencia a sus ventanas primarias. Por ejemplo, muchos controles envían un mensaje de notificación de color de control (WM_CTLCOLOR o una de sus variantes) a su elemento primario para permitir que el elemento primario proporcione un pincel para pintar el fondo del control.

En Windows y en MFC antes de la versión 4.0, la ventana primaria, a menudo un cuadro de diálogo, es responsable de controlar estos mensajes. Esto significa que el código para controlar el mensaje debe estar en la clase de la ventana primaria y que debe duplicarse en cada clase que necesite controlar ese mensaje. En el caso anterior, cada cuadro de diálogo que quiera controles con fondos personalizados tiene que controlar el mensaje de notificación de color de control. Es mucho más fácil reutilizar el código si se puede escribir una clase de control que controle su propio color de fondo.

En MFC 4.0, el mecanismo anterior sigue funcionando: las ventanas primarias pueden controlar los mensajes de notificación. Sin embargo, MFC 4.0 facilita la reutilización ya que proporciona una característica denominada "reflexión de mensajes" que permite controlar estos mensajes de notificación en la ventana de control secundaria, en la ventana primaria, o en ambas. En el ejemplo de color de fondo del control, ahora puede escribir una clase de control que establezca su propio color de fondo controlando el mensaje WM_CTLCOLOR reflejado, sin depender del elemento primario. (Tenga en cuenta que, dado que MFC implementa la reflexión de mensajes, no Windows, la clase de ventana primaria debe derivarse de CWnd para que la reflexión de mensajes funcione).

Las versiones anteriores de MFC hacían algo similar a la reflexión de mensajes ya que proporcionaban funciones virtuales para algunos mensajes, como mensajes para cuadros de lista dibujados por el propietario (WM_DRAWITEM, etc.). El nuevo mecanismo de reflexión de mensajes es generalizado y coherente.

La reflexión de mensajes es compatible con el código escrito para versiones de MFC anteriores a la 4.0.

Si ha proporcionado un controlador para un mensaje específico o para un intervalo de mensajes, en la clase de la ventana primaria, invalidará los controladores de mensajes reflejados para el mismo mensaje, siempre que no llame a la función de controlador de clase base en su propio controlador. Por ejemplo, si controla WM_CTLCOLOR en la clase de cuadro de diálogo, el control invalidará los controladores de mensajes reflejados.

Si, en la clase de ventana primaria, se proporciona un controlador para un mensaje WM_NOTIFY específico o un intervalo de mensajes WM_NOTIFY, solo se llamará al controlador si el control secundario que envía esos mensajes no tiene un controlador de mensajes reflejado a través de ON_NOTIFY_REFLECT(). Si usa ON_NOTIFY_REFLECT_EX() en el mapa de mensajes, el controlador de mensajes puede permitir que la ventana primaria controle el mensaje o no. Si el controlador devuelve FALSE, el elemento primario también controlará el mensaje, mientras que una llamada que devuelve TRUE no permite que el elemento primario lo controle. Tenga en cuenta que el mensaje reflejado se controla antes que el mensaje de notificación.

Cuando se envía un mensaje WM_NOTIFY, la primera posibilidad de controlarlo se ofrece al control. Si se envía cualquier otro mensaje reflejado, la ventana primaria tendrá la primera posibilidad de controlarlo y el control recibirá el mensaje reflejado. Para ello, necesitará una función de controlador y una entrada adecuada en el mapa de mensajes de clase del control.

La macro de mapa de mensajes para los mensajes reflejados es ligeramente diferente a la de las notificaciones normales: tiene _REFLECT anexado a su nombre habitual. Por ejemplo, para controlar un mensaje WM_NOTIFY en el elemento primario, use la macro ON_NOTIFY en el mapa de mensajes del elemento primario. Para controlar el mensaje reflejado en el control secundario, use la macro ON_NOTIFY_REFLECT en el mapa de mensajes del control secundario. En algunos casos, los parámetros también son diferentes. Tenga en cuenta que ClassWizard normalmente puede agregar de manera automática las entradas de mapa de mensajes y proporcionar implementaciones de función esqueleto con parámetros correctos.

Consulte TN061: mensajes ON_NOTIFY y WM_NOTIFY para información sobre el nuevo mensaje WM_NOTIFY.

Entradas de asignación de mensajes y prototipos de función de controlador para mensajes reflejados

Para controlar un mensaje de notificación de control reflejado, use las macros de mapa de mensajes y los prototipos de función enumerados en la tabla siguiente.

ClassWizard normalmente puede agregar estas entradas de mapa de mensajes automáticamente y proporcionar implementaciones de función esqueleto. Consulte Definición de un controlador de mensajes para un mensaje reflejado para información sobre cómo definir controladores para mensajes reflejados.

Para convertir el nombre del mensaje en el nombre de la macro reflejada, anteponga ON_ y anexe _REFLECT. Por ejemplo, WM_CTLCOLOR se convierte en ON_WM_CTLCOLOR_REFLECT. (Para ver qué mensajes se pueden reflejar, realice la conversión opuesta en las entradas de macro de la tabla siguiente).

Las tres excepciones a la regla anterior son las siguientes:

  • La macro para las notificaciones de WM_COMMAND es ON_CONTROL_REFLECT.

  • La macro para las reflexiones de WM_NOTIFY es ON_NOTIFY_REFLECT.

  • La macro para las reflexiones de ON_UPDATE_COMMAND_UI es ON_UPDATE_COMMAND_UI_REFLECT.

En cada uno de los casos especiales anteriores, debe especificar el nombre de la función miembro del controlador. En los demás casos, debe usar el nombre estándar de la función de controlador.

Los significados de los parámetros y los valores devueltos de las funciones se documentan en el nombre de la función o el nombre de la función con On antepuesto. Por ejemplo, CtlColor se documenta en OnCtlColor. Varios controladores de mensajes reflejados necesitan menos parámetros que los controladores similares de una ventana primaria. Simplemente empareje los nombres de la tabla siguiente con los nombres de los parámetros formales de la documentación.

Entrada de mapa Prototipo de función
ON_CONTROL_REFLECT(wNotifyCode,memberFxn) afx_msg voidmemberFxn( );
ON_NOTIFY_REFLECT(wNotifyCode,memberFxn) afx_msg voidmemberFxn(NMHDR*pNotifyStruct, LRESULT*result);
ON_UPDATE_COMMAND_UI_REFLECT(memberFxn) afx_msg voidmemberFxn(CCmdUI*pCmdUI);
ON_WM_CTLCOLOR_REFLECT( ) afx_msg HBRUSH CtlColor (CDC*pDC, UINTnCtlColor);
ON_WM_DRAWITEM_REFLECT( ) afx_msg void DrawItem (LPDRAWITEMSTRUCTlpDrawItemStruct);
ON_WM_MEASUREITEM_REFLECT( ) afx_msg void MeasureItem (LPMEASUREITEMSTRUCTlpMeasureItemStruct);
ON_WM_DELETEITEM_REFLECT( ) afx_msg void DeleteItem (LPDELETEITEMSTRUCTlpDeleteItemStruct);
ON_WM_COMPAREITEM_REFLECT( ) afx_msg int CompareItem (LPCOMPAREITEMSTRUCTlpCompareItemStruct);
ON_WM_CHARTOITEM_REFLECT( ) afx_msg int CharToItem (UINTnKey, UINTnIndex);
ON_WM_VKEYTOITEM_REFLECT( ) afx_msg int VKeyToItem (UINTnKey, UINTnIndex);
ON_WM_HSCROLL_REFLECT( ) afx_msg void HScroll (UINTnSBCode, UINTnPos);
ON_WM_VSCROLL_REFLECT( ) afx_msg void VScroll (UINTnSBCode, UINTnPos);
ON_WM_PARENTNOTIFY_REFLECT( ) afx_msg void ParentNotify (UINTmessage, LPARAMlParam);

Las macros ON_NOTIFY_REFLECT y ON_CONTROL_REFLECT tienen variaciones que permiten que más de un objeto (como el control y su elemento primario) controlen un mensaje determinado.

Entrada de mapa Prototipo de función
ON_NOTIFY_REFLECT_EX(wNotifyCode,memberFxn) afx_msg BOOLmemberFxn(NMHDR*pNotifyStruct, LRESULT*result);
ON_CONTROL_REFLECT_EX(wNotifyCode,memberFxn) afx_msg BOOLmemberFxn( );

Control de mensajes reflejados: ejemplo de un control reutilizable

En este ejemplo sencillo se crea un control reutilizable denominado CYellowEdit. El control funciona igual que un control de edición normal, salvo que muestra texto negro sobre un fondo amarillo. Sería fácil agregar funciones miembro que permitieran al control CYellowEdit mostrar colores diferentes.

Prueba del ejemplo que crea un control reutilizable

  1. Cree un cuadro de diálogo en una aplicación existente. Para más información, consulte el tema del editor de diálogos.

    Debe tener una aplicación en la que desarrollar el control reutilizable. Si no dispone de una aplicación existente que pueda usar, cree una aplicación basada en cuadros de diálogo mediante AppWizard.

  2. Con el proyecto cargado en Visual C++, use ClassWizard para crear una nueva clase denominada CYellowEdit basada en CEdit.

  3. Agregue tres variables miembro a su clase CYellowEdit. Las dos primeras serán variables COLORREF para contener el color del texto y el color de fondo. La tercera será un objeto CBrush que contendrá el pincel para pintar el fondo. El objeto CBrush le permite crear el pincel una vez, simplemente haciéndole referencia después, y destruir el pincel automáticamente cuando se destruye el control CYellowEdit.

  4. Inicialice las variables miembro escribiendo el constructor de la siguiente manera:

    CYellowEdit::CYellowEdit()
    {
        m_clrText = RGB(0, 0, 0);
        m_clrBkgnd = RGB(255, 255, 0);
        m_brBkgnd.CreateSolidBrush(m_clrBkgnd);
    }
    
  5. Con ClassWizard, agregue un controlador para el mensaje WM_CTLCOLOR reflejado a la clase CYellowEdit. Tenga en cuenta que el signo igual delante del nombre de mensaje en la lista de mensajes que puede controlar indica que el mensaje está reflejado. Se describe en Definición de un controlador de mensajes para un mensaje reflejado.

    ClassWizard agrega automáticamente la siguiente macro de mapa de mensajes y la función esqueleto:

    ON_WM_CTLCOLOR_REFLECT()
    // Note: other code will be in between....
    
    HBRUSH CYellowEdit::CtlColor(CDC* pDC, UINT nCtlColor)
    {
        // TODO: Change any attributes of the DC here
        // TODO: Return a non-NULL brush if the
        //       parent's handler should not be called
        return NULL;
    }
    
  6. Reemplace el cuerpo de la función por el código siguiente. El código especifica el color del texto, el color de fondo del texto y el color de fondo para el resto del control.

    pDC->SetTextColor(m_clrText);   // text
    pDC->SetBkColor(m_clrBkgnd);    // text bkgnd
    return m_brBkgnd;               // ctl bkgnd
    
  7. Cree un control de edición en el cuadro de diálogo y, a continuación, adjúntelo a una variable miembro haciendo doble clic en el control de edición mientras mantiene presionada una tecla de control. En el cuadro de diálogo Agregar variable miembro, finalice el nombre de la variable y elija "Control" para la categoría y, a continuación, "CYellowEdit" para el tipo de variable. No olvide establecer el orden de tabulación en el cuadro de diálogo. Además, asegúrese de incluir el archivo de encabezado del control CYellowEdit en el archivo de encabezado del cuadro de diálogo.

  8. Compile y ejecute su aplicación. El control de edición tendrá un fondo amarillo.

Consulte también

Notas técnicas por número
Notas técnicas por categoría