对话框应用
模态与非模态对话框
Windows对话框分为两类:模态对话框和非模态对话框。
- 模态对话框是这样的对话框,当它弹出后,本应用程序其他窗口将不再接受用户输入,只有该对话框响应用户输入,在对它进行相应操作退出后,其他窗口才能继续与用户交互。
- 非模态对话框则是,它弹出后,本程序其他窗口仍能响应用户输入。非模态对话框一般用来显示提示信息等。
模态对话框
弹出模态对话框
CMFCApplication2App类有个InitInstance()函数,在MFC应用程序框架分析中提到过此函数,不过那是单文档应用程序App类中的,函数体不太相同,但都是进行App类实例的初始化工作。
InitInstance()函数的后半部分有一段代码就是定义对话框对象并弹出对话框的,下面给出这段代码并加以注释:
CMFCApplication2Dlg dlg; // 定义对话框类CMFCApplication2Dlg的对象dlg
m_pMainWnd = &dlg; // 将dlg设为主窗口
INT_PTR nResponse = dlg.DoModal(); // 弹出对话框dlg,并将DoModal函数的返回值(退出时点击按钮的ID)赋值给nResponse
if (nResponse == IDOK) // 判断返回值是否为OK按钮
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
}
else if (nResponse == IDCANCEL) // 判断返回值是否为Cancel按钮
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
}
弹出对话框比较关键的一个函数,就是对话框类的DoModal()函数。CDialog::DoModal()函数的原型为:
virtual INT_PTR DoModal();
返回值:整数值,指定了传递给CDialog::EndDialog(该函数用于关闭对话框)的nResult参数值。如果函数不能创建对话框,则返回-1;如果出现其它错误,则返回IDABORT。
调用了它对话框就会弹出,返回值是退出对话框时所点的按钮的ID,比如,我们点了“退出”按钮,那么DoModal返回值为IDCANCEL。
创建模态对话框
- 构建对话框资源与控件资源模板:调整大小、位置以及显示文字大小等;
- 为对话框添加类(以及为变量添加变量等);
- 添加对话框类的头文件到需要处理的类中;
- 在需要呈现对话框的位置输入代码片段:
INT_PTR nRes; // 用于保存DoModal函数的返回值
CTipDlg tipDlg; // 构造对话框类CTipDlg的实例
nRes = tipDlg.DoModal(); // 弹出对话框
if (IDCANCEL == nRes) // 判断对话框退出后返回值是否为IDCANCEL,如果是则return,否则继续向下执行
return;
// 将各控件中的数据保存到相应的变量
UpdateData(TRUE);
// 将被加数和加数的加和赋值给m_editSum
m_editSum = m_addValue + m_editAddend;
// 根据各变量的值更新相应的控件,和的编辑框会显示m_editSum的值
UpdateData(FALSE);
非模态对话框
模态对话框和非模态对话框在创建对话框资源和生成对话框类上是没有区别的。
策略:将对话框的对象设定为类中的数据成员,进行动态的创建和删除。
创建步骤:
- 构建对话框资源与控件资源模板:调整大小、位置以及显示文字大小等;
- 为对话框添加类(以及为变量添加变量等);
- 添加对话框类的头文件到需要处理的类的头文件中,并创建对话框类的对象指针变量(设定为私有),在构造函数中将指针变量初始化为NULL;
- 在需要呈现非模态对话框的函数中添加以下代码片段:
// 如果指针变量m_pTipDlg的值为NULL,则对话框还未创建,需要动态创建
if (NULL == m_pDlgTip)
{
// 创建非模态对话框实例
m_pDlgTip = new CTipDlg();
m_pDlgTip->Create(IDD_TIPS, this);
}
// 显示非模态对话框
m_pDlgTip->ShowWindow(SW_SHOW);
- 在处理的类的析构函数中动态删除对话框指针变量(析构函数手动添加)
CMFCApplication2Dlg::~CMFCApplication2Dlg()
{
// 如果非模态对话框已经创建则删除它
if (NULL != m_pDlgTip)
{
// 删除非模态对话框对象
delete m_pDlgTip;
}
}
属性页对话框
属性页对话框想必大家并不陌生,XP系统中桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面。另外,我们在创建MFC工程时使用的向导对话框也属于属性页对话框,它通过点击“Next”等按钮来切换页面。
属性页对话框就是包含一般属性页对话框和向导对话框两类。它将多个对话框集成于一身,通过标签或按钮来切换页面。
使用属性页对话框时,用到的类主要有两个:CPropertyPage类和CPropertySheet类。
- CPropertyPage类
CPropertyPage类继承自CDialog类,它被用于处理某单个的属性页,所以要为每个属性页都创建一个继承自CPropertyPage的子类。- 构造函数:三个CProperty类的构造函数,函数原型为:
CPropertyPage( );
explicit CPropertyPage(
UINT nIDTemplate,
UINT nIDCaption = 0,
DWORD dwSize = sizeof(PROPSHEETPAGE)
);
explicit CPropertyPage(
LPCTSTR lpszTemplateName,
UINT nIDCaption = 0,
DWORD dwSize = sizeof(PROPSHEETPAGE)
);
第一个是没有任何参数的构造函数。
第二个构造函数中,参数nIDTemplate是属性页的对话框资源ID,参数nIDCaption是属性页对话框选项卡的标题所用字符串资源的ID,若设为0,则选项卡标题就使用该属性页的对话框资源的标题。
第三个构造函数中,参数lpszTemplateName为属性页的对话框资源的名称字符串,不能为NULL。参数nIDCaption同上。
* CancelToClose()函数
* SetModified()函数
* 可重载函数:
OnApply:处理属性页的“Apply”按钮被单击的消息
OnCancel:处理属性页的“Cancel”按钮被单击的消息
OnKillActive:处理属性页当前活动状态被切换的消息,常用于数据验证
OnOK:处理属性页的“OK”按钮、“Apply”按钮或者“Close”按钮被单击的消息
OnQueryCancel:处理属性页的“Cancel”按钮被单击前发出的消息
OnReset:处理属性页的“Reset”按钮被单击的消息
OnSetActive:处理属性页被切换为当前活动页的消息
OnWizardBack:处理属性页的“Back”按钮被单击的消息,仅在向导对话框中有效
OnWizardFinish:处理属性页的“Finish”按钮被单击的消息,仅在向导对话框中有效
OnWizardNext:处理属性页的“Next”按钮被单击的消息,仅在向导对话框中有效
- CPropertySheet类
CPropertySheet类继承自CWnd类,它是属性表类,负责加载、打开或删除属性页,并可以在属性页对话框中切换属性页。它跟对话框类似,也有模态和非模态两种。- 构造函数
- GetActiveIndex()函数: 获取当前活动属性页的索引。
- GetActivePage()函数;获取当前活动属性页对象。
- GetPage()函数: 获取某个属性页对象。
- GetPageCount()函数:获取属性页的数量。
- GetPageIndex()函数:获取某属性页在属性页对话框中的索引。
- SetActivePage()函数:设置某个属性页为活动属性页。
- SetWizardButtons()函数:在向导对话框上启用或禁用Back、Next或Finish按钮,应在调用DoModal之前调用此函数。
PSWIZB_BACK 启用“Back”按钮,如果不包含此值则禁用“Back”按钮。
PSWIZB_NEXT 启用“Next”按钮,如果不包含此值则禁用“Next”按钮。
PSWIZB_FINISH 启用“Finish”按钮。
PSWIZB_DISABLEDFINISH 显示禁用的“Finish”按钮。 - SetWizardMode()函数:设置属性页对话框为向导对话框模式,应在调用DoModal之前调用此函数。
- SetTitle()函数:设置属性对话框的标题。
- AddPage()函数:为属性对话框添加新的属性页。
- PressButton()函数:模拟按下某指定的按钮。
- RemovePage()函数: 删除某属性页。
向导对话框
向导对话框的创建步骤:
- 创建属性页对话框资源
创建对话框资源,属性设置为child/thin/命名,并删除OK和CANCEL按钮。
注意在添加对话框类之前将资源的ID一定要设置好。 - 创建属性页类
为各个属性页类创建对话框属性页类,基类为CPorpertyPage。
设置下一步按钮代码:(重载OnSetActive函数后再函数体中添加)
CPropertySheet* psheet = (CPropertySheet*)GetParent();
// 设置属性表只有“下一步”按钮
psheet->SetWizardButtons(PSWIZB_NEXT);
CPropertySheet* psheet = (CPropertySheet*)GetParent();
//设置属性表只有“完成”按钮
psheet->SetFinishText(_T("完成"));
- 上面的代码段中,字符串“完成”前加了个_T,这是因为本工程创建的时候用的默认的Unicode字符集,而如果“完成”前不加_T就是ASCII字符串。_T实际上是一个宏,工程的字符集选择为Unicode时字符串就转为Unicode字符串,选择为Muli-Byte时就转为ASCII字符串。我们可以在Solution Explorer的Addition根节点上点右键,在右键菜单上选择“Properties”,弹出工程的属性对话框,Configuration Properties->General右侧列表中的Character Set就显示选择的字符集。
- 创建属性表类
属性页资源和属性页类创建完以后,还不能生成向导对话框,我们还需要一个属性表类,来容纳这些属性页。
- 链接: VS2019中MFC添加类
在属性表类中添加属性页类对象。
- 显示向导对话框
在实现的函数体中添加代码:
// 创建属性表对象
CAddSheet sheet(_T(""));
// 设置属性对话框为向导对话框
sheet.SetWizardMode();
// 打开模态向导对话框
sheet.DoModal();
一般属性页对话框
实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的。
一般属性页对话框的创建步骤:
- 创建属性页对话框资源
- 创建属性页类
属性页类的创建和向导对话框的属性页类也基本一样,只是一般属性页对话框中不需要“下一步”和“完成”等按钮。所以不需要下一步、完成等按钮的显示。 - 创建属性表类
- 显示一般属性页对话框
在实现的函数体中添加代码:
CSumSheet sheet(_T("使用说明"));
// 打开模态一般属性对话框
sheet.DoModal();
一般属性页对话框和向导对话框的创建和显示的不同包括:是否需要调用属性表类的SetWizardMode函数设置为向导对话框模式。
消息对话框
函数原型
我们在使用Windows系统的过程中经常会见到消息对话框,提示我们有异常发生或提出询问等。因为在软件开发中经常用到消息对话框,所以MFC提供了两个函数可以直接生成指定风格的消息对话框,而不需要我们在每次使用的时候都要去创建对话框资源和生成对话框类等。这两个函数就是CWnd类的成员函数MessageBox()和全局函数AfxMessageBox()。
- CWnd类的成员函数MessageBox()
int MessageBox(
LPCTSTR lpszText,
LPCTSTR lpszCaption = NULL,
UINT nType = MB_OK
);
- lpszText:需要显示的消息字符串。
- lpszCaption:消息对话框的标题字符串。默认值为NULL。取值为NULL时使用默认标题。
- nType:消息对话框的风格和属性。默认为MB_OK风格,即只有“确定”按钮。
nType的取值可以是下面两个表中任取一个值,也可以是各取一个值的任意组合。即可以指定一个对话框类型,也可以指定一个对话框图标,还可以两者都设定。
如果想要设置nType的值为类型和图标的组合,可以像这样取值:MB_OKCANCEL | MB_ICONQUESTION。按位取或就可以了。
- 全局函数AfxMessageBox()
AfxMessageBox()的函数原型为:
int AfxMessageBox(
LPCTSTR lpszText,
UINT nType = MB_OK,
UINT nIDHelp = 0
);
- lpszText:同CWnd::MessageBox()函数
- nType:CWnd::MessageBox()函数
- nIDHelp:此消息的帮助的上下文ID。默认值为0,取0时表示要使用应用程序的默认帮助上下文。
函数返回值
我们在调用了上面两个函数后,都可以弹出模态消息对话框。消息对话框关闭后,我们也都可以得到它们的返回值。两者的返回值就是用户在消息对话框上单击的按钮的ID,可以是以下值:
- IDABORT:单击“终止”按钮。
- IDCANCEL:单击“取消”按钮。
- IDIGNORE:单击“忽略”按钮。
- IDNO:单击“否”按钮。
- IDOK:单击“确定”按钮。
- IDRETRY:单击“重试”按钮。
- IDYES:单击“是”按钮。
调用
INT_PTR nRes;
// 显示消息对话框
nRes = MessageBox(_T("您确定要进行加法计算吗?"), _T("加法计算器"), MB_OKCANCEL | MB_ICONQUESTION);
// 判断消息对话框返回值。如果为IDCANCEL就return,否则继续向下执行
if (IDCANCEL == nRes)
return;
文件对话框
文件对话框分为打开文件对话框和保存文件对话框:
- 打开文件对话框用于选择要打开的文件的路径。
- 保存文件对话框用来选择要保存的文件的路径。
函数原型:
explicit CFileDialog(
BOOL bOpenFileDialog,
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = TRUE
);
- bOpenFileDialog:指定要创建的文件对话框的类型。设为TRUE将创建打开文件对话框,否则将创建保存文件对话框。
- lpszDefExt:默认的文件扩展名。如果用户在文件名编辑框中没有输入扩展名,则由lpszDefExt指定的扩展名将被自动添加到文件名后。默认为NULL。
- lpszFileName:文件名编辑框中显示的初始文件名。如果为NULL,则不显示初始文件名。
- dwFlags:文件对话框的属性,可以是一个值也可以是多个值的组合。关于属性值的定义,可以在MSDN中查找结构体OPENFILENAME,元素Flags的说明中包含了所有属性值。默认为OFN_HIDEREADONLY和OFN_OVERWRITEPROMPT的组合,OFN_HIDEREADONLY表示隐藏文件对话框上的“Read Only”复选框,OFN_OVERWRITEPROMPT表示在保存文件对话框中如果你选择的文件存在了,就弹出一个消息对话框,要求确定是否要覆盖此文件。
- lpszFilter:文件过滤器,它是由若干字符串对组成的一个字符串序列。如果指定了文件过滤器,则文件对话框中只有符合过滤条件的文件显示在文件列表中待选择。给大家看看VS MSDN中给出的一个例子:static TCHAR BASED_CODE szFilter[] = _T(“Chart Files (.xlc)|.xlc|Worksheet Files (.xls)|.xls|Data Files (.xlc;.xls)|.xlc; .xls|All Files (.)|.||”); 这样设置过滤器以后,文件对话框的扩展名组合框中将有四个选项:Chart Files (.xlc)、Worksheet Files (.xls)、Data Files(.xlc;.xls)和All Files (.),大家可以看到每种文件的扩展名规定都是一个字符串对,例如Chart Files的过滤字符串是Chart Files(.xlc)和.xlc成对出现的。
- pParentWnd:文件对话框的父窗口的指针。
- dwSize:OPENFILENAME结构体的大小。不同的操作系统对应不同的dwSize值。MFC通过此参数决定文件对话框的适当类型(例如,创建Windows 2000文件对话框还是XP文件对话框)。默认为0,表示MFC将根据程序运行的操作系统版本来决定使用哪种文件对话框。
- bVistaStyle:指定文件对话框的风格,设为TRUE则使用Vista风格的文件对话框,否则使用旧版本的文件对话框。此参数仅在Windows Vista中编译时适用。
文件对话框也是模态对话框,所以在打开时也需要调用CFileDialog类的DoModal()成员函数。在打开文件对话框中点了“打开”或者在保存文件对话框中点了“保存”以后,我们可以使用CFileDialog类的成员函数GetPathName()获取选择的文件路径。
下面列出几个CFileDialog类的成员函数,我们可以使用它们获得文件对话框中的各种选择。
- GetFileExt():获得选定文件的后缀名。
- GetFileName():获得选定文件的名称,包括后缀名。
- GetFileTitle():获得选定文件的标题,即不包括后缀名。
- GetFolderPath():获得选定文件的目录。
- GetNextPathName():获得下一个选定的文件的路径全名。
- GetPathName():获得选定文件的路径全名。
- GetReadOnlyPref():获得是否“以只读方式打开”。
- GetStartPosition():获得文件名列表中的第一个元素的位置。
void CExample17Dlg::OnBnClickedOpenButton()
{
// TODO: Add your control notification handler code here
// 设置过滤器
TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||");
// 构造打开文件对话框
CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, szFilter, this);
CString strFilePath;
// 显示打开文件对话框
if (IDOK == fileDlg.DoModal())
{
// 如果点击了文件对话框上的“打开”按钮,则将选择的文件路径显示到编辑框里
strFilePath = fileDlg.GetPathName();
SetDlgItemText(IDC_OPEN_EDIT, strFilePath);
}
}
void CExample17Dlg::OnBnClickedSaveButton()
{
// TODO: Add your control notification handler code here
// 设置过滤器
TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|Word文件(*.doc)|*.doc|所有文件(*.*)|*.*||");
// 构造保存文件对话框
CFileDialog fileDlg(FALSE, _T("doc"), _T("my"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, this);
CString strFilePath;
// 显示保存文件对话框
if (IDOK == fileDlg.DoModal())
{
// 如果点击了文件对话框上的“保存”按钮,则将选择的文件路径显示到编辑框里
strFilePath = fileDlg.GetPathName();
SetDlgItemText(IDC_SAVE_EDIT, strFilePath);
}
}
字体对话框
字体对话框的作用是用来选择字体。我们也经常能够见到。MFC使用CFontDialog类封装了字体对话框的所有操作。字体对话框也是一种模态对话框。
CFontDialog类的构造函数:
CFontDialog(
LPLOGFONT lplfInitial = NULL,
DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS,
CDC* pdcPrinter = NULL,
CWnd* pParentWnd = NULL
);
- lplfInitial:指向LOGFONT结构体数据的指针,可以通过它设置字体的一些特征。LOGFONT指针,LOGFONT结构体中包含了字体的大部分特征,包括字体高度、宽度、方向、名称等等。
ypedef struct tagLOGFONT {
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;
- dwFlags:指定选择字体的一个或多个属性,详情可在MSDN中查阅。
- pdcPrinter:指向一个打印设备上下文的指针。
- pParentWnd:指向字体对话框父窗口的指针。
获取字体对话框中所选字体
以通过CFontDialog类的成员变量m_cf间接获得选定字体的CFont对象。m_cf是CHOOSEFONT类型的变量,CHOOSEFONT结构体定义如下:
typedef struct {
DWORD lStructSize;
HWND hwndOwner;
HDC hDC;
LPLOGFONT lpLogFont;
INT iPointSize;
DWORD Flags;
COLORREF rgbColors;
LPARAM lCustData;
LPCFHOOKPROC lpfnHook;
LPCTSTR lpTemplateName;
HINSTANCE hInstance;
LPTSTR lpszStyle;
WORD nFontType;
INT nSizeMin;
INT nSizeMax;
} CHOOSEFONT, *LPCHOOSEFONT;
- CHOOSEFON结构体中有个成员lpLogFont,它是指向LOGFONT结构体变量的指针,就像上面所说,LOGFONT中包含了字体特征,例如,我们可以通过LOGFONT的lfFaceName得知字体名。
我们最终要获得的是所选择字体的CFont对象,有了字体的LOGFONT怎样获得对应的CFont对象呢?使用CFont类的成员函数CreateFontIndirect可以达到此目的。
BOOL CreateFontIndirect(const LOGFONT* lpLogFont );
- 参数是LOGFONT指针类型,我们可以传入CFontDialog类成员变量m_cf的lpLogFont成员,就可以得到所选字体的CFont对象了。
选取字体样式并显示在编辑框中
CString strFontName; // 字体名称
LOGFONT lf; // LOGFONT变量
// 将lf所有字节清零
memset(&lf, 0, sizeof(LOGFONT));
// 将lf中的元素字体名设为“宋体”
_tcscpy_s(lf.lfFaceName, LF_FACESIZE, _T("宋体"));
// 构造字体对话框,初始选择字体名为“宋体”
CFontDialog fontDlg(&lf);
if (IDOK == fontDlg.DoModal()) // 显示字体对话框
{
// 如果m_font已经关联了一个字体资源对象,则释放它
if (m_font.m_hObject)
{
m_font.DeleteObject();
}
// 使用选定字体的LOGFONT创建新的字体
m_font.CreateFontIndirect(fontDlg.m_cf.lpLogFont);
// 获取编辑框IDC_FONT_EDIT的CWnd指针,并设置其字体
GetDlgItem(IDC_EDT_FONT)->SetFont(&m_font);
// 如果用户选择了字体对话框的OK按钮,则获取被选择字体的名称并显示到编辑框里
strFontName = fontDlg.m_cf.lpLogFont->lfFaceName;
SetDlgItemText(IDC_EDT_FONT, strFontName);
}
颜色对话框
颜色对话框大家肯定也不陌生,我们可以打开它选择需要的颜色,简单说,它的作用就是用来选择颜色。MFC中提供了CColorDialog类封装了颜色对话框的所有操作,我们可以通过它显示颜色对话框,并获取颜色对话框中选择的颜色。颜色对话框跟字体对话框一样,也是一种模态对话框。
CColorDialog(
COLORREF clrInit = 0,
DWORD dwFlags = 0,
CWnd* pParentWnd = NULL
);
- clrInit:默认选择颜色的颜色值,类型为COLORREF,实际上就是unsigned long类型。如果没有设置它的值,则默认为RGB(0,0,0),即黑色。
注:RGB(r,g,b)是宏,可以计算颜色值。括号中的三个值分别为红、绿、蓝分量的值。 - dwFlags:自定义颜色对话框功能和外观的属性值。详情可在MSDN中查阅。
- pParentWnd:颜色对话框的父窗口的指针。
获取取颜色对话框中所选颜色值
CColorDialog类的成员函数GetColor()能够很好的实现我们的要求。
GetColor()函数的原型为:
COLORREF GetColor( ) const;
它返回所选颜色的COLORREF值。
如何获得R、G、B各分量的值呢?
根据GetColor得到的COLORREF颜色值,通过使用GetRValue、GetGValue和GetBValue三个宏获得。GetRValue的语法形式为:
BYTE GetRValue(DWORD rgb);
参数rgb就是COLORREF颜色值,返回值即是R分量值。其他两个宏的形式与之类似。
COLORREF color = RGB(255, 0, 0); // 颜色对话框的初始颜色为红色
CColorDialog colorDlg(color); // 构造颜色对话框,传入初始颜色值
if (IDOK == colorDlg.DoModal()) // 显示颜色对话框,并判断是否点击了“确定”
{
color = colorDlg.GetColor(); // 获取颜色对话框中选择的颜色值
SetDlgItemInt(IDC_EDT_COLORRGB, color); // 在Color编辑框中显示所选颜色值
SetDlgItemInt(IDC_EDT_COLORR, GetRValue(color)); // 在R编辑框中显示所选颜色的R分量值
SetDlgItemInt(IDC_EDT_COLORG, GetGValue(color)); // 在G编辑框中显示所选颜色的G分量值
SetDlgItemInt(IDC_EDT_COLORB, GetBValue(color)); // 在B编辑框中显示所选颜色的B分量值
}