?? 03.3 窗口類、窗口類對象與窗口.txt
字號:
3.3 窗口類、窗口類對象與窗口
3.3.1 三者之間關(guān)系
很多開發(fā)人員都將窗口類、窗口類的對象和窗口之間的關(guān)系弄混淆了。為了使讀者能更好地理解它們之間的關(guān)系,下面我們將模擬CWnd類的封裝過程。首先新建一個Win32 Application類型的工程,取名為“WinMain”。在隨后的向?qū)Т翱谥羞x擇創(chuàng)建一個空工程(即選擇an empty project選項)。接著為該工程新建一個源文件WinMain.cpp。在該文件中,首先新建一個類CWnd,然后為其定義創(chuàng)建窗口函數(shù)(CreateEx)、顯示窗口函數(shù)(ShowWindow)和更新窗口函數(shù)(UpdateWindow)三個函數(shù),并定義一個成員變量(m_hWnd)。具體代碼如例3-18所示。
例3-18
class CWnd
{
public:
BOOL CreateEx(DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // menu handle or child identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam); // window-creation data
BOOL ShowWindow(int nCmdShow);
BOOL UpdateWindow();
public:
HWND m_hWnd;
};
小技巧:這些函數(shù)的參數(shù)可以參照MSDN中相應(yīng)MFC函數(shù)的定義,然后直接復(fù)制這些參數(shù)即可。
提示:因為SDK函數(shù)數(shù)量很多,程序員記憶負(fù)擔(dān)很重。MFC中使用的大部分函數(shù)名與相應(yīng)的SDK函數(shù)名相同,這樣做的目的就是為了方便程序員,減輕記憶負(fù)擔(dān)。程序員只需要記憶兩者中的一個就可以了。
接下來完成這三個函數(shù)的定義,代碼如例3-19所示。
例3-19
BOOL CWnd::CreateEx(DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // menu handle or child identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam) // window-creation data
{
m_hWnd=::CreateWindowEx(dwExStyle,lpClassName,dwStyle,x,y,
nWidth,nHeight,hWndParent,hMenu,hInstance,
lpParam);
if(m_hWnd!=NULL)
return TRUE;
else
return FALSE;
}
BOOL CWnd::ShowWindow(int nCmdShow)
{
return ::ShowWindow(m_hWnd,nCmdShow);
}
BOOL CWnd::UpdateWindow()
{
return ::UpdateWindow(m_hWnd);
}
其中,我們定義的CWnd類的CreateEx函數(shù)需要完成創(chuàng)建窗口的工作,這可以利用Win32提供的SDK函數(shù):CreateWindowEx函數(shù)來實現(xiàn)。該函數(shù)返回一個句柄,標(biāo)識它所創(chuàng)建的窗口。這里,我們就可以利用已定義的CWnd類的成員變量m_hWnd來保存這個窗口句柄。因為我們定義的CreateEx函數(shù)返回值是個BOOL型,所以應(yīng)該判斷一下這個窗口句柄。根據(jù)其值是否為空來決定函數(shù)是返回TRUE值,還是FALSE值。
讀者應(yīng)注意的是,在實際開發(fā)時,應(yīng)該初始化m_hWnd變量,這可以在構(gòu)造函數(shù)中實現(xiàn),給它賦一個初值NULL。這里我們只是為了演示CWnd類是如何與窗口關(guān)聯(lián)起來的,因此就不進(jìn)行初始化工作了。
接下來定義ShowWindow函數(shù)的實現(xiàn)。同樣,需要調(diào)用Platform SDK函數(shù),即ShowWindow來完成窗口的顯示。為了區(qū)分這兩個同名函數(shù),在調(diào)用這個Platform SDK函數(shù)時,前面加上作用域標(biāo)識符(即::)。這種以“::”開始的表示方法表明該函數(shù)是一個全局函數(shù),這里表示調(diào)用的ShowWindow函數(shù)是Platform SDK函數(shù)。因為CreateEx函數(shù)已經(jīng)獲取了窗口句柄并保存到m_hWnd成員變量中,所以,ShowWindow函數(shù)可以直接把這個句柄變量作為參數(shù)來使用。
提示:讀者在定義自己的成員函數(shù)時,如果調(diào)用的API函數(shù)名與自己的函數(shù)名不同,那么該API函數(shù)名前可以加也可以不加“::”符號,編譯器會自動識別API函數(shù)。但是如果當(dāng)前定義的成員函數(shù)與內(nèi)部調(diào)用的API函數(shù)名相同,那么后者前面必須加“::”符號,否則程序在編譯或運(yùn)行時就會出錯。
我們自己定義的UpdateWindow函數(shù)的實現(xiàn)比較簡單,直接調(diào)用SDK函數(shù):UpdateWindow完成更新窗口的工作。
從例3-19所示代碼可知,我們定義的CWnd類的后兩個函數(shù)(ShowWindow和UpdateWindow)內(nèi)部都需要一個窗口句柄,即需要知道對哪個窗口進(jìn)行操作。
現(xiàn)在我們就實現(xiàn)了一個窗口類:CWnd。但我們知道如果要以類的方式來完成窗口的創(chuàng)建、顯示和更新操作,那么首先還需要編寫一個WinMain函數(shù)。讀者并不需要記憶這個函數(shù)的寫法,只要機(jī)器上有MSDN就可以了,在MSDN中找到該函數(shù)的幫助文檔,直接復(fù)制其定義即可。這里,我們只是想講解在這個函數(shù)內(nèi)部所做的工作,并不是真正的實現(xiàn),因此只是寫出其主要的代碼,如例3-20所示。
例3-20
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
//首先是設(shè)計窗口類,即定義一個WNDCLASS,并為相應(yīng)字段賦值。
WNDCLASS wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
......
//注冊窗口類
RegisterClass(&wndcls);
//創(chuàng)建窗口
CWnd wnd;
wnd.CreateEx(...);
//顯示窗口
wnd.ShowWindow(SW_SHOWNORMAL);
//更新窗口
wnd.UpdateWindow();
//接下來就是消息循環(huán),此處省略
......
return 0;
}
請讀者回想一下第1章中我們利用SDK編程時為創(chuàng)建窗口、顯示窗口和更新窗口所編寫的代碼(如例3-21所示),并比較例3-20和例3-21這兩段代碼的區(qū)別。
例3-21
HWND hwnd;
hwnd=CreateWindowEx();
::ShowWindow(hwnd,SW_SHOWNORMAL);
::UpdateWindow(hwnd);
我們可以發(fā)現(xiàn),SDK程序中多了一個HWND類型的變量hwnd。該變量用來保存由CreateWindowEx函數(shù)創(chuàng)建的窗口句柄,并將其作為參數(shù)傳遞給隨后的顯示窗口操作(ShowWindow函數(shù))和更新窗口操作(UpdateWindow函數(shù))。而我們自定義的實現(xiàn)代碼中,CWnd類定義了一個HWND類型的成員變量:m_hWnd,用于保存這個窗口句柄。首先CWnd類的CreateEx函數(shù)創(chuàng)建窗口,并將該窗口句柄保存到這個成員變量,接著調(diào)用CWnd類的ShowWindow函數(shù)顯示窗口時,就不需要再傳遞這個句柄了,因為它已經(jīng)是成員變量,該函數(shù)可以直接使用它。CWnd類的UpdateWindow函數(shù)也是一樣的道理。
許多程序員在進(jìn)行MFC程序開發(fā)時,容易混淆一點:認(rèn)為這里的CWnd類型的wnd這個C++對象所代表的就是一個窗口。因為在實踐中,他們看到的現(xiàn)象是:當(dāng)C++窗口類對象銷毀時,相應(yīng)的窗口也就沒了。有時正好巧合,當(dāng)窗口銷毀時,C++窗口類對象的生命周期也到了,從而也銷毀了。正因為如此,許多程序員感覺C++窗口類對象就是窗口,窗口就是這個C++窗口類對象。事實并非如此。讀者可以想像一下,如果我們關(guān)閉了一個窗口,這個窗口就銷毀了,那么該窗口對應(yīng)的C++窗口類對象銷毀了沒有呢?當(dāng)然沒有。當(dāng)一個窗口銷毀時,它會調(diào)用CWnd類的DestroyWindow函數(shù),該函數(shù)銷毀窗口后,將CWnd成員變量:m_hWnd設(shè)為NULL。
C++窗口類對象的生命周期和窗口的生命周期不是一致的。當(dāng)一個窗口銷毀時,與C++窗口類對象沒有關(guān)系,它們之間的紐帶僅僅在于這個C++窗口類內(nèi)部的成員變量:m_hWnd,該變量保存了與這個C++窗口類對象相關(guān)的那個窗口的句柄。
另一方面,當(dāng)我們設(shè)計的這個C++窗口類對象銷毀的時候,與之相關(guān)的窗口是應(yīng)該銷毀的,因為它們之間的紐帶(m_hWnd)已經(jīng)斷了。另外,窗口也是一種資源,它也占據(jù)內(nèi)存。這樣,在C++窗口類對象析構(gòu)時,也需要回收相關(guān)的窗口資源,即銷毀這個窗口。
因此,讀者一定要注意:C++窗口類對象與窗口并不是一回事,它們之間惟一的關(guān)系是C++窗口類對象內(nèi)部定義了一個窗口句柄變量,保存了與這個C++窗口類對象相關(guān)的那個窗口的句柄。窗口銷毀時,與之對應(yīng)的C++窗口類對象銷毀與否,要看其生命周期是否結(jié)束。但C++窗口類對象銷毀時,與之相關(guān)的窗口也將銷毀。在我們定義的這個WinMain程序(例3-20所示代碼)中,當(dāng)程序運(yùn)行到WinMain函數(shù)的右大括號(})時,該函數(shù)內(nèi)部定義的Wnd窗口類對象的生命周期也就結(jié)束了。
這是我們自已定義的CWnd類,那么MFC提供的CWnd類是不是這樣實現(xiàn)的呢?讀者在MSDN中查看MFC提供的CWnd類,將會發(fā)現(xiàn)該類確實定義了一個數(shù)據(jù)成員:m_hwnd,用來保存與之相關(guān)的窗口的句柄。因為MFC中所有的窗口類都是由CWnd類派生的,于是,所有的窗口類(包括子類)內(nèi)部都有這樣的一個成員用來保存與之相關(guān)的窗口句柄。所以,讀者不能認(rèn)為我們前面創(chuàng)建的MFC程序Test中的CMainFrame類和CTestView類的對象就是一個窗口。
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -