1. 程式人生 > >深入MFC中WM_COMMAND的傳遞

深入MFC中WM_COMMAND的傳遞

   MFC將windows訊息系統進行了高度的抽象和封裝,其根本原理是運用C++的高階特性並結合一定的設計模式(如工廠模式,模板方法等)來實現的。一般的windows訊息(WM_XXX),則一定是由派生類流向基類,沒有旁流的可能。如果是命令訊息(WM_COMMAND),那就有比較奇特的路線了。下面就針對多文件/單文件(Document-View)、對話方塊兩種應用程式比較討論WM_COMMAND訊息的傳遞處理過程。討論前首先得明確命令訊息的來源,命令訊息一般是使用者選擇某個選單項,或一個加速鍵被翻譯,或一個子控制元件傳送一個通知訊息給它的父視窗時產生的。對一個選單而言,訊息接收者是Frame視窗或擁有它的對話方塊;對一個工具欄而言,訊息接收者是它的父視窗。兩種應用程式命令訊息處理流程如下圖所示。                   

   從上圖可知,文件檢視型的處理路線是先向下再向上,而對話方塊型的路線是一直向上,訊息接收者只有一個,而處理者次序有多個,每個處理者內部首先都是呼叫根基類CCmdTarget的OnCmdMsg虛擬函式,在這個函式內逐級向基類遍歷訊息對映表,根據命令ID和通知碼找到對應的訊息對映結構體AFX_MSGMAP_ENTRY,如果找到再處理這個命令訊息,否則返回FALSE,退回到this物件所在的OnCmdMsg函式進行下一步處理。如果到最後一層都沒有找到對應命令的訊息對映,則返回到系統的預設處理DefWindowProc。再綜合考慮下,如果一個對話方塊接收到了一個命令訊息例如是點選它的子控制元件工具欄某個按鈕發出的,而這個對話方塊類沒有新增相應的ON_COMMAND對映,就會進入到它的父視窗類OnCmdMsg函式進行處理,如果這個父視窗正好是Frame視窗,那麼命令訊息的處理流程就由上圖右邊轉到左邊了。而最終命令訊息能否得處理,就看上圖5種物件(Frame、View、Document、Dialog、App、Thread)是否添加了對應的ON_COMMAND對映。
   
   到此為止,我們已經明確了WM_COMMAND訊息的處理流程,但是發現最終處理卻是由收到訊息的視窗傳遞的,不是訊息通知者自己處理的,有的時候為了提高程式碼的封裝性,可能需要自己處理這些命令比較方便,比如有一個工具欄CPlayToolBar子類從CToolBar繼承,有播放、暫停、停止3個按鈕,它的父視窗是CPlayDialog對話方塊。按照常規,這3個按鈕命令事件的處理一般是在CPlayDialog類中3個ON_COMMAND對映巨集和處理函式的,但如果在CPlayToolBar類中新增3個ON_COMMAND對映巨集和處理函式,是得不到處理的,其原因在於對話方塊型的路線是一直向上,再者MFC中沒有對應的命令反射ON_COMMAND_REFLECT這個巨集。為了能使CPlayToolBar類自己處理這3個按鈕命令事件,就需要從CPlayDialog類中轉移路線,使之流向其子視窗工具欄,這樣CPlayToolbar 類就得到了自己處理的機會。具體操作是過載CPlayToolBar和CPlayDialog的OnCommand虛擬函式,  CPlayDialog程式碼如下所示:
1  BOOL   CPlayDialog::OnCommand(WPARAM wParam, LPARAM lParam)
 
2{
 
3if (lParam==(LPARAM)m_playtoolbar.m_hWnd)
 
4{
 
5              m_playtoolbar.OnCommand(wParam,lParam);   //m_playtoolbar為CPlayToolBar物件,注意使OnCommand成為公有成員
6        }

 
7else
 
8{
 
9return   CDialog::OnCommand(wParam, lParam);
10       }

11   }
   CPlayToolBar類程式碼如下所示  1    BEGIN_MESSAGE_MAP(CPlayToolBar, CToolBar)
 
2         ON_COMMAND(ID_PLAY,  Play)
 
3         ON_COMMAND(ID_PAUSE,  Pause)
 
4         ON_COMMAND(ID_STOP,  Stop)
 5    END_MESSAGE_MAP()
 
6
 7    void   CPlayToolBar::Play()
 
8{
 
9    }

10   void   CPlayToolBar::Pause()
11{
12   }

13void   CPlayToolBar::Stop()
14
15   }
    現在,3個按鈕命令事件能在CPlayToolBar類中獨立處理了,這樣一來就提高了程式碼的封裝性,簡化了父視窗CPlayDialog類的處理。 posted on 2009-12-19 21:29 春秋十二月 閱讀(4909) 評論(1)  編輯 收藏 引用 所屬分類: C/C++