http://blog.sina.com.cn/s/blog_49cb42490100nm3n.html
在VC++中,開啟對話方塊一般是用DoModal()函數調用模態對話方塊,但是模態對話方塊只能在對彈出的當前子視窗進行操作,而不能對父視窗進行操作,也無法傳遞資料到父視窗中,根據筆者的研究發現,採用非模態對話方塊的模式可以很好的解決這一問題。
在VS2008中建立一個就有MFC應用程式的Project項目,在彈出的MFC應用程式嚮導中選擇“基於對話方塊”,取消“使用Unicode庫”,單擊完成。在“資源檢視”裡面添加一個對話方塊,預設ID為IDD_DIALOG1。
雙擊IDD_DIALOG1對話方塊,在彈出的MFC類嚮導中,類名填寫CSonDialog,基類選擇CDialog,單擊完成。這樣我們就將建立的IDD_DIALOG1關聯上一個基於CDialog的類了。
在父視窗上添加一個按鈕,雙擊,便可進入這個按鈕的訊息響應函數。在最上麵包含CSonDialog的標頭檔#include “SonDialog.h”。如果在訊息響應函數中寫入如下代碼:
CSonDialogSonWnd;
SonWnd.DoModal();
運行之後按下父視窗上的按鈕,可以發現彈出了IDD_DIALOG1,但是只能在IDD_DIALOG1上操作,無法操作父視窗。如果想要在彈出子視窗後還可以操作父視窗的話,需要採用非模態對話方塊的模式彈出子視窗。
MFC在CDialog類中有一個Create(UINT nIDTemplate, CWnd *pParentWnd =0),這個函數可以建立一個Dialog,其中參數nIDTemplate為需要建立的Dialog的ID。同時還有一個函數ShowWindow(int nCmdShow),用來顯示建立的這個Dialog。在訊息響應函數中寫入如下代碼:
CSonDialogSonWnd;
SonWnd.Create(IDD_DIALOG1);
SonWnd.ShowWindow(SW_SHOW);
運行之後按下父視窗上的按鈕發現視窗閃了一下,然後就消失了。這是因為對象SonWnd是一個局部對象,在運行完SonWnd.ShowWindow(SW_SHOW)這條語句之後便退出了訊息響應函數,因此SonWnd對象也就被銷毀了。如果想要退出訊息響應函數之後視窗依然存在,則需要將SonWnd定義為一個全域變數。因此在ProjectDlg.h中添加一個CSonDialog SonWnd的定義,同時由於VC++在編譯的時候先行編譯標頭檔,因此還需要在ProjectDlg.h中包含CSonDialog的標頭檔#include
“SonDialog.h”,這樣在ProjectDlg.cpp中,便可以把SonDialog.h刪掉了。然後在按鈕的訊息響應函數中添加如下代碼:
SonWnd.Create(IDD_DIALOG1);
SonWnd.ShowWindow(SW_SHOW);
我們發現IDD_DIALOG1被建立出來,並且一直保留著。但是還是無法和父視窗進行資料交流。根據尋找資料我們發現在C++中有一個指標很特別,它指向的是當前視窗,這個指標就是this指標。我們通過傳遞this指標來相互調用對方的資料。
在CSonDialog類中,我們添加一個指向父視窗的全域指標變數CProjectDlg *m_pFaher,同時添加一個函數WndCreate(CProjectDlg *pParent),代碼如下:
voidCSonDialog::WndCreate(CProjectDlg *pParent)
{
Create(IDD_DIALOG1); //建立對話方塊
ShowWindow(SW_SHOW); //顯示對話方塊
m_pFather = pParent; //將父視窗指標傳遞進來
}
這個函數中調用了CDialog類中的Create()和ShowWindow()函數來建立和顯示對話方塊,同時採用參數傳遞的辦法將父視窗的指標傳遞到子視窗中。而在父視窗ProjectDlg.cpp的訊息響應函數中,我們添加如下代碼:
SonWnd.WndCreate(this);
編譯運行之後發現有錯,因為在ProjectDlg.h的標頭檔中包含了SonDialog.h,而在SonDialog.h中又包含了ProjectDlg.h,這樣程式在進行編譯的時候就會出現標頭檔重複包含的錯誤,有兩種辦法可以解決此問題。
第一種辦法是在兩個標頭檔中分別加入先行編譯命令#ifndef #define #endif命令,在SonDialog.h最上面加入
#ifndefSONDIALOG
#defineSONDIALOG
最下面加入
#endif
在ProjectDlg.h最上面加入
#ifndefRPOJECTDLG
#definePROJECTDLG
最下面加入
#endif
以上語句塊的意識是如果SONDIALOG/PROJECTDLG沒有被定義的話,那麼就定義SONDIALOG/PROJECTDLG,如果SONDIALOG/PROJECTDLG被定義的話,直接跳轉到#endif,這樣就可以很好的避免被重複定義的情況。這種方法我在以前編程的時候很好用,但是不知道為什麼最近幾次寫程式這種方法都失效了,於是我又想出了另外一種辦法。
第二種辦法的原理是採取避免在標頭檔中定義具體類型的指標變數,用定義null 指標的方法繞過標頭檔重複包含的問題。由於在父視窗中,指向子視窗的對象必須是全域變數,這樣才能保證子視窗在銷毀之前一直有顯示。因此在父視窗ProjectDlg.h中不得不包含SonDialog.h的標頭檔,這樣就只能在SonDialog.h中想辦法了。其實仔細想來我們發現在SonDialog.h中只要定義一個null 指標就可以解決問題。具體方法如下:
在SonDialog.h不包含ProjectDlg.h標頭檔,也不定義CProjectDlg的對象,而是定義一個null 指標LPVOIDm_pFather,將WndCreate()函數的參數改為LPVOID pPaernt,然後在WndCreate()函數中添加如下代碼:
voidCSonDialog::WndCreate(LPVOID pParent)
{
Create(IDD_DIALOG1); //建立對話方塊
ShowWindow(SW_SHOW); //顯示對話方塊
m_pFather = pParent; //將父視窗指標傳遞進來
}
這樣,父視窗的this指標傳遞進來之後到m_pFather還是一個指向任意對象的指標,只要在SonDialog.cpp的函數中需要調用父視窗中的函數或者是改動父視窗的某些變數時,在cpp檔案中包含標頭檔ProjectDlg.h,在函數開始時加入代碼:
CProjectDlg*Main;
Main = (CProjectDlg*)m_pFather; //強制將LPVOID類型轉換
Main->
就可以通過指標Main來對父視窗進行操作。這樣就可以實現兩個對話方塊中的資訊相互傳遞了。
另外在建立非模態對話方塊的時候要注意,重寫OnOk()和OnCancel()兩個函數,要在裡面加入DestoryWindow()函數,OnOk()和OnCancel()函數裡面並沒有銷毀視窗,而是使得視窗不可見,如果不銷毀視窗,在下一次再次開啟子視窗時,就會出現錯誤。
以上代碼在Windows 7家庭普通版+Visual Studio 2008SP1下運行通過。
==============
模態對話方塊傳遞參數
http://blog.csdn.net/xiaobai1593/article/details/6591893
父 傳給 子
void CDDDlg::OnBnClickedButton1(){// TODO: 在此添加控制項通知處理常式代碼CDlg2 dlg;dlg.strName="xipeng is a dog!!!";//CDlg2 中定義的public成員變數 dlg.DoModal();}
重寫CDlg2的 OnInitDialog函數。
在CDlg2中的建構函式初始化 strName
BOOL CDlg2::OnInitDialog(){CDialog::OnInitDialog();// TODO: 在此添加額外的初始化m_edit2.SetWindowText(strName);UpdateData(FALSE);return TRUE; // return TRUE unless you set the focus to a control// 異常: OCX 屬性頁面應返回 FALSE}
子傳給父
在子視窗中建立到父視窗的指標,然後給其成員變數賦值(在模態對話方塊中也可以實現)
void BDlg::OnOK() { // TODO: Add extra validation here ADlg * adlg=(ADlg *)this->GetParent(); adlg->m_edit="i hate u"; adlg->UpdateData(FALSE); CDialog::OnOK(); }
最佳解決方案
最佳方法:
是在看孫鑫的VC視頻的時候看到的,確實是高人呀!
由於非模態對話方塊的執行並不會阻塞主對話方塊的執行,所以大多數時候只能用模態對話方塊。
對話方塊在執行DoModal()函數後,返回的時候視窗被銷毀,但該對象仍然存在,所以仍然可以訪問其中的成員變數。
即可以在主對話方塊中直接存取模態對話方塊對象的成員變數,而不用非得在子對話方塊中擷取父對話方塊的指標來傳遞參數。
void ADlg::OnPopbtn() { if(updateDlg.DoModal()==IDOK) { CString sName=bdlg.m_sName; CString sID=bdlg.m_sID; } }
非模態對話方塊傳遞參數
由父視窗傳遞參數給子視窗
BDlg * pbdlg=new BDlg(); pbdlg->m_edit="i love u"; pbdlg->Create(IDD_DIALOG1, (CWnd *)this); pbdlg->ShowWindow(SW_SHOW);
注意:在使用非模態對話方塊時,如果用普通的變數,則該函數結束之後,變數的生存期就自動結束,所以視窗不會顯示出來。
解決方案只能用指標(記憶體空間在棧上),或成員變數來解決。
個人覺得用成員變數更合適些,因為用指標會涉及到銷毀問題,而解構函式並不知道該指標的地址,所以無法銷毀,從而造成記憶體的泄露。
代碼如下:
private: BDlg * m_pbdlg;
申明為指標便於動態進行構造,以免記憶體空間浪費。需要注意初始化。
if(m_pbdlg==NULL) m_pdlg=new BDlg(); m_pbdlg->m_edit="i love u"; m_pbdlg->Create(IDD_DIALOG1, (CWnd *)this); m_pbdlg->ShowWindow(SW_SHOW);
子傳給父
假設父視窗對應類為ADlg,子視窗對應類為BDlg。
void BDlg::OnOK() { ADlg * adlg=(ADlg *)this->GetParent(); adlg->m_edit="i hate u"; adlg->UpdateData(FALSE); DestroyWindow(); //CDialog::OnOK(); }
非模態對話方塊的一個問題在於,調用CDialog的OnOK()函數時,會隱藏該對話方塊,而非銷毀,所以需要調用DestoryWindow()函數來銷毀視窗自己。
====
模態對話方塊和非模態對話的OnOk
模態:調用時銷毀了對話方塊類
非模態:只是隱藏,沒有銷毀,需要重寫基類的OnOK虛函數,並且調用DestroyWindow函數。取消時,也要重寫OnCancel,然後在重寫函數中DestroyWindow函數,並且不能再調用基類的OnOK 和OnCancel
====
非模態對話方塊的建立
1、在父視窗中,或者建立模態對話方塊的類中定義一個成員變數CDlg dlg.
2、定義一個成員指標CDlg * pDlg,然後在解構函式中銷毀 delete pDlg;
3、定義局部變數,然後在非模態對話方塊重載PostNcDestroy虛函數(不是父類中的postNCDestroy),釋放this指標 delete this
注意,如果非模態對話方塊中有IDOK或IDCANCEL按鈕,需要重寫父類的函數,並且調用destroyWindow()方法銷毀,並且不再調用OnOK或OnCancel方法