分享方式:


如何:建立樣板類別的訊息對應

MFC 中的訊息對應提供有效方法將 Windows 訊息導向到適當的 C++ 物件執行個體。 MFC 訊息對應目標的範例,包括應用程式類別、文件和檢視類別、控制項類別等等。

傳統 MFC 訊息對應會使用 BEGIN_MESSAGE_MAP 宏宣告訊息對應開頭、每個訊息處理常式類別方法的宏專案,最後 宣告END_MESSAGE_MAP 宏來宣告訊息對應結尾。

BEGIN_MESSAGE_MAP 宏與包含樣板引數的類別搭配使用時,就會發生一項限制。 當搭配使用樣板類別,此巨集會因為在巨集展開期間缺少樣板參數而導致編譯時期錯誤。 BEGIN_TEMPLATE_MESSAGE_MAP 宏的設計目的是允許包含單一樣板引數的類別宣告自己的訊息對應。

範例

請考慮擴充 MFC CListBox 類別的範例,以提供與外部資料源的同步處理。 虛構 CSyncListBox 類別宣告如下:

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

類別 CSyncListBox 會以單一類型來樣板化,描述其將會同步處理的資料來源。 它也會宣告三個將參與 類別訊息對應的方法: OnPaintOnDestroyOnSynchronize 。 方法 OnSynchronize 會實作如下:

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

上述實作可讓 CSyncListBox 類別在實作 方法的任何類別類型 GetCount 上特製化,例如 CArrayCListCMap 。 函 StringizeElement 式是範本函式,其原型如下:

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

通常,這個類別的訊息對應會定義如下:

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

其中 LBN_SYNCHRONIZE 是由應用程式定義的自訂使用者訊息,例如:

#define LBN_SYNCHRONIZE (WM_USER + 1)

由於類別的 CSyncListBox 範本規格在宏擴充期間遺失,因此上述宏對應不會編譯。 BEGIN_TEMPLATE_MESSAGE_MAP 宏會藉由將指定的樣板參數併入展開的宏對應,來解決此問題。 這個類別的訊息對應會成為:

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

下列示範如何使用 CStringList 物件來示範 類別的 CSyncListBox 範例使用方式:

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

若要完成測試,函 StringizeElement 式必須特製化,才能使用 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
}

另請參閱

BEGIN_TEMPLATE_MESSAGE_MAP
訊息處理和對應