Gewusst wie: Erstellen einer Meldungszuordnung für eine Vorlagenklasse

Die Nachrichtenzuordnung in MFC bietet eine effiziente Möglichkeit, Windows-Nachrichten an eine entsprechende C++-Objektinstanz zu leiten. Beispiele für MFC-Nachrichtenzuordnungsziele sind Anwendungsklassen, Dokument- und Ansichtsklassen, Steuerelementklassen usw.

Herkömmliche MFC-Nachrichtenzuordnungen werden mithilfe des BEGIN_MESSAGE_MAP Makros deklariert, um den Start der Nachrichtenzuordnung, einen Makroeintrag für jede Nachrichtenhandlerklassenmethode und schließlich das END_MESSAGE_MAP Makro zum Deklarieren des Endes der Nachrichtenzuordnung zu deklarieren.

Eine Einschränkung mit dem BEGIN_MESSAGE_MAP Makros tritt auf, wenn es in Verbindung mit einer Klasse verwendet wird, die Vorlagenargumente enthält. Bei Verwendung mit einer Vorlagenklasse verursacht dieses Makro aufgrund der fehlenden Vorlagenparameter während der Makroerweiterung einen Kompilierungsfehler. Das BEGIN_TEMPLATE_MESSAGE_MAP-Makro wurde so konzipiert, dass Klassen, die ein einzelnes Vorlagenargument enthalten, ihre eigenen Nachrichtenzuordnungen deklarieren können.

Beispiel

Betrachten Sie ein Beispiel, in dem die MFC CListBox-Klasse erweitert wird, um die Synchronisierung mit einer externen Datenquelle bereitzustellen. Die fiktive CSyncListBox Klasse wird wie folgt deklariert:

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

Die CSyncListBox Klasse wird auf einem einzelnen Typ vorlagen, der die Datenquelle beschreibt, mit der sie synchronisiert wird. Außerdem werden drei Methoden deklariert, die an der Nachrichtenzuordnung der Klasse teilnehmen: OnPaint, , OnDestroyund OnSynchronize. Die OnSynchronize Methode wird wie folgt implementiert:

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

Die obige Implementierung ermöglicht es der CSyncListBox Klasse, auf jeden Klassentyp spezialisiert zu sein, der die GetCount Methode implementiert, z CArray. B. , CListund CMap. Die StringizeElement Funktion ist eine Vorlagenfunktion, die wie folgt prototypiert ist:

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

Normalerweise würde die Nachrichtenzuordnung für diese Klasse wie folgt definiert:

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

dabei ist LBN_SYNCHRONIZE eine benutzerdefinierte Benutzernachricht, die von der Anwendung definiert wird, z. B.:

#define LBN_SYNCHRONIZE (WM_USER + 1)

Die oben genannte Makrozuordnung wird nicht kompiliert, da die Vorlagenspezifikation für die CSyncListBox Klasse während der Makroerweiterung fehlt. Das BEGIN_TEMPLATE_MESSAGE_MAP Makro löst dies durch Einbeziehung des angegebenen Vorlagenparameters in die erweiterte Makrozuordnung. Die Nachrichtenzuordnung für diese Klasse wird wie folgt:

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

Im folgenden Beispiel wird die Verwendung der CSyncListBox Klasse mithilfe eines CStringList Objekts veranschaulicht:

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

Um den Test abzuschließen, muss die StringizeElement Funktion spezialisiert sein, um mit der CStringList Klasse zu arbeiten:

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
}

Siehe auch

BEGIN_TEMPLATE_MESSAGE_MAP
Meldungsbehandlung und Zuordnung