Compartilhar via


Como criar um mapa de mensagens 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 MFC incluem classes de aplicativo, classes de documento e exibição, classes de controle e assim por diante.

Mapas de mensagens 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 fim do mapa de mensagens.

Uma limitação com a 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 de tempo de compilação devido aos parâmetros de modelo ausentes 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 do MFC é estendida para fornecer sincronização com uma fonte de dados externa. A classe fictícia CSyncListBox é declarada da seguinte maneira:

// 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 CSyncListBox classe é modelo em um único tipo que descreve a fonte de dados com a qual será sincronizada. Ele também declara três métodos que participarão do mapa de mensagens da classe: OnPaint, OnDestroye OnSynchronize. O OnSynchronize método é 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 CSyncListBox classe seja especializada em qualquer tipo de classe que implemente o GetCount método, como CArray, CListe CMap. A StringizeElement função é uma função de 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 mensagens dessa 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 macros acima não será compilado, devido ao fato de que a especificação do modelo para a CSyncListBox classe 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()

O seguinte demonstra o uso de exemplo da CSyncListBox classe usando um 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 concluir o teste, a StringizeElement função 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
}

Consulte também

BEGIN_TEMPLATE_MESSAGE_MAP
Tratamento e mapeamento de mensagens