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方法

阅读更多
文章标签: VC
个人分类: VC++
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