Partilhar via


TN061: mensagens ON_NOTIFY e WM_NOTIFY

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 apresenta informações em segundo plano sobre a nova mensagem WM_NOTIFY e descreve a maneira recomendada (e mais comum) de lidar com mensagens WM_NOTIFY em seu aplicativo MFC.

Mensagens de notificação no Windows 3.x

No Windows 3.x, os controles notificam seus pais sobre eventos como cliques do mouse, alterações no conteúdo e na seleção e controlam a pintura em segundo plano enviando uma mensagem para o pai. As notificações simples são enviadas como mensagens de WM_COMMAND especiais, com o código de notificação (como BN_CLICKED) e a ID de controle empacotada no wParam e no identificador do controle em lParam. Observe que, como wParam e lParam estão cheios, não há como passar dados adicionais. Essas mensagens podem ser apenas uma notificação simples. Por exemplo, na notificação BN_CLICKED, não há como enviar informações sobre o local do cursor do mouse quando o usuário clicou no botão.

Quando os controles no Windows 3.x precisam enviar uma mensagem de notificação que inclua dados adicionais, eles usam uma variedade de mensagens de finalidade especial, incluindo WM_CTLCOLOR, WM_VSCROLL, WM_HSCROLL, WM_DRAWITEM, WM_MEASUREITEM, WM_COMPAREITEM, WM_DELETEITEM, WM_CHARTOITEM, WM_VKEYTOITEM etc. Essas mensagens podem ser refletidas novamente para o controle que as enviou. Para mais informações, confira TN062: Reflexão de mensagens para controles do Windows.

Mensagens de notificação no Win32

Para controles que existiam no Windows 3.1, a API Win32 usa a maioria das mensagens de notificação usadas no Windows 3.x. No entanto, o Win32 também adiciona uma série de controles sofisticados e complexos aos compatíveis com o Windows 3.x. Frequentemente, esses controles precisam enviar dados adicionais com suas mensagens de notificação. Em vez de adicionar uma mensagem WM_* para cada nova notificação que precisa de dados adicionais, os designers da API Win32 optaram por adicionar apenas uma mensagem, WM_NOTIFY, que pode passar qualquer quantidade de dados adicionais de maneira padronizada.

Mensagens WM_NOTIFY contêm a ID do controle que envia a mensagem no wParam e um ponteiro para uma estrutura no lParam. Essa estrutura é uma estrutura NMHDR ou uma estrutura maior que tem uma estrutura NMHDR como seu primeiro membro. Observe que, como o membro NMHDR é o primeiro, um ponteiro para essa estrutura pode ser usado como um ponteiro para um NMHDR ou como um ponteiro para a estrutura maior, dependendo de como você a converte.

Na maioria dos casos, o ponteiro apontará para uma estrutura maior e você precisará convertê-la quando a for usar. Em apenas algumas notificações, como as notificações comuns (cujos nomes começam com NM_) e as notificações TTN_SHOW e TTN_POP do controle de dica de ferramenta, uma estrutura NMHDR realmente é usada.

A estrutura NMHDR ou membro inicial contém o identificador e a ID do controle que envia a mensagem e o código de notificação (como TTN_SHOW). O formato da estrutura NMHDR é mostrado abaixo:

typedef struct tagNMHDR {
    HWND hwndFrom;
    UINT idFrom;
    UINT code;
} NMHDR;

Para uma mensagem TTN_SHOW, o membro code seria definido como TTN_SHOW.

A maioria das notificações passa um ponteiro para uma estrutura maior que contém uma estrutura NMHDR como primeiro membro. Por exemplo, considere a estrutura usada pela mensagem de notificação LVN_KEYDOWN do controle de exibição de lista, que é enviada quando uma tecla é pressionada em um controle de exibição de lista. O ponteiro aponta para uma estrutura LV_KEYDOWN, que é definida conforme mostrado abaixo:

typedef struct tagLV_KEYDOWN {
    NMHDR hdr;
    WORD wVKey;
    UINT flags;
} LV_KEYDOWN;

Observe que, como o membro NMHDR é o primeiro nessa estrutura, o ponteiro passado na mensagem de notificação pode ser convertido em um ponteiro para um NMHDR ou um ponteiro para um LV_KEYDOWN.

Notificações comuns a todos os novos controles do Windows

Algumas notificações são comuns a todos os novos controles do Windows. Essas notificações passam um ponteiro para uma estrutura NMHDR.

Código de notificação Enviado porque
NM_CLICK O usuário clicou no botão esquerdo do mouse no controle
NM_DBLCLK O usuário clicou duas vezes no botão esquerdo do mouse no usuário no controle
NM_RCLICK O usuário clicou no botão direito do mouse no controle
NM_RDBLCLK O usuário clicou duas vezes no botão direito do mouse no controle
NM_RETURN O usuário pressionou a tecla ENTER enquanto o controle tinha o foco de entrada
NM_SETFOCUS O controle recebeu o foco de entrada
NM_KILLFOCUS O controle perdeu o foco de entrada
NM_OUTOFMEMORY O controle não pôde concluir uma operação porque não havia memória suficiente disponível

ON_NOTIFY: Como manipular mensagens WM_NOTIFY em aplicativos MFC

