How to: Create a Message Map for a Template (Clase)
Mediante la asignación de mensajes de MFC, los mensajes de Windows se dirigen a una instancia de objeto C++ adecuada de forma eficiente. Entre los ejemplos de destino de la asignación de mensajes de MFC se incluyen clases de aplicación, clases de vista y documento, clases de control, etc.
Las asignaciones de mensajes de MFC tradicionales se declaran mediante la macro BEGIN_MESSAGE_MAP para declarar el inicio de la asignación, una entrada de macro para cada método de clase de controlador de mensajes y, por último, la macro END_MESSAGE_MAP para declarar el final de la asignación de mensajes.
Se produce una limitación con la macro BEGIN_MESSAGE_MAP al usarse con una clase que contiene argumentos de plantilla. Al usarse con una clase de plantilla, se producirá un error en esta macro en tiempo de compilación debido a los parámetros de plantilla que falten durante la expansión de macros. La macro BEGIN_TEMPLATE_MESSAGE_MAP está pensada para permitir que las clases que contienen un único argumento de plantilla declaren sus propias asignaciones de mensajes.
Ejemplo
Considere un ejemplo en el que se extiende la clase CListBox de MFC para hacer posible la sincronización con un origen de datos externo. La clase CSyncListBox
ficticia se declara de la forma siguiente:
// 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
};
La clase CSyncListBox
se base en un único tipo que describe el origen de datos con el que se sincronizará. También declara tres métodos que intervendrán en la asignación de mensajes de la clase: OnPaint
, OnDestroy
y OnSynchronize
. El método OnSynchronize
se implementa del siguiente modo:
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;
}
Mediante la implementación anterior, la clase CSyncListBox
puede especializarse en cualquier tipo de clase que implemente el método GetCount
, como CArray
, CList
y CMap
. La función StringizeElement
es una función de plantilla prototipo de la siguiente:
// Template function for converting an element within a collection
// to a CString object
template<typename CollectionT>
CString StringizeElement(CollectionT* pCollection, INT iIndex);
La asignación de mensajes de esta clase suele definirse:
BEGIN_MESSAGE_MAP(CSyncListBox, CListBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()
donde LBN_SYNCHRONIZE es un mensaje de usuario personalizado definido por la aplicación, como:
#define LBN_SYNCHRONIZE (WM_USER + 1)
La asignación de macros anterior no se compilará porque faltará la especificación de plantilla de la clase CSyncListBox
durante la expansión de macros. Esto se resuelve cuando la macro BEGIN_TEMPLATE_MESSAGE_MAP incorpora el parámetro de plantilla especificado en la asignación de macros expandida. La asignación de mensajes de esta clase se convierte en:
BEGIN_TEMPLATE_MESSAGE_MAP(CSyncListBox, CollectionT, CListBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()
A continuación se muestra el uso de ejemplo de la clase CSyncListBox
mediante un 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 completar la prueba, la función StringizeElement
debe especializarse en trabajar con la clase 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
}