Como criar um mapa de mensagem para uma classe de modelo
O mapeamento de mensagens no MFC fornece uma maneira eficiente de direcionar mensagens do Windows para uma instância de objeto C++ apropriada. Exemplos de destinos de mapa de mensagens do MFC incluem classes de aplicativo, classes de documentação e exibição, classes de controle e assim por diante.
Mapas de mensagens do MFC tradicionais são declarados usando a macro BEGIN_MESSAGE_MAP para declarar o início do mapa de mensagens, uma entrada de macro para cada método de classe de manipulador de mensagens e, por fim, a macro END_MESSAGE_MAP para declarar o final do mapa de mensagens.
Uma limitação da macro BEGIN_MESSAGE_MAP ocorre quando ela é usada em conjunto com uma classe que contém argumentos de modelo. Quando usada com uma classe de modelo, essa macro causará um erro em tempo de compilação devido à ausência dos parâmetros de modelo durante a expansão da macro. A macro BEGIN_TEMPLATE_MESSAGE_MAP foi projetada para permitir que classes que contêm um único argumento de modelo declarem seus próprios mapas de mensagens.
Exemplo
Considere um exemplo em que a classe CListBox MFC é estendida para fornecer sincronização com uma fonte de dados externa. A classe CSyncListBox
fictícia é declarada da seguinte forma:
// Extends the CListBox class to provide synchronization with
// an external data source
template <typename CollectionT>
class CSyncListBox : public CListBox
{
public:
CSyncListBox();
virtual ~CSyncListBox();
afx_msg void OnPaint();
afx_msg void OnDestroy();
afx_msg LRESULT OnSynchronize(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
// ...additional functionality as needed
};
A classe CSyncListBox
é feita modelo em um único tipo que descreve a fonte de dados com a qual será sincronizada. Ela também declara três métodos que participarão do mapa de mensagens da classe: OnPaint
, OnDestroy
e OnSynchronize
. O método OnSynchronize
é implementado da seguinte maneira:
template <class CollectionT>
LRESULT CSyncListBox<CollectionT>::OnSynchronize(WPARAM, LPARAM lParam)
{
CollectionT* pCollection = (CollectionT*)(lParam);
ResetContent();
if (pCollection != NULL)
{
INT nCount = (INT)pCollection->GetCount();
for (INT n = 0; n < nCount; n++)
{
CString s = StringizeElement(pCollection, n);
AddString(s);
}
}
return 0L;
}
A implementação acima permite que a classe CSyncListBox
seja especializada em qualquer tipo de classe que implemente o método GetCount
, como CArray
, CList
e CMap
. A função StringizeElement
é uma função de modelo tornada protótipo pelo seguinte:
// Template function for converting an element within a collection
// to a CString object
template<typename CollectionT>
CString StringizeElement(CollectionT* pCollection, INT iIndex);
Normalmente, o mapa de mensagens para essa classe seria definido como:
BEGIN_MESSAGE_MAP(CSyncListBox, CListBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()
em que LBN_SYNCHRONIZE é uma mensagem de usuário personalizada definida pelo aplicativo, como:
#define LBN_SYNCHRONIZE (WM_USER + 1)
O mapa de macro acima não será compilado, visto que a especificação do modelo para a classe CSyncListBox
estará ausente durante a expansão da macro. A macro BEGIN_TEMPLATE_MESSAGE_MAP resolve isso incorporando o parâmetro de modelo especificado no mapa de macro expandido. O mapa de mensagens dessa classe se torna:
BEGIN_TEMPLATE_MESSAGE_MAP(CSyncListBox, CollectionT, CListBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()
Vejamos a seguir o uso de exemplo da classe CSyncListBox
usando um objeto CStringList
:
void CSyncListBox_Test(CWnd* pParentWnd)
{
CSyncListBox<CStringList> ctlStringLB;
ctlStringLB.Create(WS_CHILD | WS_VISIBLE | LBS_STANDARD | WS_HSCROLL,
CRect(10, 10, 200, 200), pParentWnd, IDC_MYSYNCLISTBOX);
// Create a CStringList object and add a few strings
CStringList stringList;
stringList.AddTail(_T("A"));
stringList.AddTail(_T("B"));
stringList.AddTail(_T("C"));
// Send a message to the list box control to synchronize its
// contents with the string list
ctlStringLB.SendMessage(LBN_SYNCHRONIZE, 0, (LPARAM)& stringList);
// Verify the contents of the list box by printing out its contents
INT nCount = ctlStringLB.GetCount();
for (INT n = 0; n < nCount; n++)
{
TCHAR szText[256];
ctlStringLB.GetText(n, szText);
TRACE(_T("%s\n"), szText);
}
}
Para concluir o teste, a função StringizeElement
deve ser especializada para trabalhar com a CStringList
classe :
template<>
CString StringizeElement(CStringList* pStringList, INT iIndex)
{
if (pStringList != NULL && iIndex < pStringList->GetCount())
{
POSITION pos = pStringList->GetHeadPosition();
for (INT i = 0; i < iIndex; i++)
{
pStringList->GetNext(pos);
}
return pStringList->GetAt(pos);
}
return CString(); // or throw, depending on application requirements
}
Confira também
BEGIN_TEMPLATE_MESSAGE_MAP
Tratamento e mapeamento de mensagem