Dela via


Anvisningar: Skapa en meddelandekarta för en mallklass

Meddelandemappning i MFC är ett effektivt sätt att dirigera Windows-meddelanden till en lämplig C++-objektinstans. Exempel på MFC-mål för meddelandekarta är programklasser, dokument- och visningsklasser, kontrollklasser och så vidare.

Traditionella MFC-meddelandekartor deklareras med hjälp av det BEGIN_MESSAGE_MAP makrot för att deklarera början av meddelandekartan, en makropost för varje klassmetod för meddelandehanterare och slutligen END_MESSAGE_MAP makro för att deklarera slutet på meddelandekartan.

En begränsning med makrot BEGIN_MESSAGE_MAP inträffar när det används tillsammans med en klass som innehåller mallargument. När det används med en mallklass orsakar det här makrot ett kompileringsfel på grund av att mallparametrarna saknas under makroexpansionen. Makrot BEGIN_TEMPLATE_MESSAGE_MAP har utformats för att tillåta klasser som innehåller ett enda mallargument att deklarera sina egna meddelandekartor.

Exempel

Tänk dig ett exempel där klassen MFC CListBox utökas för att tillhandahålla synkronisering med en extern datakälla. Den fiktiva CSyncListBox klassen deklareras på följande sätt:

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

Klassen CSyncListBox är mallad på en enda typ som beskriver den datakälla som den ska synkroniseras med. Den deklarerar också tre metoder som kommer att delta i meddelandekartan för klassen: OnPaint, OnDestroyoch OnSynchronize. Metoden OnSynchronize implementeras på följande sätt:

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

Implementeringen ovan gör att klassen kan CSyncListBox vara specialiserad på alla klasstyper som implementerar GetCount metoden, till exempel CArray, CListoch CMap. Funktionen StringizeElement är en mallfunktion som är prototyperad enligt följande:

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

Normalt definieras meddelandekartan för den här klassen som:

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

där LBN_SYNCHRONIZE är ett anpassat användarmeddelande som definieras av programmet, till exempel:

#define LBN_SYNCHRONIZE (WM_USER + 1)

Makrokartan ovan kompileras inte på grund av att mallspecifikationen CSyncListBox för klassen saknas under makroexpansionen. Det BEGIN_TEMPLATE_MESSAGE_MAP makrot löser detta genom att infoga den angivna mallparametern i den expanderade makrokartan. Meddelandekartan för den här klassen blir:

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

Följande visar exempelanvändning av CSyncListBox klassen med hjälp av ett CStringList -objekt:

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

För att slutföra testet måste funktionen StringizeElement vara specialiserad för att fungera med klassen 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
}

Se även

BEGIN_TEMPLATE_MESSAGE_MAP
Meddelandehantering och mappning