首页 > 代码库 > MFC对话框

MFC对话框

MFC的对话框
   1 相关类
   CDialog类-父类是CWnd,本质上也是一个窗口。是对话框类的顶层父类。
   CCommonDialog类-父类是CDialog类。称为通用对话框。MFC提供了6个通用对话框类,都是CCommonDialog类的子类,
   包括:
   文件对话框、颜色对话框、字体对话框、查找替换对话框、打印对话框和打印设置对话框。
   CPropertyPage-父类是CDialog类,属性页对话框。
   2 分类
   模式对话框和非模式对话框
   3 使用MFC的类创建基于模式对话框的应用程序
     3.1 插入对话框资源,双击对话框资源创建对话框类,这样就完成了对话框类和对话框资源的关联
     3.2 创建和显示调用: CDialog::DoModal()
     3.3 如果需要在对话框关闭时做一些善后处理,可以重写虚函数
         CDialog::OnOK()/OnCancel()
   4 使用MFC的类创建基于非模式对话框的应用程序
     4.1 插入对话框资源,并与对话框类关联
     4.2 创建和显示与一般框架窗口类似    
     4.3 必须重写以下下函数,否则程序并没有退出
         重写OnOK和OnCancel函数,在函数中,调用DestroyWindow()函数

         // 如果需要做资源回收等善后处理工作可以
         重写PostNcDestroy()函数,在函数中,delete this;


编写测试程序:

新建一个Win32 Application,选择A Simple Win32 Application, 修改stdafx.h中的Windows.h为afxwin.h, 删除默认的主函数

编写如下测试代码:

// Dlg.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"
class CMyDlg : public CDialog
{
public:
	// 通过调用父类的构造函数来关联对话框资源
	CMyDlg():CDialog (IDD_DIALOG1){} 

	//重写虚函数做关闭时的善后处理
	virtual void OnOK ();
	virtual void OnCancel ();
	virtual void PostNcDestroy ();
};
/****************************
    用户点击了默认的"OK"按钮
******************************/
void CMyDlg::OnOK ()
{
	DestroyWindow ();
}
/*************************************
   用户点击了默认的"Cancel"或关闭按钮
**************************************/
void CMyDlg::OnCancel ()
{
	DestroyWindow ();	
}
/**************************************
   程序将要退出最后发出的消息
***************************************/
void CMyDlg::PostNcDestroy ()
{
   delete this;
}

class CMyWinApp : public CWinApp
{
public:
	virtual BOOL InitInstance ();
};
CMyWinApp theApp;
BOOL CMyWinApp::InitInstance ()
{
	// 创建和显示模式对话框
    /*
	CMyDlg dlg;
	m_pMainWnd = &dlg;
	dlg.DoModal ();
    */
 

	// 创建和显示非模式对话框
	CMyDlg *pDlg = new CMyDlg;
	pDlg->Create (IDD_DIALOG1);
	m_pMainWnd = pDlg;
	pDlg->ShowWindow (SW_SHOW);
	pDlg->UpdateWindow ();
	return TRUE;
}
而在MFC中创建对话框: 新建一个对话框资源后双击该对话框选择创建一个类,MFC自动会把我们创建的对话框资源和对话框类关联起来了,我们只需要定义一个我们生成的对话框类对象使用即可。

这里创建一个当文档应用程序,通过点击菜单项分别创建模式和非模式对话框,菜单响应函数如下:

// 包含对话框资源关联的类头文件
#include "DlgModal.h"
#include "DlgModaless.h"
/***********************************
    创建并显示模式对话框
*********************************/
void CMainFrame::OnModal() 
{
	CDlgModal dlg;
	if (IDOK == dlg.DoModal ())
		MessageBox ("执行相关操作");
	else
		MessageBox ("取消操作");
}
/**************************************
    创建并显示非模式对话框
***************************************/
void CMainFrame::OnModaless() 
{
	CDlgModaless *pDlg = new CDlgModaless;
	pDlg->Create (IDD_DIALOG_NOMODAL);
	pDlg->ShowWindow (SW_SHOW);
	pDlg->UpdateWindow ();
}

MFC中同样需要为非模式对话框添加处理OnOK、OnCancel、PostNcDestroy虚函数

注意:添加OnOK,OnCancel可以通过双击对话框资源的对应按钮来实现,PostNcDestroy通过右键天机虚函数实现

函数如下:

void CDlgModaless::OnOK() 
{
	// TODO: Add extra validation here
	CDialog::OnOK();
	DestroyWindow ();
}

void CDlgModaless::OnCancel() 
{
	// TODO: Add extra cleanup here
	CDialog::OnCancel();
	DestroyWindow ();
}

void CDlgModaless::PostNcDestroy() 
{
	// TODO: Add your specialized code here and/or call the base class
	CDialog::PostNcDestroy();
	delete this;
}


为什么模式对话框就不需要显示的重写OnOK/OnCancel/PostNcDestroy呢,为了搞清楚这个问题,下面分析DoMal()函数的执行过程,看看MFC为我们做了哪些事:

跟之前记得把MFC库改为静态库

void CMainFrame::OnModal() 
{
	CDlgModal dlg;
	dlg.DoModal ();
}
跟进:

OnModal-->DoModal
int CDialog::DoModal()
{
     .........................................................
	 // 超找对话并加载框资源
     HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
	 hDialogTemplate = LoadResource(hInst, hResource);
	if (hDialogTemplate != NULL)// 由于一个对话框类可以有多个对话框窗口,这里锁定是为了多线程的操作
		lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);

	
	if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
	{// 如果父窗口当前是可用的,那么设置为不可用
		::EnableWindow(hWndParent, FALSE);
		bEnableParent = TRUE;
	}

	......................................
    // create modeless dialog, 创建和显示非模式对话框
	if (CreateDlgIndirect(lpDialogTemplate,CWnd::FromHandle(hWndParent), hInst))
	........................................................
	// 进入对话框的消息循环
	VERIFY(RunModalLoop(dwFlags) == m_nModalResult);

     // hide the window before enabling the parent, etc.
	 // 点击OK、Cancel按钮后跳出上面的消息循环来到这里,隐藏对话框窗口
	  if (m_hWnd != NULL)
		SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);

   .......................................................

	if (bEnableParent) 
		::EnableWindow(hWndParent, TRUE);// 启用父窗口
	if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
		::SetActiveWindow(hWndParent);   // 设置父窗口为活动

	// destroy modal window
	// 销毁对话框,这里可以看到是MFC帮我们调用了这两个函数
	DestroyWindow();
	..................................................
    // unlock/free resources as necessary
	if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)
		UnlockResource(hDialogTemplate);// 解锁
	if (m_lpszTemplateName != NULL)
		FreeResource(hDialogTemplate);// 释放对话框资源

    return m_nModalResult;//返回用户点击的按钮对应的数值值
}


总结上面的流程如下:

DoModal()函数的执行过程
     1 查找和加载对话框资源
     2 将父窗口设置为不可用状态
     3 创建和显示对话框
     4 进入对话框的消息循环
     5 点击OK/Cancel/关闭按钮时,跳出循环,隐藏对话框窗口
     6 将父窗口设置为可用的和活动的状态
     7 销毁对话框窗口
     8 释放对话框资源
     9 函数执行结束,得到DoModal函数的返回值