Partilhar via


TN062: reflexão de mensagem para controles do Windows

Observação

A nota técnica a seguir não foi atualizada desde que foi incluída pela primeira vez na documentação online. Como resultado, alguns procedimentos e tópicos podem estar desatualizados ou incorretos. Para obter as informações mais recentes, é recomendável que você pesquise o tópico de interesse no índice de documentação online.

Esta nota técnica descreve a reflexão de mensagem, um novo recurso do MFC 4.0. Ela também contém instruções para criar um controle reutilizável simples que usa reflexão de mensagem.

Esta nota técnica não discute a reflexão de mensagem no contexto dos controles ActiveX (chamados anteriormente de controles OLE). Consulte o artigo Controles ActiveX: subclasses de um controle do Windows.

O que é a reflexão de mensagem

Frequentemente, os controles do Windows enviam mensagens de notificação para suas janelas pai. Por exemplo, muitos controles enviam uma mensagem de notificação de cor do controle (WM_CTLCOLOR ou uma de suas variantes) para os pais para permitir que o pai forneça um pincel para pintar a tela de fundo do controle.

No Windows e no MFC antes da versão 4.0, a janela pai, geralmente uma caixa de diálogo, é responsável por manipular essas mensagens. Isso significa que o código para manipular a mensagem precisa estar na classe da janela pai e que precisa ser duplicado em todas as classes que precisam manipular a mensagem. No caso acima, todas as caixas de diálogo que quisessem controles com telas de fundo personalizadas precisariam manipular a mensagem de notificação de cor do controle. Seria muito mais fácil reutilizar o código se fosse possível escrever uma classe de controle que manipulasse a própria cor da tela de fundo.

No MFC 4.0, o mecanismo antigo ainda funciona – as janelas pai podem manipular as mensagens de notificação. Além disso, o MFC 4.0 facilita a reutilização fornecendo um recurso chamado "reflexão de mensagem", que permite que essas mensagens de notificação sejam manipuladas na janela do controle filho, na janela pai ou em ambos. No exemplo de cor da tela de fundo do controle, agora você pode escrever uma classe de controle que define a própria cor da tela de fundo manipulando a mensagem WM_CTLCOLOR refletida, tudo sem depender do pai. (Observe que, como a reflexão de mensagem é implementada pelo MFC, e não pelo Windows, a classe da janela pai deve ser derivada de CWnd para que a reflexão de mensagem funcione.)

Versões mais antigas do MFC faziam algo semelhante à reflexão de mensagem fornecendo funções virtuais para algumas mensagens, como mensagens para caixas de listagem desenhadas pelo proprietário (WM_DRAWITEM e assim por diante). O novo mecanismo de reflexão de mensagem é generalizado e consistente.

A reflexão de mensagem é compatível com código escrito para versões do MFC anteriores à 4.0.

Se você forneceu um manipulador para uma mensagem específica ou para um intervalo de mensagens, na classe da janela pai, ele substituirá os manipuladores de mensagens refletidas pela mesma mensagem se você não chamar a função de manipulador da classe base em seu manipulador. Por exemplo, se você manipular WM_CTLCOLOR em sua classe de caixa de diálogo, sua manipulação substituirá todos os manipuladores de mensagens refletidos.

Se, em sua classe de janela pai, você fornecer um manipulador para uma mensagem WM_NOTIFY específica ou um intervalo de mensagens WM_NOTIFY, o manipulador será chamado somente se o controle filho que envia essas mensagens não tiver um manipulador de mensagens refletido por meio de ON_NOTIFY_REFLECT(). Se você usar ON_NOTIFY_REFLECT_EX() no mapa de mensagens, o manipulador de mensagens poderá ou não permitir que a janela pai manipule a mensagem. Se o manipulador retornar FALSE, a mensagem também será manipulada pelo pai, enquanto uma chamada que retorna TRUE não permite que o pai a manipule. Observe que a mensagem refletida é manipulada antes da mensagem de notificação.

Quando uma mensagem WM_NOTIFY é enviada, o controle tem a primeira chance de manipulá-la. Se qualquer outra mensagem refletida for enviada, a janela pai terá a primeira chance de manipulá-la e o controle receberá a mensagem refletida. Para fazer isso, ela precisará de uma função de manipulador e uma entrada apropriada no mapa de mensagens de classe do controle.

A macro de mapa de mensagens para mensagens refletidas é ligeiramente diferente da que é usada para notificações regulares: ela tem _REFLECT acrescentada ao seu nome habitual. Por exemplo, para manipular uma mensagem WM_NOTIFY no pai, use a macro ON_NOTIFY no mapa de mensagens do pai. Para manipular a mensagem refletida no controle filho, use a macro ON_NOTIFY_REFLECT no mapa de mensagens do controle filho. Em alguns casos, os parâmetros também são diferentes. Observe que ClassWizard geralmente pode adicionar as entradas de mapa de mensagens para você e fornecer implementações de função esqueleto com os parâmetros corretos.

Consulte TN061: mensagens ON_NOTIFY e WM_NOTIFY para obter informações sobre a nova mensagem WM_NOTIFY.

Entradas de mapa de mensagens e protótipos de função de manipulador para mensagens refletidas

Para manipular uma mensagem de notificação de controle refletida, use as macros de mapa de mensagens e os protótipos de função listados na tabela abaixo.

