1. 接下来所演示的DlgDemo1程序将演示基本的模态对话框的使用:
1) 该程序是基于视图的MFC程序;
2) 视图左上角将显示一个粉红色的矩形;
3) 菜单“文件”中有一个Options菜单项,点击后可以打开一个对话框;
4) 对话框中可以输入想设定的矩形的宽和高(范围必须在1 ~ 128内);
5) 还可以选择显示模式(英寸、厘米、像素,是一组单选按钮);
6) 退出按钮就是默认的OK和Cancel;
7) 还有一个重置按钮Reset,点击后会把宽、高、显示模式重置为默认值(4、2和英寸),但是不关闭对话框,仅仅是控件中的数据发生变化;
8) OK保存后退出对话框,视图中的矩形将会按照用户在对话框中的设置重新显示;
!!工程建立的步骤还是和之前的一样,只不过需要添加一个类COptionsDlg类,以及相应的文件OptionsDlg.h和OptionsDlg.cpp
2. 资源脚本CD了刚Demo1.rc:
1) 菜单项Options的定义:
IDR_MAINFRAME MENU PRELOAD DISCARDABLE
BEGIN
POPUP "文件(&F)"
BEGIN
MENUITEM "退出(&X)", ID_APP_EXIT
MENUITEM "&Options...", ID_FILE_OPTIONS
END
!!注意,必须加...表示需要用户进一步输入才能进行下一步响应;
2) COptionsDlg的框架窗口口的定义:
IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 192, 121
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Options"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "&Width", IDC_STATIC, 16, 16, 28, 8
EDITTEXT IDC_WIDTH, 44, 12, 64, 12, ES_AUTOHSCROLL
LTEXT "&Height", IDC_STATIC, 16, 36, 28, 8
EDITTEXT IDC_HEIGHT, 44, 32, 64, 12, ES_AUTOHSCROLL
GROUPBOX "Units", IDC_STATIC, 16, 56, 92, 52
CONTROL "&Inches", IDC_INCHES, "Button", BS_AUTORADIOBUTTON |
WS_GROUP, 28, 68, 52, 8
CONTROL "&Centimeters", IDC_CENTIMETERS, "Button",
BS_AUTORADIOBUTTON, 28, 80, 52, 8
CONTROL "&Pixels", IDC_PIXELS, "Button", BS_AUTORADIOBUTTON,
28, 92, 52, 8
DEFPUSHBUTTON "OK", IDOK, 128, 12, 50, 14, WS_GROUP
PUSHBUTTON "Cancel", IDCANCEL, 128, 32, 50, 14, WS_GROUP
PUSHBUTTON "&Reset", IDC_RESET, 128, 52, 50, 14, WS_GROUP
END
3. CMainFrame的定义和过去一样,只需要自己再添加一个OnCreate函数即可(注意消息映射也需要添加一下);
4. CChildView的定义:
1) .h文件中添加成员数据和Options菜单项的消息映射:
protected:
// 在视图中定义相应的矩形宽、高以及显示模式,作为和对话框交换数据的基础
int m_nWidth;
int m_nHeight;
int m_nUnits; // 0是英尺,1是厘米,2是像素
//{{AFX_MSG(CChildView)
afx_msg void OnPaint();
afx_msg void OnFileOptions(); // 菜单项命令处理程序
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
2) .cpp文件中的定义:OnPaint和OnFileOptions
// ChildView.cpp : implementation of the CChildView class
//
#include "stdafx.h"
#include "CDlgDemo1.h"
#include "ChildView.h"
#include "OptionsDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CChildView
CChildView::CChildView() // 初始化要显示的矩形的相关参数
{
m_nWidth = 4;
m_nHeight = 2;
m_nUnits = 0; // 初始化为英尺比例显示
}
CChildView::~CChildView()
{
}
BEGIN_MESSAGE_MAP(CChildView,CWnd )
//{{AFX_MSG_MAP(CChildView)
ON_WM_PAINT()
ON_COMMAND(ID_FILE_OPTIONS, OnFileOptions)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CChildView message handlers
BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOW+1), NULL);
return TRUE;
}
void CChildView::OnPaint() // 矩形信息由对话框传送而来,重画只要一句视图的矩形信息即可
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CBrush brush(RGB(255, 0, 255));
CBrush* pOldBrush = dc.SelectObject(&brush);
switch (m_nUnits)
{
case 0: // 英尺比例显示
dc.SetMapMode(MM_LOENGLISH);
dc.Rectangle(0, 0, m_nWidth * 100, -m_nHeight * 100);
break;
case 1: // 厘米显示比例
dc.SetMapMode(MM_LOMETRIC);
dc.Rectangle(0, 0, m_nWidth * 100, -m_nHeight * 100);
break;
case 2: // 像素显示
dc.SetMapMode(MM_TEXT);
dc.Rectangle(0, 0, m_nWidth, m_nHeight);
break;
}
dc.SelectObject(pOldBrush);
// Do not call CWnd::OnPaint() for painting messages
}
// 选中Options菜单项后创建相应对话框
// 并且视图窗口和对话框中的数据就在该函数中交换
void CChildView::OnFileOptions()
{
COptionsDialog dlg;
// 打开对话框之前先用视图中正确的数据初始化对话框中的数据
dlg.m_nWidth = m_nWidth;
dlg.m_nHeight = m_nHeight;
dlg.m_nUnits = m_nUnits;
if (dlg.DoModal() == IDOK) { // 用户相应完对话框后将数据回送至视图并重画视图中的矩形
m_nWidth = dlg.m_nWidth;
m_nHeight = dlg.m_nHeight;
m_nUnits = dlg.m_nUnits;
Invalidate(); // 无效导致重画
}
}
5. 对话框COptionsDlg的定义:
1) .h文件:
#if !defined(AFX_OPTIONSDIALOG_H__5F0F17AA_D0C2_4CCC_A53D_3A3CC1284F9B__INCLUDED_)
#define AFX_OPTIONSDIALOG_H__5F0F17AA_D0C2_4CCC_A53D_3A3CC1284F9B__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// OptionsDialog.h : header file
//
/////////////////////////////////////////////////////////////////////////////
// COptionsDialog dialog
class COptionsDialog : public CDialog
{
// Construction
public:
COptionsDialog(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(COptionsDialog)
enum { IDD = IDD_OPTIONS }; // COptionsDialog可以由ClassWizard自动生成
// 并且选择从逻辑上链接到.rc中定义的对话框模板上
// 逻辑上链接的方法就是定义IDD这个枚举值,之后可以在派生类的构造函数中作为
// 该类的数据成员传给构造函数作为参数(构造时需要用到对话框模板的ID)
// 三个成员变量,因为可以从ClassWizard中生成(并且是可以和控件交换数据的)
// 因此处于AFX_DATA注释宏的包围中,作为MFC自动生成的数据成员看待
int m_nHeight;
int m_nWidth;
int m_nUnits;
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(COptionsDialog)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(COptionsDialog)
// NOTE: the ClassWizard will add member functions here
afx_msg void OnReset(); // Reset按钮的处理程序
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_OPTIONSDIALOG_H__5F0F17AA_D0C2_4CCC_A53D_3A3CC1284F9B__INCLUDED_)
2) .cpp文件:
// OptionsDialog.cpp : implementation file
//
#include "stdafx.h"
#include "CDlgDemo1.h"
#include "OptionsDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// COptionsDialog dialog
COptionsDialog::COptionsDialog(CWnd* pParent /*=NULL*/)
: CDialog(COptionsDialog::IDD, // 之前定义的枚举值作为构造函数的默认参数,实在是巧妙而安全,因为该枚举值成为了成员变量!
pParent)
{
//{{AFX_DATA_INIT(COptionsDialog)
// 该初始值是MFC默认设定的,依据就是所这些数据成员所链接的控件
// 一般链接编辑框的int类型的数据成员都会初始化成0
// 链接单选按钮的int类型的数据成员都会初始化成-1,表示谁都没有选上
// 由于这些初始化都可以由MFC的ClassWizard自动生成,因此包含在AFX_DATA_INIT的注释宏中
m_nHeight = 0;
m_nWidth = 0;
m_nUnits = -1;
//}}AFX_DATA_INIT
// 但是问题就来了,我们定义宽和高的有效范围为1 ~ 128,而显示模式的有效范围为0 ~ 2,这个默认的初始化明显不符合要求
// 会直接在DDV中报错
// 但不过对话框中的数据是要和CChildView中相应的数据做交换的
// CChildView中创建对话框时会将CChildView中初始的在有效范围内的值(4, 2, 0)赋给对话框中的数据成员
// 而在关闭对话框后会将用户输入的对话框中的数据再回送到CChildView中
}
void COptionsDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(COptionsDialog)
DDX_Text(pDX, IDC_HEIGHT, m_nHeight);
DDV_MinMaxInt(pDX, m_nHeight, 1, 128);
DDX_Text(pDX, IDC_WIDTH, m_nWidth);
DDV_MinMaxInt(pDX, m_nWidth, 1, 128);
// 因为单选按钮在界面中是固定的
// 在界面中有几个就是几个,不可能选择最大个数以外的按钮
// 所以无需进行DDV校验
DDX_Radio(pDX, IDC_INCHES, m_nUnits);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(COptionsDialog, CDialog)
//{{AFX_MSG_MAP(COptionsDialog)
// NOTE: the ClassWizard will add message map macros here
ON_BN_CLICKED(IDC_RESET, OnReset)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COptionsDialog message handlers
// 复位按钮处理程序
void COptionsDialog::OnReset()
{ // 成员变量重置为默认值
m_nWidth = 4;
m_nHeight = 2;
m_nUnits = 0;
// 需要将重置后的值回显到控件中
UpdateData(FALSE);
}