?? 06.5.4 框架類窗口及本章小結(jié).txt
字號:
6.5.4 框架類窗口
下面我們想讓程序中動態(tài)添加的菜單項(xiàng)命令由框架類來捕獲。根據(jù)前面"菜單命令的路由"一節(jié)中的內(nèi)容,我們知道,當(dāng)框架類窗口接收到一個消息時(shí),它首先會把消息交給其子窗口,即視類窗口去處理。那么這里如何讓框架類窗口首先捕獲菜單命令井響應(yīng)呢?我們再回想一下"菜單命令的路由"一節(jié)中講述的命令消息的路由過程 (如圖 6.8所示),我們知道菜單命令是交由 OnCommand函數(shù)來處理的,在這個函數(shù)中將完成命令消息的路由。那么我們就可以這樣設(shè)想,如果 OnCommand函數(shù)是一個虛函數(shù)的話,我們就可以在框架類中重寫它,然后截獲本該交由視類去響應(yīng)的命令消息,讓框架類來完成這些消息的處理。
如果在 MSDN中查看 OnCommand這個函數(shù)的信息,可以找到該函數(shù)具有以下形式的聲明:
virtual BOOL OnCornmand( WPARAM wParam, LPARAM lparam );
可以發(fā)現(xiàn)它確實(shí)是一個虛函數(shù)。這樣,我們就可以在
Menu2程序的框架類中重寫這個函數(shù),截獲那些動態(tài)添加的菜
單項(xiàng)的命令消息,讓它們不再繼續(xù)向下路由。遵照這樣的思路,
我們先給 CMainFrame類添加一個 OnCornmand虛函數(shù),添加方法可以是在 ClassView標(biāo)簽上,用鼠標(biāo)右鍵單擊 CMainFrame類的名稱,然后在彈出的快捷菜單中選擇【
Add Virtual Function.. .】命令 (如圖 6.47所示 ),就會出現(xiàn)圖 6.48所示的添加虛擬函數(shù)對話框。在這個對話框左邊的 New Virtual Functions列表中選擇 OnCornmand函數(shù),然后單擊【
Add Handler】按鈕,為 CMainFrame類增加一個 OnCornmand虛函數(shù)的重寫。這時(shí),在這個對話框右邊的 Existing virtual function
圖 6 .47添加虛函數(shù)的命令
overrides列表中就會多了一個重寫的 OnCornmand函數(shù),并處于選中狀態(tài),如圖 6 .49所示。
圖 6.48添加虛擬函數(shù)對話框
圖 6 .4 9添加了一個 OnCommand虛擬函數(shù)的重寫
然后,我們就可以單擊對話框上的【Edit Existing】按鈕,這時(shí) VC++將會定位到程序中 OnCommand這個重寫函數(shù)的定義處,該函數(shù)的定義代碼如例 6-43所示。例 6-43
BOOL CMa工 nFrame : : OnCommand(WPARAM wParam , LPARAM lparam) 11 TODO: Add your specialized code here and/ or call the base class return CFrameWnd : :OnCommand(wParam, lparam);
這時(shí),在程序運(yùn)行時(shí),當(dāng)菜單命令由程序框架類 ( CFrameWnd類)的 OnWndMsg函數(shù)交由 OnCommand函數(shù)后,因?yàn)槠渥宇? CMainFrame類重寫了這個 OnCommand函數(shù),菜單命令消息就會先到子類的這個函數(shù)(即上述例 6-43所示的 OnCommand函數(shù))中報(bào)到。后者最后將調(diào)用基類(即程序框架類: CFrameWnd )的 OnCommand函數(shù)進(jìn)行消息的路由。
OnCommand函數(shù)是對所有的命令消息進(jìn)行路由處理的,包括菜單、工具按鈕,以及加速鍵的命令消息。而這里我們只對動態(tài)添加的菜單項(xiàng)的命令感興趣。因此,在上述例 6-43所示的 OnCommand函數(shù)中,需要對到達(dá)的消息加以判斷,檢查該消息是否是我們需要的。根據(jù)上述例 6-43所示的 OnCommand函數(shù)代碼,可以看到, OnCommand這個函數(shù)帶有兩個參數(shù),其中第一個參數(shù)的類型是 WPARAM,這是一個 4字節(jié)的無符號整型。在參數(shù) wParam低端的兩個字節(jié)中放置的是發(fā)送當(dāng)前消息的菜單項(xiàng)、工具按鈕,或加速鍵的命令 E。因此,就可以利用 LOWORD這個宏取得當(dāng)前消息的命令 ID,然后判斷其是否是程序中動態(tài)添加的菜單項(xiàng),即當(dāng)前消息的命令 E是否在這些菜單項(xiàng) E范圍之內(nèi),如果是,就處理:否則,就把消息交由基類繼續(xù)路由。
LOWORD宏從給定的 32位值中取得低端字。回WORD宏從給定的 32
位值中取得高端字。
本例中要處理的菜單項(xiàng)命令范圍的下限就是我們剛才定義的 IDM PHONEl,但是我們并不知道在程序中會動態(tài)增加多少菜單項(xiàng)。為了讓程序具有較好的可擴(kuò)充性,在程序中
4‘~.... I 213
就不應(yīng)該用具體的數(shù)字來指定這個范圍,例如,把這個范圍的上限指定為 IDM_PHONEl +3,或者 IDM PHONEl+5,這是不合適的。因?yàn)槲覀円呀?jīng)把每次輸入的字符串都保存到 m s位Array這個集合類變量中了。集合類的成員函數(shù)GetSize可以獲得集合變量中元素的個數(shù),也就是輸入的字符串個數(shù)。但是m_strArray是視類CMenu2View的成員變量,在框架類 CMainFrame中如何去調(diào)用視類的成員變量呢?當(dāng)然,我們首先需要獲得視類對象,然后才能訪問該對象的公有成員。我們可以利用CMainFrame類提供的GetActiveView成員函數(shù),獲取與框架相關(guān)聯(lián)的當(dāng)前視類的指針。這個函數(shù)的聲明如下所示:
CView* GetActiveView( ) const;
我們可以看到這個函數(shù)返回一個 CView類型的指針,而程序需要的是 CMenu2View類型的指針,因此需要進(jìn)行類型轉(zhuǎn)換。有了這個視類指針,就可以調(diào)用其public類型的成員變量了,這就是先前把m_strArray定義為public類型的原因了。
下面,我們就在CMainFrame類的OnCommand函數(shù)中添加需處理的菜單命令范圍的實(shí)現(xiàn)代碼,代碼如例6-44所示。
鑼IJ 6-44
BOOL CMainFrarne :: OnCommand(WPARAM wParam , LPARAM lpararn)
{ // TODO : Add your specialized code here and/or call the base class int MenuCmdID = LOWORD(wParam);
I CMenu2view *pView = (CMenu2View *)GetActiveView(); if ( MenuCmd工D>=工DM_PHONEl && MenuCmd工D<工DM_PHONEl + pView->m_strArray.
GetSize() )
{
}
return CFrameWnd : :OnCommand(wPararn, lPararn) ;
對于例6-44所示這段代碼,還有一個問題需要提醒讀者注意,就是菜單項(xiàng)范圍的上限的確定問題,到底應(yīng)該是小于,還是小于等于這個上限值呢?讀者可以簡單地計(jì)算一下,例如,如果m_strArray中存儲了 2個字符串,對應(yīng)的菜單項(xiàng)標(biāo)識就應(yīng)該是IDM_PHONEl和IDMPHONE2。并且 GetSize函數(shù)返回值是 2,而EM-PHONE1+2相當(dāng)于 IDM_PHONE3,因此到底應(yīng)該是小于,還是小于等于,經(jīng)過這種計(jì)算就很清楚了。
E 小技瑪:在編寫程序時(shí),經(jīng)常會遇到一些這種邏輯上的問題。我們可以像
這里遇到的問題一樣,簡單地把問題變成一些具體的數(shù)值,然后去計(jì)算,就可以得到答案了。
編譯這時(shí)的Menu2程序,將會出現(xiàn)如下錯誤信息:
--一----------------Configuration: Menu2 -win32 Debug-一一-----------------Cornpiling . . .
Ma工nFrm.cpp
214 I ~~~
0 :\VC++深入編程 \Chapter6\Menu2\MainFrm.cpp(132} error C2065: 'CMenu2View' : undeclared identifier
0:\VC++深入編程\Chapter6\Menu2\MainFrm.cpp(132} : error C2065: 'pView' undeclared identifier
0:\VC++深入編程\Chapter6\Menu2\MainFrm. cpp (132) : error C2059: syntax error :
0 :\VC++深入編程\Chapter6\Menu2\MainFrm.cpp(133} : error C2 227: left o f '->m_strArray' must point to class/ struc t / union
0 :\VC++深入編程\Chapter6\Menu2\MainFrm.cpp(133} : error C2228: left of
'.GetSize' must have class/ struct/ union type Error executing cl.exe. Creating browse info file...
Menu2.exe -5 error(s} , 0 warning(s}
因?yàn)樵诳蚣茴?CMainFrame)中用到了視類(CMenu2View )類型,所以應(yīng)該在框架類的源文件中包含視類的頭文件。即把下面灰色顯示的那行代碼添加到CMainFrame類的源文件的前部,結(jié)果如例6-45所示。
iJtl6-45
// MainFrm.cpp : implementation of the CMainFrame class
//
#include "stdafx.h" #include "Menu2 .h"
#include "MainFrm.h" #include "Menu2View.h"
國際z陽C程序中,除了CMainFrame類以外,其他程序類去掉類名前面
的第一個字母:C,就是該類頭文件和源文件的名稱(指不帶文件%后綴的名稱)。另外一種獲知類的頭文件名稱的方法是,在ClassView選項(xiàng)卡上雙擊類名,就可以打開該類的頭文件,從而在VC++開發(fā)環(huán)境的窗口標(biāo)題欄上就可以看到該類的頭文件名稱。
再次編譯Menu2程序,仍有錯誤發(fā)生,編譯器提示發(fā)生以下錯誤:
-------------------Configuration: Menu2 -Win32 Debug-----------------一-Compiling . . .
Ma工nFrm.cpp
d:\vc++深入編程\chapter6\menu2\menu2view.h(21} : error C2143 : syntax error : missing ';' before ,*,
d : \vc++深入編程\chapter6\menu2\menu2view.h(21} : error C2501: 'CMenu2Doc' : missing storage-class or type spec工fiers
d:\vc++深入編程 \chapter6\menu2\menu2view.h(21} error C2501: 'GetDocument' : missing storage-class or type specifiers
" ‘ I 215
鵝"
Error executing cl .exe. Creating browse info file...
Menu2.exe -3 error(s) , 0 warning(s)
上述錯誤信息提示:在"*"號之前少了一個l' . "。讀者可以在第一條錯誤提示上雙擊
鼠標(biāo)左鍵, VC++將在代碼編輯窗口打開CMenu2View類頭文件,井定位到如圖6.50中光標(biāo)所示位置。這個錯誤在程序開發(fā)中會經(jīng)常遇到,實(shí)際
上,并不是錯誤信息所說的那樣要在*號前加一class CMe nu2Uiew : public CUiew
p~otected: 11 c~eatp from serialization on19
個分號,而是程序不認(rèn)識CMenu2Doc這個類。< CMe nu2Uiew( >;
在第二章中我們曾經(jīng)介紹過,C++程序在編譯時(shí),DECLARE_DYHCREATE(CMe nu2Uiew> 11 Attributes
只有源文件參與編譯,我們剛才在 CMainFramepubl1c:
類的源文件前部加入了包含Menu2view.h文件的_1 CMe nu2Doc* C.tOocu回nt() ;
11 Operations
. 代碼,因此,在編譯CMainFrame類的源文件時(shí),public:
當(dāng)遇到這行語句,就會展開Menu2View.h文件的圖6.50編譯器提示的出錯位置內(nèi)容,但該文件中引用了尚未定義的CMenu2Doc 類,即圖6.50中所示代碼處引用了尚未定義的CMenu2Doc類。因此編譯器就會報(bào)告上述錯誤信息。那為什么視類的源文件包含它的頭文件,在編譯時(shí)沒有出錯呢?可以看看 CMenu2View類的源文件,如例6-46所示是它開始的幾行語句。
f9IJ 6-46
11 Menu2View.cpp ; implementation of the CMenu2View class
11
#include "stdafx.h"
#include "Menu2 .h"
#include "Menu2Doc.h"
#include "Menu2View .h"
可以看到, Menu2View.cpp文件在包含Menu2view.h文件之前包含了 Menu2Doc.h文件,這樣,當(dāng)編譯器在編譯這個源文件時(shí),將先展開Menu2Doc.h類文件的內(nèi)容,該文件中是 CMenu2Doc類的定義。然后才展開 Menu2View.h文件,這時(shí)編譯器己經(jīng)知道了 CMenu2Doc類的定義。其實(shí),為了解決上述錯誤,歸根到底就是要讓編譯器在引用 CMenu2View類定義之前就已經(jīng)知道了 CMenu2Doc類的定義。因此,為了解決這里出現(xiàn)的問題,可以把視類源文件中包含文檔類的定義語句移到視類的頭文件中,并放置在視類定義之前。即剪切Menu2View.cpp文件中的"州nclude "Menu2Doc.h""這行語旬,并將其粘貼到Menu2View.h文件的前部。如例6-47所示是修改之后的Menu2View.h文件的前部代碼。
例6-47
11 Menu2View.h : interface of the CMenu2View class
216 I ~~~
//
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -