[MFC]DlgDemo2程序:非模态对话框的基本应用

本文详细介绍了MFC中非模态对话框的创建方法,包括与模态对话框的区别,如关闭对话框需使用DestroyWindow,禁止使用基类的OnOK和OnCancel,以及对象必须在堆上分配。此外,还讨论了非模态对话框在消息循环中的处理和数据回送策略,并给出了一个具体的DlgDemo2程序设计案例,展示了如何在非模态对话框中实现数据的实时更新和关闭操作。
摘要由CSDN通过智能技术生成

1. 非模态对话框的创建方法以及和模态对话框的不同之处:

    1) 首先是创建非模态对话框使用的是CDialog::Create而不是CDialog::DoModal;

    2) 那么最大的问题就来了,Create是从CWnd继承而来的用来创建普通窗口用的函数,这就导致了关闭对话框时和模态对话框的大不相同!!

    3) 由于使用Create创建对话框窗口了,因此关闭它就也要和关闭普通窗口一样,必须使用DestroyWindow来关闭窗口,DestroyWindow会接着触发PostNcDestroy,这完全就和关闭普通窗口一模一样了,因此这里最大的区别就是模态对话框关闭必须靠EndDialog,而非模态对话框的关闭必须使用DestroyWindow;

    4) 那么这里就要特别强调了,再关闭非模态对话框的时候就绝对!绝对!不能使用基类CDialog的OnOK和OnCancel函数,因为基类的这两个函数底层都会默认调用EndDialog;

!!因此,再使用非模态对话框时如果要使用默认的IDOK和IDCANCEL按钮,就必须!必须!手动覆盖OnOK和OnCancel函数,并删去里面调用基类相应函数的语句,而剩下的数据回送(UpdateData(TRUE))函数也需要自己手动调用了,因为UpdateData(TRUE)是在基类的OnOK和OnCancel函数中调用的,由于删去了这两个函数的调用,UpdateData(TRUE)必须自己手写上去了;

    5) 非模态对话框的对象绝对不能建立在局部栈上,必须建立在堆上!!

         i. 原因是显而易见的,因为模态对话框一旦被激活显示,则其上层窗口将无法被激活而必须等待对话框处理完成并关闭后才能被重新激活,这就导致了直到关闭模态对话框DoModel才会返回值,因此在打开和关闭模态对话框中间的时间段里肯定不会做其它事情,所以模态对话框的对象完全可以建立在局部栈上,因为创建后立即使用,使用完立即就删除;

         ii. 而非模态对话框其实就跟普通窗口差不多,其被显示期间仍然可以激活其上层的父窗口,因此Create一定是立马返回值的,而不是要等待对话框关闭才返回值,否则程序就会卡在这个地方而无法实现重新激活其上层父窗口的功能了,因此非模态对话框在建立之后根本无法推测其什么时候才会关闭(这是和模态对话框最大的不同之处),在关闭之前可能还会利用上层的父窗口完成一些任务,所以非模态对话框的对象绝对不能建立在临时栈上,而必须建立在堆上(new),什么时候想关闭了(按下退出按钮)再临时删除(delete,所以必须在退出按钮的处理程序中使用delete删除对象);

!!一般标准的做法就是在OnCancel中调用DestroyWindow,然后在PostNcDestroy中使用delete删除对象(DestroyWindow会自动触发PostNcDestroy);

    6) 在Win32 API编程中,使用非模态对话框就必须修改消息循环,使用::IsDialogMessage将消息转发给相应的对话框,但是在MFC中这些都已经自动实现了,程序员完全不用关心这些细节;


!!关于CDialog::Create:

        a. 函数原型:BOOL CDialog::Create(UINT nIDTemplate, CWnd* pParent = NULL);

        b. 第一个参数是要绑定的对话框的资源ID,这里就会奇怪了,CDialog在构造函数创建的时候已经绑定过一次资源ID了,为什么这里还要绑定?没办法,就是要再绑定一次;

        c. 第二个参数是所属窗口的指针,如果为NULL就代表父窗口为主框架窗口;


