WTL怎麼讓View來響應菜單事件?

來源:互聯網
上載者:User

在WTL裡面,CMainFrame的訊息迴圈是這樣的:
程式碼

BEGIN_MSG_MAP(CMainFrame)

  MESSAGE_HANDLER(WM_Create, OnCreate)

  CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)

  CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)

END_MSG_MAP()

而View裡面的訊息迴圈是這樣的:
程式碼

BEGIN_MSG_MAP(CGameView)

  MESSAGE_HANDLER(WM_PAINT, OnPaint)

  MESSAGE_HANDLER(WM_Create, OnCreate)

  COMMAND_ID_HANDLER(ID_GAME_START_NEW, OnGameStartNew)

END_MSG_MAP()

  雖然我明明寫了一個OnGameStartNew來響應功能表項目ID_GAME_START_NEW,但是通過實驗可以確定,CMainFrame並沒有將這個訊息轉交過來,而是自己截獲認為是無用的訊息扔掉了。那麼,如何讓CMainFrame將訊息交給View就是我要解決的問題了。

  首先我想到的是在PreTranslateMessage這個函數上做文章,因為CMainFrame的PreTranslateMessage確實調用了 View的PreTranslateMessage函數,所以要想截獲這個訊息還是蠻容易的,但是由於自己要寫一個顯式的訊息迴圈,寫出來的代碼極為醜 陋,不爽,只把它作為最後保底的方案。

  接著我想到的是向訊息迴圈註冊Message Filter的方法,也就是在View的OnCreate中加入以下代碼:
程式碼

// register object for message filtering and idle updates

CMessageLoop* pLoop = _Module.GetMessageLoop();

ATLASSERT(pLoop != NULL);

pLoop->AddMessageFilter(this);

  並且,要讓View派生自CMessageFilter,如下所示:
程式碼

class CGameView

  : public CScrollWindowImpl<CGameView>

  , public CMessageFilter

{

  // ...

};

  但是很可惜,只有FrameWindow才能享用Message Filter帶來的靈活性,View不可以。

  再接著,我嘗試使用CHAIN_MSG_MAP_MEMBER(),直接在CMainFrame的訊息迴圈裡面把View的訊息迴圈給插進來:
程式碼

BEGIN_MSG_MAP(CMainFrame)

  MESSAGE_HANDLER(WM_Create, OnCreate)

  CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)

  CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)

  CHAIN_MSG_MAP_MEMBER(m_view)

END_MSG_MAP()

  不過讓我十分沮喪的是,這樣可以捕捉菜單訊息了,也非常的簡潔明了,但是View截獲了很多本來不應該收到的訊息,使得整個CMainFrame的顯示等等都亂了!這可不行!

  最後,我找到了CHAIN_MSG_MAP_ALT_MEMBER()和ALT_MSG_MAP(),用它們一起配合,終於讓事情變得簡潔好用了!
  先要改View裡面的訊息迴圈,把它寫成如下形式:
程式碼

BEGIN_MSG_MAP(CGameView)

  MESSAGE_HANDLER(WM_PAINT, OnPaint)

  MESSAGE_HANDLER(WM_Create, OnCreate)

ALT_MSG_MAP(1) // Handle notify message.

  COMMAND_ID_HANDLER(ID_GAME_START_NEW, OnGameStartNew)

END_MSG_MAP()

  然後在CMainFrame的訊息迴圈中加入CHAIN_MSG_MAP_ALT_MEMBER():
程式碼

BEGIN_MSG_MAP(CMainFrame)

  MESSAGE_HANDLER(WM_Create, OnCreate)

  CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)

  CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)

  CHAIN_MSG_MAP_ALT_MEMBER(m_view, 1)

END_MSG_MAP()

  好了,這就可以了。

  最後的最後,來廢話一下原理:
  在ATL/WTL 裡面,為了能夠將好幾個訊息迴圈有選擇串起來,特別做了這個msgMapID的東西,訊息迴圈都是先switch (msgMapID),然後在不同的 Map ID中再來尋找對應的訊息響應函數。預設的Windows訊息始終使用Map ID為0的訊息響應函數,而一旦有需要,都可以手動改成指定的 Map ID。注意,不同的Map ID的訊息響應函數是互斥的,一次一般只可能使用一個Map(當然,非要手動把多個Map ID加入同一個訊息迴圈的 話也可以……)。
  具體的實現代碼可以看WTL的源碼,如果有一定的Windows編程基礎的話,應該非常好懂。

  使用Map ID的額外好處:
  通過這種機制,我們可以很方便的在CMainFrame中將訊息派發到不同的控制項當中去,這樣就比MFC的訊息機制要靈活很多。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.