Comment : créer une table des messages pour une classe de modèle

La création d'une table des messages dans MFC est un moyen efficace pour diriger les messages Windows vers une instance d'objet appropriée C++. Les exemples de cibles de table des messages MFC comprennent des classes d'application, des classes de document et de vue, des classes de contrôle, etc.

Les mappages de messages MFC traditionnels sont déclarés à l’aide de la macro BEGIN_MESSAGE_MAP pour déclarer le début du mappage de messages, une entrée de macro pour chaque méthode de classe de gestionnaire de messages, et enfin la macro END_MESSAGE_MAP pour déclarer la fin du mappage de messages.

Une limitation avec la macro BEGIN_MESSAGE_MAP se produit lorsqu’elle est utilisée conjointement avec une classe contenant des arguments de modèle. Si elle est utilisée avec une classe de modèle, la macro entraînera une erreur de compilation en raison des paramètres de modèle manquants pendant l'expansion macro. La macro BEGIN_TEMPLATE_MESSAGE_MAP a été conçue pour permettre aux classes contenant un seul argument de modèle de déclarer leurs propres mappages de messages.

Exemple

Prenons un exemple où la classe CListBox MFC est étendue pour fournir une synchronisation avec une source de données externe. La classe fictive CSyncListBox est déclarée comme suit :

// 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 classe est modèleée sur un type unique qui décrit la source de données avec laquelle elle sera synchronisée. Il déclare également trois méthodes qui participeront à la carte des messages de la classe : OnPaint, OnDestroyet OnSynchronize. La OnSynchronize méthode est implémentée comme suit :

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

L’implémentation ci-dessus permet à la CSyncListBox classe d’être spécialisée sur n’importe quel type de classe qui implémente la GetCount méthode, telle que CArray, CListet CMap. La StringizeElement fonction est une fonction de modèle prototype par les éléments suivants :

// Template function for converting an element within a collection
// to a CString object
template<typename CollectionT>
CString StringizeElement(CollectionT* pCollection, INT iIndex);

Normalement, la table des messages pour cette classe est définie comme suit :

BEGIN_MESSAGE_MAP(CSyncListBox, CListBox)
  ON_WM_PAINT()
  ON_WM_DESTROY()
  ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()

LBN_SYNCHRONIZE est un message utilisateur personnalisé défini par l’application, par exemple :

#define LBN_SYNCHRONIZE (WM_USER + 1)

Le mappage de macros ci-dessus ne sera pas compilé, en raison du fait que la spécification du modèle pour la classe sera manquante pendant l’expansion CSyncListBox de macro. La macro BEGIN_TEMPLATE_MESSAGE_MAP résout ce problème en incorporant le paramètre de modèle spécifié dans la carte de macros développée. La table des messages pour cette classe est :

BEGIN_TEMPLATE_MESSAGE_MAP(CSyncListBox, CollectionT, CListBox)
   ON_WM_PAINT()
   ON_WM_DESTROY()
   ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
   END_MESSAGE_MAP()

L’exemple d’utilisation de la classe à l’aide CSyncListBox d’un CStringList objet est le suivant :

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

Pour effectuer le test, la StringizeElement fonction doit être spécialisée pour travailler avec la 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
}

Voir aussi

BEGIN_TEMPLATE_MESSAGE_MAP
Gestion et mappage des messages