http://xz7.2000y.net/mb/2/ReadNews.asp?NewsID=443411
3.5.1 框架窗口
框架窗口為應(yīng)用程序的用戶界面提供結(jié)構(gòu)框架,它是應(yīng)用程序的主窗口,負(fù)責(zé)管理其包容的窗口,一個(gè)應(yīng)用程序的最頂層的框架窗口是應(yīng)用程序啟動(dòng)時(shí)創(chuàng)建的第一個(gè)窗口。
MFC提供三種類型的框架窗口:單文檔窗口,多文檔窗口(MDI),對話框。在AppWizard的第一個(gè)對話框中,就提供了選項(xiàng),讓用戶選擇應(yīng)用程序是基于單文檔、多文檔還是對話框的。MFC單文檔窗口一次只能打開一個(gè)文檔框架窗口,而MDI應(yīng)用程序運(yùn)行時(shí),在應(yīng)用程序的一個(gè)實(shí)例中打開多個(gè)文檔框架窗口,這些窗口稱作子窗口(Child Window)。這些文檔可以是同一類型的,也可以是不同類型的。如Visual Studio就可以打開資源文件窗口和源程序窗口等不同類型的窗口。此時(shí),激活不同類型的MDI子窗口,菜單也將相應(yīng)變化。
MFC提供了三個(gè)類CFrameWnd、CMDIFrameWnd、CMDIChildWnd和CDialog 分別用于支持單文檔窗口、多文檔窗口和對話框。
CFrameWnd
用于SDI框架窗口,形成單個(gè)文檔及其視的邊框。框架窗口既是應(yīng)用程序的主框架窗口,也是當(dāng)前文檔對應(yīng)的視圖的邊框。
CMDIFrameWnd
用于MDI應(yīng)用程序的主框架窗口。主框架窗口是所有MDI文檔窗口的容器,并與它們共享菜單條。MDI框架窗口是出現(xiàn)在桌面中的頂層窗口。
CMDIChildWnd
用于在MDI主框架窗口中顯示打開的各個(gè)文檔。每個(gè)文檔及其視都有一個(gè)MDI子框架窗口,子框架窗口包含在MDI主框架窗口中。子框架窗口看起來類似一般的框架邊框窗口,但它是包含在主框架窗口中,而不是位于桌面的,并且為主窗口所裁剪。而且MDI子窗口沒有自己的菜單,它與主MDI框架窗口共享菜單。
CDialog
對話框是一種特殊類型的窗口,它的邊框一般不可以調(diào)整,而且內(nèi)部包含一些控件窗口。有關(guān)對話框作為主窗口的技術(shù)可以參見下一章。
要生成一個(gè)單文檔窗口,主窗口就必須從CFrameWnd派生;要生成一個(gè)多文檔窗口,主窗口就必須從CMDIFrameWnd派生,而且其中的子窗口必須從CMDIChildWnd派生出來;而基于對話框的窗口程序就要從CDialog派生出主窗口類。
子窗口
子窗口就是具有WS_CHILD風(fēng)格的窗口,且一定有一個(gè)父窗口。所有的控件都是子窗口。子窗口可以沒有邊框。子窗口被完全限制在父窗口內(nèi)部。
父窗口
父窗口就是擁有子窗口的窗口。彈出式窗口
具有WS_POPUP風(fēng)格,它可以沒有父窗口。這種窗口幾乎什么都沒有,可看作一個(gè)矩形區(qū)域。3.5.2窗口的創(chuàng)建
窗口的創(chuàng)建分為兩步:第一步是用new創(chuàng)建一個(gè)C++的窗口對象,但是此時(shí)只是初始化窗口的數(shù)據(jù)成員,并沒有真正創(chuàng)建窗口(這一點(diǎn)與一般的對象有所不同)。
//第一步:創(chuàng)建一個(gè)C++對象,其中CMainFrame是從CFrameWnd派生的對象。
CMainFrame* pMyFrame=new CMainFrame();//用new操作符創(chuàng)建窗口對象
或
CMainFrame MyFrame;//定義一個(gè)窗口對象,自動(dòng)調(diào)用其構(gòu)造函數(shù)
第二步是創(chuàng)建窗口。CFrameWnd的Create成員函數(shù)把窗口給做出來,并將其HWND保存在C++對象的公共數(shù)據(jù)成員m_hWnd中。
//第二步:創(chuàng)建窗口
pMyFrame->Create(NULL,“My Frame Window”);
或
MyFrame.Create(NULL,“My Frame Window”);
Create函數(shù)的原形如下:
BOOL Create( LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle = WS_OVERLAPPEDWINDOW, const RECT& rect = rectDefault, CWnd* pParentWnd = NULL, LPCTSTR lpszMenuName = NULL, DWORD dwExStyle = 0, CCreateContext* pContext = NULL );
Create函數(shù)第一個(gè)參數(shù)為窗口注冊類名,它指定了窗口的圖標(biāo)和類風(fēng)格。這里我們使用NULL做為其值,表明使用缺省屬性。第二個(gè)參數(shù)為窗口標(biāo)題。其余幾個(gè)參數(shù)指定了窗口的風(fēng)格、大小、父窗口、菜單名等。
這個(gè)函數(shù)看起來比較復(fù)雜,對于CFrameWnd派生出來的窗口,我們可以使用LoadFrame從資源文件中創(chuàng)建窗口,它只需要一個(gè)參數(shù)。
pMyFrame->LoadFrame(IDR_FRAME);
LoadFrame使用該參數(shù)從資源中獲取許多默認(rèn)值,包括主邊框窗口的標(biāo)題、圖標(biāo)、菜單、加速鍵等。但是,在使用LoadFrame時(shí),必須確保標(biāo)題字符串、圖標(biāo)、菜單、加速鍵等資源使用同一個(gè)ID標(biāo)識(shí)符(在本例中,我們使用IDR_FRAME)。
提示:在Hello程序的InitInstance中我們看不到創(chuàng)建窗口的過程。實(shí)際上,在
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CHelloDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CHelloView));
AddDocTemplate(pDocTemplate); 程序片段中,我們看到,CSingleDocTemplate構(gòu)造函數(shù)的第二個(gè)參數(shù)就是IDR_MAINFRAME。在構(gòu)造函數(shù)內(nèi)部,已經(jīng)通過調(diào)用m_pMainWnd->LoadFrame(IDR_MAINFRAME),完成了應(yīng)用程序主窗口的創(chuàng)建過程。
在InitInstance中,創(chuàng)建完窗口后,窗口調(diào)用ShowWindow成員函數(shù)來顯示窗口。ShowWindow帶一個(gè)參數(shù),指示窗口以何種方式顯示(最大化、最小化或一般)。缺省方式為SW_SHOW,但實(shí)際上我們經(jīng)常希望應(yīng)用程序啟動(dòng)時(shí)窗口最大化,此時(shí)可以將該參數(shù)該為SW_SHOWMAXMIZED,即調(diào)用
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
在MainFrm.cpp中,我們還看到CMainFrame類有一個(gè)OnCreate方法。OnCreate成員函數(shù)定義如清單3.3。當(dāng)調(diào)用Create或CreateEx時(shí),操作系統(tǒng)會(huì)向窗口發(fā)送一條WM_CREATE消息。這一函數(shù)就是用來響應(yīng)WM_CREATE消息的。
清單3.3 OnCreate成員函數(shù)定義
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
在OnCreate函數(shù)中,首先調(diào)用CFrameWnd的缺省處理方法OnCreate完成窗口創(chuàng)建工作。后面是應(yīng)用程序主窗口的特定工作,在上面程序中,創(chuàng)建了工具條和狀態(tài)欄(有關(guān)工具條和狀態(tài)欄編程參見下一章有關(guān)內(nèi)容)。可以在此處加入一些初始化工作,如從INI文件中載入設(shè)置,顯示Splash Window(啟動(dòng)畫面)等。
3.5.3 注冊窗口
在傳統(tǒng)的Windows C程序中,送給一個(gè)窗口的所有消息是在它的窗口函數(shù)中處理的。把一個(gè)窗口同它的窗口函數(shù)聯(lián)系起來的過程稱為注冊窗口類。注冊窗口包括對窗口指定一個(gè)窗口函數(shù)(給出窗口函數(shù)的指針)以及設(shè)定窗口的光標(biāo)、背景刷子等內(nèi)容。一個(gè)注冊窗口類可以被多個(gè)窗口共享。注冊窗口通過調(diào)用API函數(shù)RegisterClass來完成。
在MFC下,框架提供了缺省的自動(dòng)窗口注冊過程。框架仍然使用傳統(tǒng)的注冊類,而且提供了幾個(gè)標(biāo)準(zhǔn)的注冊類,它們在標(biāo)準(zhǔn)的應(yīng)用程序初始化函數(shù)中注冊。調(diào)用AfxRegisterWndClass全局函數(shù)就可以注冊附加的窗口類,然后把已經(jīng)注冊的類傳給CWnd的Create成員函數(shù)。用戶可以定制自己的注冊過程,以提供一些附加的特性。比如設(shè)置窗口的圖標(biāo)、背景、光標(biāo)等。下面是注冊窗口的例子。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
UINT ClassStyle=CS_VREDRAW|CS_HREDRAW;
cs.style=cs.style&(~FWS_ADDTOTITLE);
cs.lpszClass = AfxRegisterWndClass(ClassStyle,
AfxGetApp()->LoadStandardCursor(IDC_ARROW),
(HBRUSH)(COLOR_WINDOW+1),//for brush
AfxGetApp()->LoadIcon(IDR_MAINFRAME));
return TRUE;
}
注冊窗口在CFrameWnd的PreCreateWnd方法中完成。從成員函數(shù)名字PreCreateWindow中就可以看出來,注冊窗口的工作必須在調(diào)用Create函數(shù)創(chuàng)建窗口之前完成。其中cs.style=cs.style&(~FWS_ADDTOTITLE)指定窗口標(biāo)題風(fēng)格,關(guān)閉自動(dòng)添加文檔標(biāo)題的功能。AfxRegisterWndClass指定窗口使用箭頭光標(biāo)、背景刷子使用比窗口顏色標(biāo)號(hào)大一的顏色、圖標(biāo)使用IDR_MAINFRAME標(biāo)識(shí)符指定的圖標(biāo)(當(dāng)然也可以使用其它圖標(biāo))。用上面的程序段替換Hello程序MainFrm.cpp中的PreCreateWindow成員函數(shù)定義,并重新編譯和運(yùn)行程序。此時(shí),窗口標(biāo)題變成了Hello,原來令人討厭的“Untitled-”沒有了,因?yàn)榇翱陲L(fēng)格中關(guān)閉自動(dòng)添加當(dāng)前文件名的風(fēng)格。
3.5.4 關(guān)閉和銷毀窗口
框架窗口不僅維護(hù)窗口的創(chuàng)建,還管理著窗口的關(guān)閉和銷毀過程。關(guān)閉窗口時(shí),操作系統(tǒng)依次向被關(guān)閉的窗口發(fā)送WM_CLOSE和WM_DESTROY消息。WM_CLOSE消息的缺省處理函數(shù)OnClose將調(diào)用DestroyWindow,來銷毀窗口;最后,框架調(diào)用窗口的析構(gòu)函數(shù)作清理工作并刪除C++窗口對象。
不要使用C++的delete操作符來銷毀框架窗口,而應(yīng)當(dāng)采用CWnd的DestroyWindow成員函數(shù)來銷毀。DestroyWindow首先刪除子窗口,再刪除窗口本身。若窗口以變量方式產(chǎn)生(即在堆棧上分配內(nèi)存),該窗口對象會(huì)被自動(dòng)清除。若對象是用new操作符創(chuàng)建的(也就是在堆上分配內(nèi)存的),則需要用戶自己處理。有關(guān)DestroyWindow問題在第五章對話框技術(shù)中還要作進(jìn)一步解釋。
OnClose()常用功能:保存窗口的一些狀態(tài)、工具條狀態(tài),提示保存未保存的數(shù)據(jù)等等。
void CMainFrame::OnClose()
{
SaveBarState( "MyDockState" );//保存工具條狀態(tài)
CFrameWnd::OnClose();
}3.5.5 窗口激活
活動(dòng)窗口必定是一個(gè)沒有父窗口的頂層窗口,包括框架窗口和對話框。當(dāng)頂層窗口被激活時(shí),Windows向窗口發(fā)送WM_ACTIVATE消息,對此消息的缺省處理是將活動(dòng)窗口設(shè)為有輸入焦點(diǎn)。
輸入焦點(diǎn)用于表示哪個(gè)窗口有資格接收鍵盤輸入消息。帶有輸入焦點(diǎn)的窗口或是一個(gè)活動(dòng)窗口,或者是該活動(dòng)窗口的子窗口。當(dāng)一個(gè)頂層窗口獲得輸入焦點(diǎn)時(shí),Windows向該窗口發(fā)送WM_SETFOCUS消息,此窗口可將輸入焦點(diǎn)重定位到它的子窗口上。子窗口不會(huì)自動(dòng)獲得輸入焦點(diǎn)。失去輸入焦點(diǎn)的窗口會(huì)收到WM_KILLFOCUS消息。當(dāng)子窗口擁有輸入焦點(diǎn)時(shí),父窗口就不會(huì)處理鍵盤輸入了。