Como criar um mapa de mensagem para uma classe de modelo
O mapeamento de mensagem em MFC fornece uma maneira eficiente de direcionar mensagens do windows a uma instância adequada do objeto C++. Os exemplos de destinos da mensagem MFC incluem classes do aplicativo, documento e classes de exibição, classes de controle, e assim por diante.
Os mapas de mensagens tradicionais MFC são declarados usando a macro de BEGIN_MESSAGE_MAP para declarar o início da mensagem de entrada, uma macro para cada método da classe retornadas a, e a macro de END_MESSAGE_MAP para declarar finalmente ao final da mensagem.
Uma restrição com a macro de BEGIN_MESSAGE_MAP ocorre quando é usado junto com uma classe que contém argumentos de modelo. Quando usado com uma classe do modelo, esta macro causará um erro de tempo de compilação devido aos parâmetros faltantes do modelo durante a expansão macro. A macro de BEGIN_TEMPLATE_MESSAGE_MAP foi projetado para permitir classes que contêm um único argumento do modelo para declarar seus próprios mapas da mensagem.
Exemplo
Considere um exemplo na classe de MFC CListBox foi estendido para fornecer a sincronização com uma fonte de dados externa. A classe de CSyncListBox fictícia é declarada como segue:
// 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 de CSyncListBox templated em um único tipo que descreve a fonte de dados que sincronizará com. Também declara três métodos que participarão da mensagem da classe: OnPaint, OnDestroy, e OnSynchronize. O método de OnSynchronize é implementado como segue:
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 de CSyncListBox é especializada em qualquer tipo de classe que implemente o método de GetCount , como CArray, CListe, em CMap. A função de StringizeElement é uma função do modelo 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 mensagem 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()
onde LBN_SYNCHRONIZE é uma mensagem de usuário personalizado definido pelo aplicativo, como:
#define LBN_SYNCHRONIZE (WM_USER + 1)
O mapa macro acima não criará, devido ao fato de que a especificação do modelo para a classe de CSyncListBox estarão ausentes durante a expansão macro. A macro de BEGIN_TEMPLATE_MESSAGE_MAP resolve esse inserindo o parâmetro especificado do modelo no mapa macro expandido. O mapa de mensagem para essa classe é:
BEGIN_TEMPLATE_MESSAGE_MAP(CSyncListBox, CollectionT, CListBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()
O seguinte demonstra o uso de exemplo da classe de CSyncListBox que usa um objeto de 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 de StringizeElement deve ser especializada para trabalhar com a classe de CStringList :
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
}