本文整体目录和绝大部门内容来自 【鸡啄米网站】的MFC系列文章,欢迎支持原创
(一)VS2010/MFC编程入门之前言
-
VC++全称是Visual C++,是由微软提供的C++开发工具,它与C++的根本区别就在于,C++是语言,而VC++是用C++语言编写程序的工具平台
-
VC++不仅是一个编译器更是一个集成开发环境,包括编辑器、调试器和编译器等,一般它包含在Visual Studio中
-
因为VC++ 6.0以后的版本不再有独立的安装程序,所VC++ 6.0以后的版本不叫VC++ 7.0等等,而是用VC++所属的Visual Studio的版本名称代替,比如VS2003
VC++与MFC
MFC全称Microsoft Foundation Classes,也就是微软基础类库。它是VC++的核心,是C++与Windows API的结合,很彻底的用C++封装了Windows SDK(Software Development Kit,软件开发工具包)中的结构和功能,还提供了一个应用程序框架,此应用程序框架为软件开发者完成了一些例行化的工作,比如各种窗口、工具栏、菜单的生成和管理等,不需要开发者再去解决那些很复杂很乏味的难题,比如每个窗口都要使用Windows API注册、生成与管理。这样就大大减少了软件开发者的工作量,提高了开发效率。
(二)MFC向导生成MFC应用
(三)VS2017应用程序工程中文件的组成结构)
应该分析应用工序工程的组成结构。知道每个每个文件的作用是什么?
(四)MFC应用程序框架分析
一.SDK应用程序与MFC应用程序
Windows SDK开发程序就是不使用MFC类库,直接用Windows API函数进行软件开发
//本例是要显示“HELLO WORLD”字符串,UpdateWindow函数会发送WM_PAINT消息,但是此消息不经过消息队列而是直接送到窗口过程处理,在窗口过程函数中最终绘制了“HELLO WORLD”字符串
#include <windows.h>
LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
const static TCHAR appName[] = TEXT("Hello world");
WNDCLASSEX myWin;
myWin.cbSize = sizeof(myWin);
myWin.style = CS_HREDRAW | CS_VREDRAW;
myWin.lpfnWndProc = myWndProc;
myWin.cbClsExtra = 0;
myWin.cbWndExtra = 0;
myWin.hInstance = hInstance;
myWin.hIcon = 0;
myWin.hIconSm = 0;
myWin.hCursor = 0;
myWin.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
myWin.lpszMenuName = 0;
myWin.lpszClassName = appName;
//Register
if (!RegisterClassEx(&myWin)) return 0;
const HWND hWindow = CreateWindow(
appName,
appName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
hInstance,
0);
ShowWindow(hWindow,iCmdShow);
UpdateWindow(hWindow);
{
MSG msg;
while(GetMessage(&msg,0,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
}
LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg==WM_PAINT)
{
PAINTSTRUCT ps;
const HDC hDC = BeginPaint(hWindow,&ps);
RECT rect;
GetClientRect(hWindow,&rect);
DrawText(hDC,TEXT("HELLO WORLD"),-1,&rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hWindow,&ps);
return 0;
}
else if (msg==WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow,msg,wParam,lParam);
}
-
进入WinMain函数
-
初始化WNDCLASSEX,调用RegisterClassEx函数注册窗口类-
-
调用ShowWindow和UpdateWindow函数显示并更新窗口->进入消息循环。
Windows应用程序是消息驱动的,系统或用户让应用程序进行某项操作或完成某个任务时会发送消息,进入程序的消息队列,然后消息循环会将消息队列中的消息取出,交予相应的窗口过程处理,此程序的窗口过程函数就是myWndProc函数,窗口过程函数处理完消息就完成了某项操作或任务。
VS2017创建基于对话框的MFC
#include "pch.h"
#include "framework.h"
#include "MFCListCtrol.h"
#include "MFCListCtrolDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CMFCListCtrolApp
BEGIN_MESSAGE_MAP(CMFCListCtrolApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CMFCListCtrolApp 构造
CMFCListCtrolApp::CMFCListCtrolApp()
{
// 支持重新启动管理器
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的 CMFCListCtrolApp 对象
CMFCListCtrolApp theApp;
// CMFCListCtrolApp 初始化
BOOL CMFCListCtrolApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
CMFCListCtrolDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。
");
}
#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)
ControlBarCleanUp();
#endif
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}
两者运行过程对比
到此,通过对比可以发现,MFC应用程序的运行流程与SDK程序是类似的,都是先进行一些初始化过程,再注册并创建窗口,然后显示、更新窗口,最后进入消息循环,消息都由窗口过程函数处理
(五)MFC消息映射机制概述)
Windows应用程序是消息驱动的。在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息,通过对消息的处理实现相应的操作。比较典型的过程是,用户操作窗口,然后有消息产生,送给窗口的消息处理函数处理,对用户的操作做出响应。
1.消息
窗口消息一般由三个部分组成:
-
(1)一个无符号整数,是消息值;
-
(2)消息附带的WPARAM类型的参数;
-
(3)消息附带的LPARAM类型的参数。
其实我们一般所说的消息是狭义上的消息值,也就是一个无符号整数,经常被定义为宏。
2.消息映射机制
MFC使用一种消息映射机制来处理消息,在应用程序框架中的表现就是一个消息与消息处理函数一一对应的消息映射表,以及消息处理函数的声明和实现等代码。当窗口接收到消息时,会到消息映射表中查找该消息对应的消息处理函数,然后由消息处理函数进行相应的处理。SDK编程时需要在窗口过程中一一判断消息值进行相应的处理,相比之下MFC的消息映射机制要方便好用的多
3.Windows消息分类
Windows消息分为系统消息和用户自定义消息。
1. Windows系统消息分类
1.标准Windows消息。除WM_COMMAND外以WM_开头的消息是标准消息。例如,WM_CREATE、WM_CLOSE。
2.命令消息。消息名为WM_COMMAND,消息中附带了标识符ID来区分是来自哪个菜单、工具栏按钮或加速键的消息。
3.通知消息。通知消息一般由列表框等子窗口发送给父窗口,消息名也是WM_COMMAND,其中附带了控件通知码来区分控件。
CWnd的派生类都可以接收到标准Windows消息、通知消息和命令消息。命令消息还可以由文档类等接收。
2. 用户自定义消息
实际上就是用户定义一个宏作为消息,此宏的值应该大于等于WM_USER,然后此宏就可以跟系统消息一样使用,窗口类中可以定义它的处理函数。
4.消息映射表
除了一些没有基类的类或CObject的直接派生类外,其他的类都可以自动生成消息映射表
BEGIN_MESSAGE_MAP(CMFCListCtrolDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CMFCListCtrolDlg::OnLvnItemchangedList1)
END_MESSAGE_MAP()
在BEGIN_MESSAG_MAP和END_MESSAGE_MAP之间的内容成为消息映射入口项。消息映射除了在CMFCListCtrolDlg的实现文件中添加消息映射表外,在类的定义文件CMFCListCtrolDlg.h中还会添加一个宏调用:
DECLARE_MESSAGE_MAP()
一般这个宏调用写在类定义的结尾处。
5. 添加消息处理函数
如何添加消息处理函数呢?不管是自动还是手动添加都有三个步骤:
1.在类定义中加入消息处理函数的函数声明
//注意要以afx_msg打头
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
2.在类的消息映射表中添加该消息的消息映射入口项。
BEGIN_MESSAGE_MAP(CMFCListCtrolDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &CMFCListCtrolDlg::OnLvnItemchangedList1)
END_MESSAGE_MAP()
3.在类实现中添加消息处理函数的函数实现。
例如,CMFCListCtrolDlg.cpp中WM_PAINT的消息处理函数的实现:
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMFCListCtrolDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
上三个步骤以后,WM_CREATE等消息就可以在窗口类中被消息处理函数处理了
(六)对话框:创建对话框模板和修改对话框属性
创建对话框主要分两大步:
第一,创建对话框资源,主要包括创建新的对话框模板、设置对话框属性和为对话框添加各种控件;
对话框常用属性
1.ID:对话框ID,唯一标识对话框资源,可以修改。此处为IDD_ADDITION_DIALOG,我们不修改它。
2.Caption:对话框标题。此处默认为Addition,我们将其修改为“加法计算器”。
3.Border:边框类型。有四种类型:None、Thin、Resizing和Dialog Frame。我们使用默认的Dialog Frame。
4.Maximize:是否使用最大化按钮。我们使用默认的False。
5.Minimize:是否使用最小化按钮。同样我们使用默认的False。
6.Style:对话框类型。有三种类型:Overlapped(重叠窗口)、Popup(弹出式窗口)和Child(子窗口)。弹出式窗口比较常见。我们使用默认的Popup类型。
7.System Menu:是否带有标题栏左上角的系统菜单,包括移动、关闭等菜单项。我们使用默认的True。
8.Title Bar:是否带有标题栏。我们使用默认的True。
9.Font(Size):字体类型和字体大小。如果将其修改为非系统字体,则Use System自动改为False。而如果Use System原来为False,将其修改为True,则Font(Size)自动设置为系统字体。这里我们使用默认的系统字体。
第二,生成对话框类,主要包括新建对话框类、添加控件变量和控件的消息处理函数等
-
类的成员变量名一般以m_打头,以标识它是一个成员变量
-
数据交换机制
在AdditionDlg.cpp中CAdditionDlg的DoDataExchange()函数的函数体中多了三条DDX_Text调用语句
void CAdditionDlg::DoDataExchange(CDataExchange* pDX) { // 处理MFC默认的数据交换 CDialogEx::DoDataExchange(pDX); // 处理控件IDC_SUMMAND_EDIT和变量m_editSummand之间的数据交换 DDX_Text(pDX, IDC_SUMMAND_EDIT, m_editSummand); // 处理控件IDC_ADDEND_EDIT和变量m_editAddend之间的数据交换 DDX_Text(pDX, IDC_ADDEND_EDIT, m_editAddend); // 处理控件IDC_SUM_EDIT和变量m_editSum之间的数据交换 DDX_Text(pDX, IDC_SUM_EDIT, m_editSum); }
如果我们在程序运行界面中输入被加数,则通过CAddition的DoDataExchange()函数可以将输入的值保存到m_editSummand变量中,反之如果程序运行中修改了变量m_editSummand的值,则通过CAddition的DoDataExchange()函数也可以将新的变量值显示到被加数的编辑框中。
这种数据交换机制中,DoDataExchange()并不是被自动调用的,而是需要我们在程序中调用CDialogEx::UpdateData()函数,由UpdateData()函数再去自动调用DoDataExchange()的。
//CDialogEx::UpdateData()函数的原型为: BOOL UpdateData(BOOL bSaveAndValidate = TRUE); //参数:bSaveAndValidate用于指示数据传输的方向,TRUE表示从控件传给变量,FALSE表示从变量传给控件。默认值是TRUE,即从控件传给变量。 //返回值:CDialogEx::UpdateData()函数的返回值表示操作是否成功,成功则返回TRUE,否则返回FALSE。
(七)对话框:设置对话框控件的Tab顺序
打开“Resource View”视图,然后在资源中找到对话框IDD_ADDITION_DIALOG,双击ID后中间客户区域出现其模板视图。在主菜单中选择“Format”->"Tab Order",或者按快捷键Ctrl+D,对话框模板上就会显示各个控件的Tab顺序数字。
如下图:
上图中每个控件左上角都有一个数字,这就是它的Tab响应顺序。对话框刚打开时输入焦点就在Tab顺序为1的“退出”按钮上,不做任何操作按下Tab键,输入焦点就会转移到Tab顺序为2的“被加数”静态文本框上,但是因为静态文本框不接受任何输入,所以输入焦点继续自动转移到Tab顺序为3的被加数编辑框,再按Tab键,输入焦点又会转移到Tab顺序为4的“加数”静态文本框上,同样由于它是静态文本框,输入焦点不停留继续转移到加数编辑框,后面的控件同理。
我们认为这个顺序不合理,那怎么修改呢?很简单,从自己认为Tab顺序应该为1的控件开始依次单击,随着单击的完成,各控件的Tab响应顺序也按我们的想法设置好了。
九、属性页
-
桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面。另外,我们在创建MFC工程时使用的向导对话框也属于属性页对话框,它通过点击“Next”等按钮来切换页面。
属性页对话框就是包含一般属性页对话框和向导对话框两类。它将多个对话框集成于一身,通过标签或按钮来切换页面。
一般属性页对话框的创建步骤:
1.创建属性页对话框资源
属性页对话框资源的创建方法同向导对话框是一样的,上一讲中的对话框资源不需进行任何修改。
2.创建属性页类
属性页类的创建和向导对话框的属性页类也基本一样,只是一般属性页对话框中不需要“下一步”和“完成”等按钮,所以上一讲中属性页类的OnSetActive和OnWizardFinish等重载函数可以去掉。即CSummandPage类中的OnSetActive函数、CAddPage类中的OnSetActive函数和OnWizardFinish函数可以删除或注释掉。其他部分不需作任何修改。
3.创建属性表类
创建属性表类的过程同向导对话框属性表类也是一样的,所以上一讲中的CAddSheet类不需修改。
4.显示一般属性页对话框
上一讲向导对话框的显示是在OnBnClickedInstructButton函数中实现的,其中语句sheet.SetWizardMode();旨在设置属性表为向导对话框模式,所以显示一般属性页对话框时不需调用SetWizardMode成员函数。另外,我们可以将属性页对话框的标题设为“使用说明”,在构造属性表对象时将此字符串作为构造函数的参数传入。OnBnClickedInstructButton函数修改如下:
void CAdditionDlg::OnBnClickedInstructButton()
{
// 创建属性表对象
CAddSheet sheet(_T("使用说明"));
// 打开模态一般属性页对话框
sheet.DoModal();
}
九 对话框:消息对话框
Windows系统的过程中经常会见到消息对话框,提示我们有异常发生或提出询问等。因为在软件开发中经常用到消息对话框,所以MFC提供了两个函数可以直接生成指定风格的消息对话框,而不需要我们在每次使用的时候都要去创建对话框资源和生成对话框类等。
这两个函数就是CWnd类的成员函数MessageBox()和全局函数AfxMessageBox()。
int MessageBox(
LPCTSTR lpszText,
LPCTSTR lpszCaption = NULL,
UINT nType = MB_OK
);
int AfxMessageBox(
LPCTSTR lpszText,
UINT nType = MB_OK,
UINT nIDHelp = 0
);
判断消息对话框返回值
void CAdditionDlg::OnBnClickedAddButton()
{
INT_PTR nRes;
// 显示消息对话框
nRes = MessageBox(_T("您确定要进行加法计算吗?"), _T("加法计算器"), MB_OKCANCEL | MB_ICONQUESTION);
// 判断消息对话框返回值。如果为IDCANCEL就return,否则继续向下执行
if (IDCANCEL == nRes)
return;
// 将各控件中的数据保存到相应的变量
UpdateData(TRUE);
// 将被加数和加数的加和赋值给m_editSum
m_editSum = m_editSummand + m_editAddend;
// 根据各变量的值更新相应的控件。和的编辑框会显示m_editSum的值
UpdateData(FALSE);
// 设置属性对话框为向导对话框
//sheet.SetWizardMode();
}
十(对话框:文件对话框)
1. 文件对话框的分类
文件对话框分为打开文件对话框和保存文件对话框
例如,很多编辑软件像记事本等都有“打开”选项,选择“打开”后会弹出一个对话框,让我们选择要打开文件的路径,这个对话框就是打开文件对话框;
除了“打开”选项一般还会有“另存为”选项,选择“另存为”后往往也会有一个对话框弹出,让我们选择保存路径,这就是保存文件对话框。
打开文件对话框用于选择要打开的文件的路径,保存文件对话框用来选择要保存的文件的路径。
2. 文件对话框类CFileDialog
2.1构造
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:文件过滤器,它是由若干字符串对组成的一个字符串序列。如果指定了文件过滤器,则文件对话框中只有符合过滤条件的文件显示在文件列表中待选择
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()获取选择的文件路径。
2.2成员函数
GetFileExt() //获得选定文件的后缀名。
GetFileName()//获得选定文件的名称,包括后缀名。
GetFileTitle()//获得选定文件的标题,即不包括后缀名。
GetFolderPath()//获得选定文件的目录。
GetNextPathName()//获得下一个选定的文件的路径全名。
GetPathName()//获得选定文件的路径全名。
GetReadOnlyPref()//获得是否“以只读方式打开”。
GetStartPosition()//获得文件名列表中的第一个元素的位置。
2.3举例
添加两个编辑框,ID分别为IDC_OPEN_EDIT和IDC_SAVE_EDIT,再添加两个按钮,ID分别设为IDC_OPEN_BUTTON和IDC_SAVE_BUTTON,Caption分别设为“打开”和“保存”。
按钮IDC_OPEN_BUTTON用于显示打开文件对话框,编辑框IDC_OPEN_EDIT显示在打开文件对话框中选择的文件路径。
按钮IDC_SAVE_BUTTON用于显示保存文件对话框,编辑框IDC_SAVE_BUTTON显示在保存文件对话框中选择的文件路径。
分别为按钮IDC_OPEN_BUTTON和IDC_SAVE_BUTTON添加点击消息的消息处理函数OnBnClickedOpenButton()和OnBnClickedSaveButton()。
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);
}
}
十一(对话框:字体对话框)
1.创建一个基于对话框的MFC工程,名字为“Example18”。
2.在自动生成的主对话框IDD_EXAMPLE18_DIALOG的模板中,删除“TODO: Place dialog controls here.”静态文本框,添加一个按钮,ID设为IDC_FONT_BUTTON,Caption设为“字体选择”,用于显示字体对话框来选择字体,再添加一个编辑框,ID设为IDC_FONT_EDIT,用来以所选字体显示字体名字符串。
3.在Example18Dlg.h中为CExample18Dlg类添加private成员变量:CFont m_font;,用来保存编辑框中选择的字体。
4.为按钮IDC_FONT_BUTTON添加点击消息的消息处理函数CExample18Dlg::OnBnClickedFontButton()。
5.修改消息处理函数CExample18Dlg::OnBnClickedFontButton()如下:
void CExample18Dlg::OnBnClickedFontButton()
{
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_FONT_EDIT)->SetFont(&m_font);
// 如果用户选择了字体对话框的OK按钮,则获取被选择字体的名称并显示到编辑框里
strFontName = fontDlg.m_cf.lpLogFont->lfFaceName;
SetDlgItemText(IDC_FONT_EDIT, strFontName);
}
}
十二、(对话框:颜色对话框)
void CExample19Dlg::OnBnClickedColorButton()
{
// TODO: Add your control notification handler code here
COLORREF color = RGB(255, 0, 0); // 颜色对话框的初始颜色为红色
CColorDialog colorDlg(color); // 构造颜色对话框,传入初始颜色值
if (IDOK == colorDlg.DoModal()) // 显示颜色对话框,并判断是否点击了“确定”
{
color = colorDlg.GetColor(); // 获取颜色对话框中选择的颜色值
SetDlgItemInt(IDC_COLOR_EDIT, color); // 在Color编辑框中显示所选颜色值
SetDlgItemInt(IDC_R_EDIT, GetRValue(color)); // 在R编辑框中显示所选颜色的R分量值
SetDlgItemInt(IDC_G_EDIT, GetGValue(color)); // 在G编辑框中显示所选颜色的G分量值
SetDlgItemInt(IDC_B_EDIT, GetBValue(color)); // 在B编辑框中显示所选颜色的B分量值
}
}