2. DlgDemo2程序的设计:

    1) 功能完全和DlgDemo1一样,只不过不同之处在于,由于DlgDemo2使用的是非模态对话框,可以不关闭并使用上层父窗口,因此这里就要求按下OK按钮后对话框并不关闭,而是直接时上层的视图窗口中的矩形按照用户的输入更新并显示,只有点击Cancel才能关闭对话框;

    2) 因此这里的OK按钮改名为Apply(应用),而Cancel按钮改为Close(关闭),但是这些只是按钮上文本的改变,ID仍然使用MFC默认的IDOK和IDCANCEL;

    3) 由于对话框的对象要建立在堆上,因此就为视图类增加一个新的成员,即COptionsDlg* m_pDlg,在视图类的构造函数中初始化为NULL,然后在Options菜单项的处理函数中new出对象赋给m_pDlg,并且这里还有一个非常重要的陷阱要介绍:那就是非模态对话框打开时还可以玩转上层窗口,这就意味着此时仍能打开上层窗口中的菜单并再次点击Options菜单项,这不就重复建立了多个对话框了吗?所以在Options的处理函数中必须要进行逻辑判断,即m_pDlg非空的时候才能创建对话框,否则就不能(意味着已经打开了一个,不能再重复打开了);

    4) 剩下的最大的难处就是无法在Options的处理函数中将对话框的数据回送到视图类的相应数据中,因为非模态对话框的关闭时间点无法预测,所以Options中Create对话框后剩下的语句会立马顺序执行完,程序会立即退出处理函数,这中间的过程太快,用户根本来不及往对话框中输入数据,因此程序必须通过其它方式将数据回送至视图类:

         i. 思路是使用消息传递的方法进行回送;

         ii. 在OnOK中使用SendMessage函数将数据打包在一个结构中并通过lParam回送至MainFrame中,SendMessage必须通过AfxGetMainWnd调用,所以只能先回送到MainFrame中;

         iii. 接着在MainFrame的该消息的处理程序中利用m_wndView.SendMessage继续将消息传递给视图类;

         iv. 在视图类的相应的消息处理函数中将lParam中包裹的数据打开并复制给其数据成员完成对话框往视图类的数据回送全过程;

!!!同样,在关闭对话框时,在PostNcDestroy中不仅要delete掉自己(即delete this),也希望将m_pDlg也置空,但是m_pDlg是视图类的成员,因此在对话框内部无法访问它,所以还是需要通过消息传递的机制来完成该任务:

         i. 在PostNcDestroy中使用AfxGetMainWnd->SendMessage将关闭消息发送至MainFrame;

         ii. MainFrame再将消息继续传递给CChildView;

         iii. 最后再CChildView中就可以顺利地将自己的成员m_pDlg置为NULL了;

!!因此这里我们需要自创两个消息了,一个就是WM_USER_APPLY,还有一个就是WM_USER_DESTROY,我们可以利用MFC预定义好的WM_USER消息ID来定义它们,WM_USER是MFC规定的用户可以自定义的消息ID的下界,MFC标准规定用户可以自定义的消息ID的范围是0x0400 ~ 0x7FFF,而WM_USER就等于0x0400,因此我们可以定义WM_USER_APPLY为WM_USER + 0x0100等;

!!在给自定义消息ID命名时最好加上USER中缀,以表示该消息是一个用户自定义的消息;


3. COptions类的实现:

    1) .h:

#if !defined(AFX_OPTIONSDLG_H__525087D4_B252_423F_BB70_3F77BE9E4DD5__INCLUDED_)
#define AFX_OPTIONSDLG_H__525087D4_B252_423F_BB70_3F77BE9E4DD5__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// OptionsDlg.h : header file
//

/
// COptionsDlg dialog

class COptionsDlg : public CDialog
{
// Construction
public:
	COptionsDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
	//{
  {AFX_DATA(COptionsDlg)
	enum { IDD = IDD_OPTIONS };
	int		m_nHeight;
	int		m_nWidth;
	int		m_nUnits;
	//}}AFX_DATA


// Overrides
	// ClassWizard generated virtual function overrides
	//{
  {AFX_VIRTUAL(COptionsDlg)
	protected:
	virt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值