Geralmente, ClassWizard pode adicionar essas entradas de mapa de mensagens para você e fornecer implementações de função esqueleto. Consulte Definindo um manipulador de mensagens para uma mensagem refletida para obter informações sobre como definir manipuladores para mensagens refletidas.

Para converter do nome da mensagem para o nome da macro refletida, preceda com ON_ e acrescente _REFLECT. Por exemplo, WM_CTLCOLOR se torna ON_WM_CTLCOLOR_REFLECT. (Para ver quais mensagens podem ser refletidas, faça a conversão oposta nas entradas de macro na tabela a seguir.)

Estas são as três exceções à regra acima:

  • A macro para notificações de WM_COMMAND é ON_CONTROL_REFLECT.

  • A macro para reflexões de WM_NOTIFY é ON_NOTIFY_REFLECT.

  • A macro para reflexões de ON_UPDATE_COMMAND_UI é ON_UPDATE_COMMAND_UI_REFLECT.

Em cada um dos casos especiais acima, você precisa especificar o nome da função membro do manipulador. Nos outros casos, você precisa usar o nome padrão da sua função de manipulador.

Os significados dos parâmetros e os valores retornados das funções são documentados no nome da função ou no nome da função precedido por On. Por exemplo, CtlColor está documentado em OnCtlColor. Vários manipuladores de mensagens refletidas precisam de menos parâmetros do que os manipuladores semelhantes em uma janela pai. Basta corresponder os nomes na tabela abaixo com os nomes dos parâmetros formais na documentação.

Entrada de mapa Protótipo da função
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);

As macros ON_NOTIFY_REFLECT e ON_CONTROL_REFLECT têm variações que permitem que mais de um objeto (como o controle e seu pai) manipule uma determinada mensagem.

Entrada de mapa Protótipo da função
ON_NOTIFY_REFLECT_EX(wNotifyCode,memberFxn) afx_msg BOOLmemberFxn( NMHDR*pNotifyStruct, LRESULT*result);
ON_CONTROL_REFLECT_EX(wNotifyCode,memberFxn) afx_msg BOOLmemberFxn( );

Manipulando mensagens refletidas: um exemplo de controle reutilizável

Este exemplo simples cria um controle reutilizável chamado CYellowEdit. O controle funciona da mesma forma que um controle de edição comum, exceto pelo fato de que exibe texto preto em uma tela de fundo amarela. Seria fácil adicionar funções membro que permitiriam que o controle CYellowEdit exibisse cores diferentes.

Para experimentar o exemplo que cria um controle reutilizável

  1. Crie uma caixa de diálogo em um aplicativo existente. Para obter mais informações, consulte o tópico sobre o editor de caixa de diálogo.

    Você precisa ter um aplicativo no qual desenvolver o controle reutilizável. Se não tiver um aplicativo existente para usar, crie um baseado em caixa de diálogo usando o AppWizard.

  2. Com o projeto carregado no Visual C++, use ClassWizard para criar uma classe chamada CYellowEdit baseada em CEdit.

  3. Adicione três variáveis de membro à classe CYellowEdit. As duas primeiras serão variáveis COLORREF para conter a cor do texto e a cor da tela de fundo. A terceira será um objeto CBrush que conterá o pincel para pintar a tela de fundo. O objeto CBrush permite que você crie o pincel uma vez, apenas referenciando-o depois disso, e destrua o pincel automaticamente quando o controle CYellowEdit é destruído.

  4. Inicialize as variáveis de membro escrevendo o construtor da seguinte maneira:

    CYellowEdit::CYellowEdit()
    {
        m_clrText = RGB(0, 0, 0);
        m_clrBkgnd = RGB(255, 255, 0);
        m_brBkgnd.CreateSolidBrush(m_clrBkgnd);
    }
    
  5. Usando ClassWizard, adicione um manipulador para a mensagem WM_CTLCOLOR refletida à sua classe CYellowEdit. Observe que o sinal de igual na frente do nome da mensagem na lista de mensagens que você pode manipular indica que a mensagem é refletida. Isso é descrito em Definindo um manipulador de mensagens para uma mensagem refletida.

    ClassWizard adiciona a seguinte macro de mapa de mensagens e função esqueleto para você:

    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. Substitua o corpo da função pelo código a seguir. O código especifica a cor do texto, a cor da tela de fundo do texto e a cor da tela de fundo para o restante do controle.

    pDC->SetTextColor(m_clrText);   // text
    pDC->SetBkColor(m_clrBkgnd);    // text bkgnd
    return m_brBkgnd;               // ctl bkgnd
    
  7. Crie um controle de edição na caixa de diálogo e anexe-o a uma variável de membro clicando duas vezes no controle de edição enquanto mantém uma tecla de controle pressionada. Na caixa de diálogo Adicionar Variável de Membro, preencha o nome da variável e escolha "Controle" como a categoria e "CYellowEdit" como o tipo de variável. Não se esqueça de definir a ordem de tabulação na caixa de diálogo. Além disso, inclua o arquivo de cabeçalho para o controle CYellowEdit no arquivo de cabeçalho da caixa de diálogo.

  8. Compile e execute seu aplicativo. O controle de edição terá uma tela de fundo amarela.

Confira também

Observações técnicas por número
Observações técnicas por categoria