A função CWnd::OnNotify lida com mensagens de notificação. Sua implementação padrão verifica o mapa de mensagens para os manipuladores de notificação chamarem. Em geral, você não substitui OnNotify. Em vez disso, você fornece uma função de manipulador e adiciona uma entrada de mapa de mensagens para esse manipulador ao mapa de mensagens da classe da janela do proprietário.

O ClassWizard, por meio da folha de propriedades ClassWizard, pode criar a entrada de mapa de mensagens ON_NOTIFY e fornecer uma função de manipulador de esqueleto. Para mais informações sobre como usar o ClassWizard para facilitar isso, confira Como mapear mensagens para funções.

A macro do mapa de mensagens ON_NOTIFY tem a seguinte sintaxe:

ON_NOTIFY(wNotifyCode, id, memberFxn)

em que os parâmetros são:

wNotifyCode
O código para a mensagem de notificação a ser tratada, como LVN_KEYDOWN.

id
O identificador filho do controle para o qual a notificação é enviada.

memberFxn
A função de membro a ser chamada quando essa notificação é enviada.

Sua função de membro deve ser declarada com o seguinte protótipo:

afx_msg void memberFxn(NMHDR* pNotifyStruct, LRESULT* result);

em que os parâmetros são:

pNotifyStruct
Um ponteiro para a estrutura de notificação, conforme descrito na seção acima.

result
Um ponteiro para o código de resultado que você definirá antes de retornar.

Exemplo

Para especificar que você deseja que a função de membro OnKeydownList1 manipule mensagens LVN_KEYDOWN de CListCtrl cuja ID é IDC_LIST1, você usaria ClassWizard para adicionar o seguinte ao mapa de mensagens:

ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, OnKeydownList1)

No exemplo acima, a função fornecida por ClassWizard é:

void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;

    // TODO: Add your control notification handler
    //       code here

    *pResult = 0;
}

Observe que ClassWizard fornece um ponteiro do tipo apropriado automaticamente. Você pode acessar a estrutura de notificação por meio de pNMHDR ou pLVKeyDow.

ON_NOTIFY_RANGE

Se você precisar processar a mesma mensagem WM_NOTIFY para um conjunto de controles, poderá usar ON_NOTIFY_RANGE em vez de ON_NOTIFY. Por exemplo, você pode ter um conjunto de botões para os quais deseja executar a mesma ação para uma determinada mensagem de notificação.

Ao usar ON_NOTIFY_RANGE, especifique um intervalo contíguo de identificadores filho para os quais manipular a mensagem de notificação especificando os identificadores filho iniciais e finais do intervalo.

ClassWizard não manipula ON_NOTIFY_RANGE; para usá-lo, você precisa editar seu mapa de mensagens por conta própria.

A entrada do mapa de mensagens e o protótipo de função para ON_NOTIFY_RANGE são os seguintes:

ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn)

em que os parâmetros são:

wNotifyCode
O código para a mensagem de notificação a ser tratada, como LVN_KEYDOWN.

id
O primeiro identificador no intervalo contíguo de identificadores.

idLast
O último identificador no intervalo contíguo de identificadores.

memberFxn
A função de membro a ser chamada quando essa notificação é enviada.

Sua função de membro deve ser declarada com o seguinte protótipo:

afx_msg void memberFxn(UINT id, NMHDR* pNotifyStruct, LRESULT* result);

em que os parâmetros são:

id
O identificador filho do controle que enviou a notificação.

pNotifyStruct
Um ponteiro para a estrutura de notificação, conforme descrito acima.

result
Um ponteiro para o código de resultado que você definirá antes de retornar.

ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE

Se quiser que mais de um objeto no roteamento de notificação manipule uma mensagem, use ON_NOTIFY_EX (ou ON_NOTIFY_EX_RANGE) em vez de ON_NOTIFY (ou ON_NOTIFY_RANGE). A única diferença entre a versão EX e a versão regular é que a função de membro chamada para a versão EX retorna um BOOL que indica se o processamento de mensagens deve ou não continuar. Retornar FALSE dessa função permite que você processe a mesma mensagem em mais de um objeto.

O ClassWizard não manipula ON_NOTIFY_EX ou ON_NOTIFY_EX_RANGE; se você quiser usar um deles, precisará seu editar o mapa de mensagens por conta própria.

A entrada do mapa de mensagens e o protótipo de função para ON_NOTIFY_EX e ON_NOTIFY_EX_RANGE são apresentados a seguir. Os significados dos parâmetros são os mesmos das versões não EX.

ON_NOTIFY_EX(nCode, id, memberFxn)
ON_NOTIFY_EX_RANGE(wNotifyCode, id, idLast, memberFxn)

O protótipo para ambas as opções acima é o mesmo:

afx_msg BOOL memberFxn(UINT id, NMHDR* pNotifyStruct, LRESULT* result);

Em ambos os casos, a ID contém o identificador filho do controle que enviou a notificação.

Sua função deverá retornar TRUE se a mensagem de notificação tiver sido completamente manipulada ou FALSE se outros objetos no roteamento de comando tiverem a chance de lidar com a mensagem.

Confira também

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