5.4.1非模態(tài)對(duì)話框的特點(diǎn)
與模態(tài)對(duì)話框不同,非模態(tài)對(duì)話框不壟斷用戶的輸入,用戶打開非模態(tài)對(duì)話框后,仍然可以與其它界面進(jìn)行交互。
非模態(tài)對(duì)話框的設(shè)計(jì)與模態(tài)對(duì)話框基本類似,也包括設(shè)計(jì)對(duì)話框模板和設(shè)計(jì)CDialog類的派生類兩部分。但是,在對(duì)話框的創(chuàng)建和刪除過程中,非模態(tài)對(duì)話框與模態(tài)對(duì)話框相比有下列不同之處:
非模態(tài)對(duì)話框的模板必須具有Visible風(fēng)格,否則對(duì)話框?qū)⒉豢梢姡B(tài)對(duì)話框則無(wú)需設(shè)置該項(xiàng)風(fēng)格。更保險(xiǎn)的辦法是調(diào)用CWnd::ShowWindow(SW_SHOW)來顯示對(duì)話框,而不管對(duì)話框是否具有Visible風(fēng)格。
非模態(tài)對(duì)話框?qū)ο笫怯胣ew操作符在堆中動(dòng)態(tài)創(chuàng)建的,而不是以成員變量的形式嵌入到別的對(duì)象中或以局部變量的形式構(gòu)建在堆棧上。通常應(yīng)在對(duì)話框的擁有者窗口類內(nèi)聲明一個(gè)指向?qū)υ捒蝾惖闹羔槼蓡T變量,通過該指針可訪問對(duì)話框?qū)ο蟆?br>
通過調(diào)用CDialog::Create函數(shù)來啟動(dòng)對(duì)話框,而不是CDialog::DoModal,這是模態(tài)對(duì)話框的關(guān)鍵所在。由于Create函數(shù)
不會(huì)啟動(dòng)新的消息循環(huán),對(duì)話框與應(yīng)用程序共用同一個(gè)消息循環(huán),這樣對(duì)話框就不會(huì)壟斷用戶的輸入。Create在顯示了對(duì)話框后就立即返回,而
DoModal是在對(duì)話框被關(guān)閉后才返回的。眾所周知,在MFC程序中,窗口對(duì)象的生存期應(yīng)長(zhǎng)于對(duì)應(yīng)的窗口,也就是說,不能在未關(guān)閉屏幕上窗口的情況下先
把對(duì)應(yīng)的窗口對(duì)象刪除掉。由于在Create返回后,不能確定對(duì)話框是否已關(guān)閉,這樣也就無(wú)法確定對(duì)話框?qū)ο蟮纳嫫冢虼酥缓迷诙阎袠?gòu)建對(duì)話框?qū)ο螅?
不能以局部變量的形式來構(gòu)建之。
必須調(diào)用CWnd::DestroyWindow而不是CDialog::EndDialog來關(guān)閉
非模態(tài)對(duì)話框。調(diào)用CWnd::DestroyWindow是直接刪除窗口的一般方法。由于缺省的CDialog::OnOK和CDialog::
OnCancel函數(shù)均調(diào)用EndDialog,故程序員必須編寫自己的OnOK和OnCancel函數(shù)并且在函數(shù)中調(diào)用DestroyWindow來關(guān)
閉對(duì)話框。
因?yàn)槭怯胣ew操作符構(gòu)建非模態(tài)對(duì)話框?qū)ο螅虼吮仨氃趯?duì)話框關(guān)閉后,用delete操作符刪除對(duì)話框?qū)ο蟆T谄聊簧弦粋€(gè)窗口被刪除后,框架會(huì)調(diào)用CWnd::PostNcDestroy,這是一個(gè)虛擬函數(shù),程序可以在該函數(shù)中完成刪除窗口對(duì)象的工作,具體代碼如下
void CModelessDialog::PostNcDestroy
{
delete this;//刪除對(duì)象本身
}
這樣,在刪除屏幕上的對(duì)話框后,對(duì)話框?qū)ο髮⒈蛔詣?dòng)刪除。擁有者對(duì)象就不必顯式的調(diào)用delete來刪除對(duì)話框?qū)ο罅恕?br>
必須有一個(gè)標(biāo)志表明非模態(tài)對(duì)話框是否是打開的。這樣做的原因是用戶有可能在打開一個(gè)模態(tài)對(duì)話框的情況下,又一次選擇打開命令。程序根據(jù)標(biāo)志來決定是打開
一個(gè)新的對(duì)話框,還是僅僅把原來打開的對(duì)話框激活。通常可以用擁有者窗口中的指向?qū)υ捒驅(qū)ο蟮闹羔樧鳛檫@種標(biāo)志,當(dāng)對(duì)話框關(guān)閉時(shí),給該指針賦NULL值,
以表明對(duì)話框?qū)ο笠巡淮嬖诹恕?br>
提示:在C++編程中,判斷一個(gè)位于堆中的對(duì)象是否存在的常用方法是判斷指向該對(duì)象的指針是否為空。這種機(jī)制要求程序員將指向該對(duì)象的指針初始化為NULL值,在創(chuàng)建對(duì)象時(shí)將返回的地址賦給該指針,而在刪除對(duì)象時(shí)將該指針置成NULL值。
根據(jù)上面的分析,我們很容易把Register程序中的登錄數(shù)據(jù)對(duì)話框改成非模態(tài)對(duì)話框。這樣做的好處在于如果用戶在輸入數(shù)據(jù)時(shí)發(fā)現(xiàn)編輯視圖中有錯(cuò)誤的數(shù)據(jù),那么不必關(guān)閉對(duì)話框,就可以在編輯視圖中進(jìn)行修改。
請(qǐng)讀者按下面幾步操作:
在登錄數(shù)據(jù)對(duì)話框模板的屬性對(duì)話框的MoreStyles頁(yè)中選擇Visible項(xiàng)。
在RegisterView.h頭文件的CRegisterView類的定義中加入
public:
CRegisterDialog * m_pRegisterDlg;
在RegisterView.h頭文件的頭部加入對(duì)CRegisterDialog類的聲明
class CRegisterDialog;
加入該行的原因是在CRegisterView類中有一個(gè)CRegisterDialog類型的指針,因此必須保證CRegisterDialog類的
聲明出現(xiàn)在CRegisterView之前,否則編譯時(shí)將會(huì)出錯(cuò)。解決這個(gè)問題有兩種辦法,一種辦法是保證在#include
“RegisterView.h”語(yǔ)句之前有#include“RegisterDialog.h”語(yǔ)句,這種辦法造成了一種依賴關(guān)系,增加了編譯負(fù)擔(dān),
不是很好;另一種辦法是在CRegisterView類的聲明之前加上一個(gè)對(duì)CRegisterDialog的聲明來暫時(shí)“蒙蔽”編譯器,這樣在有
#include“RegisterView.h”語(yǔ)句的模塊中,除非要用到CRegisterDialog類,否則不用加入#include
“RegisterDialog.h”語(yǔ)句。
在RegisterDialog.cpp文件的頭部的#include語(yǔ)句區(qū)的末尾添加下面兩行
#include "RegisterDoc.h"
#include "RegisterView.h"
利用ClassWizard為CRegisterDialog類加入OnCancel和PostNcDestroy成員函數(shù)。加入的方法是進(jìn)入
ClassWizard后選擇MessageMaps頁(yè),并在Classname欄中選擇CRegisterDialog。然后,在ObjectIDs欄
中選擇IDCANCEL后,在Messages欄中雙擊BN_CLICKED,這就創(chuàng)建了OnCancel。要?jiǎng)?chuàng)建PostNcDestroy,先在
ObjectIDs欄中選擇CRegisterDialog,再在Messages欄中雙擊PostNcDestroy即可。
分別按清單5.10和5.11,對(duì)CRegisterView類和CRegisterDialog類進(jìn)行修改。
清單5.10CRegisterView類的部分代碼
CRegisterView::CRegisterView()
{
//TODO:add construction code here
m_pRegisterDlg=NULL;//指針初始化為NULL
}
void CRegisterView::OnEditRegister()
{
//TODO:Add your command handler code here
if(m_pRegisterDlg)
m_pRegisterDlg->SetActiveWindow();//激活對(duì)話框
else
{
//創(chuàng)建非模態(tài)對(duì)話框
m_pRegisterDlg=new CRegisterDialog(this);
m_pRegisterDlg->Create(IDD_REGISTER,this);
}
}
清單5.11CRegisterDialog的部分代碼
void CRegisterDialog::PostNcDestroy()
{
//TODO:Add your specialized code here and/or call the base class
delete this;//刪除對(duì)話框?qū)ο?/span>
}
void CRegisterDialog::OnCancel()
{
//TODO:Add extra cleanup here
((CRegisterView*)m_pParent)->m_pRegisterDlg=NULL;
DestroyWindow();//刪除對(duì)話框
}
CRegisterView::OnEditRegister函數(shù)判斷登錄數(shù)據(jù)對(duì)話框是否已打開,若是,就激活對(duì)話框,否則,就創(chuàng)建該對(duì)話框。該函數(shù)中主要調(diào)用了下列函數(shù):
調(diào)用CWnd::SetActiveWindow激活對(duì)話框,該函數(shù)的聲明為
CWnd* SetActiveWindow();
該函數(shù)使本窗口成為活動(dòng)窗口,并返回原來活動(dòng)的窗口。
調(diào)用CDialog::Create來顯示對(duì)話框,該函數(shù)的聲明為
BOOL Create(UINTnIDTemplate,CWnd*pParentWnd=NULL);
參數(shù)nIDTemplate是對(duì)話框模板的ID。pParentWnd指定了對(duì)話框的父窗口或擁有者。
當(dāng)用戶在登錄數(shù)據(jù)對(duì)話框中點(diǎn)擊“取消”按鈕后,CRegisterDialog::OnCancel將被調(diào)用,在該函數(shù)中調(diào)用CWnd::
DestroyWindow來關(guān)閉對(duì)話框,并且將CRegisterView的成員m_pRegisterDlg置為NULL以表明對(duì)話框被關(guān)閉了。調(diào)用
DestroyWindow導(dǎo)致了對(duì)CRegisterDialog::PostNcDestroy的調(diào)用,在該函數(shù)中用delete操作符刪除了
CRegisterDialog對(duì)象本身。
編譯并運(yùn)行Register,現(xiàn)在登錄數(shù)據(jù)對(duì)話框已經(jīng)變成一個(gè)非模態(tài)對(duì)話框了。
5.4.2窗口對(duì)象的自動(dòng)清除
一個(gè)MFC窗口對(duì)象包括兩方面的內(nèi)容:一是窗口對(duì)象封裝的窗口,即存放在m_hWnd成員中的HWND(窗口句柄),二是窗口對(duì)象本身是一個(gè)C++對(duì)象。要?jiǎng)h除一個(gè)MFC窗口對(duì)象,應(yīng)該先刪除窗口對(duì)象封裝的窗口,然后刪除窗口對(duì)象本身。
刪除窗口最直接方法是調(diào)用CWnd::DestroyWindow或::DestroyWindow,前者封裝了后者的功能。前者不僅會(huì)調(diào)用后者,而且
會(huì)使成員m_hWnd保存的HWND無(wú)效(NULL)。如果DestroyWindow刪除的是一個(gè)父窗口或擁有者窗口,則該函數(shù)會(huì)先自動(dòng)刪除所有的子窗
口或被擁有者,然后再刪除父窗口或擁有者。在一般情況下,在程序中不必直接調(diào)用DestroyWindow來刪除窗口,因?yàn)镸FC會(huì)自動(dòng)調(diào)用
DestroyWindow來刪除窗口。例如,當(dāng)用戶退出應(yīng)用程序時(shí),會(huì)產(chǎn)生WM_CLOSE消息,該消息會(huì)導(dǎo)致MFC自動(dòng)調(diào)用CWnd::
DestroyWindow來刪除主框架窗口,當(dāng)用戶在對(duì)話框內(nèi)按了OK或Cancel按鈕時(shí),MFC會(huì)自動(dòng)調(diào)用CWnd::DestroyWindow
來刪除對(duì)話框及其控件。
窗口對(duì)象本身的刪除則根據(jù)對(duì)象創(chuàng)建方式的不同,分為兩種情況。在MFC編程中,會(huì)使用大量的窗口對(duì)象,有些窗
口對(duì)象以變量的形式嵌入在別的對(duì)象內(nèi)或以局部變量的形式創(chuàng)建在堆棧上,有些則用new操作符創(chuàng)建在堆中。對(duì)于一個(gè)以變量形式創(chuàng)建的窗口對(duì)象,程序員不必關(guān)
心它的刪除問題,因?yàn)樵搶?duì)象的生命期總是有限的,若該對(duì)象是某個(gè)對(duì)象的成員變量,它會(huì)隨著父對(duì)象的消失而消失,若該對(duì)象是一個(gè)局部變量,那么它會(huì)在函數(shù)返
回時(shí)被清除。
對(duì)于一個(gè)在堆中動(dòng)態(tài)創(chuàng)建的窗口對(duì)象,其生命期卻是任意長(zhǎng)的。初學(xué)者在學(xué)習(xí)C++編程時(shí),對(duì)new操作符的使用往往不太踏
實(shí),因?yàn)橛胣ew在堆中創(chuàng)建對(duì)象,就不能忘記用delete刪除對(duì)象。讀者在學(xué)習(xí)MFC的例程時(shí),可能會(huì)產(chǎn)生這樣的疑問,為什么有些程序用new創(chuàng)建了一
個(gè)窗口對(duì)象,卻未顯式的用delete來刪除它呢?問題的答案就是有些MFC窗口對(duì)象具有自動(dòng)清除的功能。
如前面講述非模態(tài)對(duì)話框時(shí)
所提到的,當(dāng)調(diào)用CWnd::DestroyWindow或::DestroyWindow刪除一個(gè)窗口時(shí),被刪除窗口的PostNcDestroy成員
函數(shù)會(huì)被調(diào)用。缺省的PostNcDestroy什么也不干,但有些MFC窗口類會(huì)覆蓋該函數(shù)并在新版本的PostNcDestroy中調(diào)用
delete
this來刪除對(duì)象,從而具有了自動(dòng)清除的功能。此類窗口對(duì)象通常是用new操作符創(chuàng)建在堆中的,但程序員不必操心用delete操作符去刪
除它們,因?yàn)橐坏┱{(diào)用DestroyWindow刪除窗口,對(duì)應(yīng)的窗口對(duì)象也會(huì)緊接著被刪除。
不具有自動(dòng)清除功能的窗口類如下所示。這些窗口對(duì)象通常是以變量的形式創(chuàng)建的,無(wú)需自動(dòng)清除功能。
所有標(biāo)準(zhǔn)的Windows控件類。
從CWnd類直接派生出來的子窗口對(duì)象(如用戶定制的控件)。
切分窗口類CSplitterWnd。
缺省的控制條類(包括工具條、狀態(tài)條和對(duì)話條)。
模態(tài)對(duì)話框類。
具有自動(dòng)清除功能的窗口類如下所示,這些窗口對(duì)象通常是在堆中創(chuàng)建的。
主框架窗口類(直接或間接從CFrameWnd類派生)。
視圖類(直接或間接從CView類派生)。
讀者在設(shè)計(jì)自己的派生窗口類時(shí),可根據(jù)窗口對(duì)象的創(chuàng)建方法來決定是否將窗口類設(shè)計(jì)成可以自動(dòng)清除的。例如,對(duì)于一個(gè)非模態(tài)對(duì)話框來說,其對(duì)象是創(chuàng)建在堆中的,因此應(yīng)該具有自動(dòng)清除功能。
綜上所述,對(duì)于MFC窗口類及其派生類來說,在程序中一般不必顯式刪除窗口對(duì)象。也就是說,既不必調(diào)用DestroyWindow來刪除窗口對(duì)象封裝的
窗口,也不必顯式地用delete操作符來刪除窗口對(duì)象本身。只要保證非自動(dòng)清除的窗口對(duì)象是以變量的形式創(chuàng)建的,自動(dòng)清除的窗口對(duì)象是在堆中創(chuàng)建的,
MFC的運(yùn)行機(jī)制就可以保證窗口對(duì)象的徹底刪除。
如果需要手工刪除窗口對(duì)象,則應(yīng)該先調(diào)用相應(yīng)的函數(shù)(如CWnd::
DestroyWindow)刪除窗口,然后再刪除窗口對(duì)象.對(duì)于以變量形式創(chuàng)建的窗口對(duì)象,窗口對(duì)象的刪除是框架自動(dòng)完成的.對(duì)于在堆中動(dòng)態(tài)創(chuàng)建了的非
自動(dòng)清除的窗口對(duì)象,必須在窗口被刪除后,顯式地調(diào)用delete來刪除對(duì)象(一般在擁有者或父窗口的析構(gòu)函數(shù)中進(jìn)行).對(duì)于具有自動(dòng)清除功能的窗口對(duì)
象,只需調(diào)用CWnd::DestroyWindow即可刪除窗口和窗口對(duì)象。注意,對(duì)于在堆中創(chuàng)建的窗口對(duì)象,不要在窗口還未關(guān)閉的情況下就用
delete操作符來刪除窗口對(duì)象.
提示:在非模態(tài)對(duì)話框的OnCancel函數(shù)中可以不調(diào)用CWnd::
DestroyWindow,取而代之的是調(diào)用CWnd::ShowWindow(SW_HIDE)來隱藏對(duì)話框.在下次打開對(duì)話框時(shí)就不必調(diào)用
Create了,只需調(diào)用CWnd::ShowWindow(SW_SHOW)來顯示對(duì)話框.這樣做的好處在于對(duì)話框中的數(shù)據(jù)可以保存下來,供以后使用.
由于擁有者窗口在被關(guān)閉時(shí)會(huì)調(diào)用DestroyWindow刪除每一個(gè)所屬窗口,故只要非模態(tài)對(duì)話框是自動(dòng)清除的,程序員就不必?fù)?dān)心對(duì)話框?qū)ο蟮膭h除問
題.