VC 窗口創建以及窗口之間傳遞數據、傳遞消息(模態、非模態)

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,這樣就可以很好的避免被重複定義的情況。這種方法我在以前編程的時候很好用,但是不知道爲什麼最近幾次寫程序這種方法都失效了,於是我又想出了另外一種辦法。

第二種辦法的原理是採取避免在頭文件中定義具體類型的指針變量,用定義空指針的方法繞過頭文件重複包含的問題。由於在父窗口中,指向子窗口的對象必須是全局變量,這樣才能保證子窗口在銷燬之前一直有顯示。因此在父窗口ProjectDlg.h中不得不包含SonDialog.h的頭文件,這樣就只能在SonDialog.h中想辦法了。其實仔細想來我們發現在SonDialog.h中只要定義一個空指針就可以解決問題。具體方法如下:

在SonDialog.h不包含ProjectDlg.h頭文件,也不定義CProjectDlg的對象,而是定義一個空指針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方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章