Compartir a través de


Cómo: Crear un mapa de mensajes para una clase de plantilla

La asignación de mensajes en MFC proporciona una manera eficaz de dirigir los mensajes de Windows a una instancia de objeto de C++ adecuada. Algunos ejemplos de destinos de mapa de mensajes de MFC incluyen clases de aplicación, clases de documento y vista, clases de control, etc.

Los mapas de mensajes MFC tradicionales se declaran mediante la macro BEGIN_MESSAGE_MAP para declarar el inicio del mapa de mensajes, 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 del mapa de mensajes.

Una limitación con la macro BEGIN_MESSAGE_MAP se produce cuando se usa junto con una clase que contiene argumentos de plantilla. Cuando se usa con una clase de plantilla, esta macro provocará un error en tiempo de compilación debido a los parámetros de plantilla que faltan durante la expansión de macros. La macro BEGIN_TEMPLATE_MESSAGE_MAP se diseñó para permitir que las clases que contienen un único argumento de plantilla declaren sus propios mapas de mensajes.

Ejemplo

Considere un ejemplo en el que se extiende la clase CListBox de MFC para proporcionar sincronización con un origen de datos externo. La clase ficticia CSyncListBox se declara de la siguiente manera:

// 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 CSyncListBox clase se crea con plantilla en un único tipo con el que se describe el origen de datos con el que se sincronizará. También declara tres métodos que participarán en el mapa de mensajes de la clase: OnPaint, OnDestroyy OnSynchronize. El OnSynchronize método se implementa de la manera siguiente:

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;
}

La implementación anterior permite que la CSyncListBox clase se especializa en cualquier tipo de clase que implemente el GetCount método, como CArray, CListy CMap. La StringizeElement función 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);

Normalmente, el mapa de mensajes para esta clase se definiría como:

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)

El mapa de macros anterior no se compilará, debido al hecho de que faltará la especificación de plantilla de la clase durante la CSyncListBox expansión de macros. La macro BEGIN_TEMPLATE_MESSAGE_MAP resuelve esto incorporando el parámetro de plantilla especificado en la asignación de macros expandida. El mapa 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 CSyncListBox clase mediante un CStringList objeto :

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 StringizeElement función debe estar especializada para trabajar con la CStringList clase :

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
}

Consulte también

BEGIN_TEMPLATE_MESSAGE_MAP
Control y mapeo de mensajes