MFC 基于对话框程序完整教程(涵盖控件、对话框、线程、菜单)
前言
本教程基于 Visual Studio(推荐 2017/2019/2022),以「基于对话框的 MFC 应用程序」为核心,从项目创建到各类控件、对话框、线程、菜单的使用,进行分步详细讲解,适合 MFC 入门学习者,兼顾实用性和可操作性,所有示例均可直接编译运行。
目录
- 创建基于对话框程序
- MFC 对话框程序整体构造理解
- 控件界面创建(拖入与基本控制)
- 调试输出 ------AfxMessageBox 的使用
- Visual Studio 基于 MFC 的调试操作
- Button 控件的操作
- 菜单的创建
- 菜单的绑定
- 菜单的使用
- 认识模态对话框
- 创建模态对话框
- 认识非模态对话框
- 创建非模态对话框
- 认识多线程
- 如何创建 MFC 线程
- 线程间消息传递 ------PostMessage
- 线程间同步消息 ------SendMessage
- Picture Control 控件的使用
- Edit Control 的使用(补充序号,对应原文 20)
- Check Box 的使用(对应原文 21)
- List Box 的使用(对应原文 22)
- Combo Box 的使用(对应原文 23)
- Radio Button 的使用(对应原文 24)
- Slider Control 的使用(对应原文 25)
1. 创建基于对话框程序
操作步骤
- 打开 Visual Studio,点击「创建新项目」。
- 在模板列表中搜索「MFC」,选择「MFC 应用程序」,点击「下一步」。
- 填写项目名称(如「MfcDialogTutorial」),选择项目保存路径,点击「创建」。
- 进入 MFC 应用程序向导:
- 「应用程序类型」:选择「基于对话框」(默认已选中),取消「使用 Unicode 字符集」(新手推荐,避免字符转换问题,后续可修改为多字节字符集)。
- 「复合文档支持」:选择「无」。
- 「高级功能」:保持默认(可取消「ActiveX 控件」,简化项目)。
- 「生成的类」:保持默认(对话框类名为
CMfcDialogTutorialDlg,继承自CDialogEx)。
- 点击「完成」,Visual Studio 自动生成基于对话框的 MFC 项目。
项目结构说明
- 生成的项目包含「头文件」「源文件」「资源文件」三大目录。
- 核心文件:
MfcDialogTutorialDlg.h(对话框类头文件)、MfcDialogTutorialDlg.cpp(对话框类实现文件)、Resource.h(资源 ID 定义)、MfcDialogTutorial.rc(资源文件,包含对话框、控件、菜单等资源)。 - 默认生成一个空白对话框(
IDD_MFCDIALOGTUTORIAL_DIALOG),包含「确定」「取消」两个按钮。
2. MFC 对话框程序整体构造理解
MFC 基于对话框程序的核心是「对话框类 」+「资源 」+「消息映射机制」,整体构造可分为 3 个部分:
(1)核心类结构
-
入口函数:
WinMain(MFC 已封装,无需手动编写),程序启动后自动初始化应用程序对象,加载对话框资源。 -
应用程序类:
CMfcDialogTutorialApp(继承自CWinApp),负责应用程序的初始化、运行和退出,核心函数InitInstance()中会创建并显示主对话框。cpp
BOOL CMfcDialogTutorialApp::InitInstance() { // 初始化代码(MFC 自动生成) CMfcDialogTutorialDlg dlg; m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); // 显示模态主对话框 if (nResponse == IDOK) { // 「确定」按钮点击后的处理 } else if (nResponse == IDCANCEL) { // 「取消」按钮点击后的处理 } return FALSE; } -
对话框类:
CMfcDialogTutorialDlg(继承自CDialogEx),负责对话框的界面绘制、控件操作、消息响应,是开发者主要编写代码的类,UI线程类。
(2)资源体系
MFC 的资源存储在 .rc 文件中,通过「资源视图」(View → Resource View)查看和编辑,核心资源包括:
- 对话框资源(Dialog):如
IDD_MFCDIALOGTUTORIAL_DIALOG,用于设计界面。 - 控件资源:嵌入在对话框中的 Button、Edit 等控件,每个控件有唯一 ID(如
IDOK、IDC_BUTTON1)。 - 菜单资源(Menu):后续创建的菜单存储在此。
- 字符串资源(String Table):存储程序中使用的字符串常量。
#define IDM_ABOUTBOX 0x0010
#define IDD_ABOUTBOX 100
#define IDS_ABOUTBOX 101
#define IDD_MFCAPPLICATION1_DIALOG 102
(3)消息映射机制
MFC 采用「消息映射」将「窗口 / 控件事件」与「处理函数」绑定,核心流程:
- 控件 / 窗口触发事件(如按钮点击),系统发送对应消息(如
BN_CLICKED)。 - MFC 通过消息映射表,找到该消息对应的处理函数。
- 执行处理函数中的自定义代码。
消息映射表在对话框类的 .cpp 文件中,格式如下:(不要误操作导致其绑定到about 对话框中)
cpp
BEGIN_MESSAGE_MAP(CMfcDialogTutorialDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDOK, &CMfcDialogTutorialDlg::OnBnClickedOk) // 按钮点击消息映射
ON_BN_CLICKED(IDCANCEL, &CMfcDialogTutorialDlg::OnBnClickedCancel)
END_MESSAGE_MAP()
3. 控件界面创建 ------ 讲解控件的控制,拖入
(1)打开对话框设计器
- 切换到「资源视图」(若未显示,点击顶部菜单栏「视图」→「资源视图」)。
- 展开项目目录 →「Dialog」,双击
IDD_MFCDIALOGTUTORIAL_DIALOG,打开对话框设计界面。
(2)控件拖入与布局
- 显示「工具箱」(若未显示,点击「视图」→「工具箱」),工具箱中包含 MFC 所有常用控件(Button、Edit、Static Text 等)。
- 拖入控件:点击工具箱中的目标控件(如
Button),在对话框设计界面中点击 / 拖拽,即可创建控件。 - 控件基本操作:
- 移动:选中控件,按住鼠标左键拖拽。
- 调整大小:选中控件,拖动控件四周的控制点。
- 对齐 / 分布:选中多个控件(按住
Ctrl键点击),右键选择「对齐」「分布」,可实现控件整齐排列。 - 复制 / 删除:选中控件,使用
Ctrl+C/Ctrl+V复制,Delete键删除。
(3)控件属性设置
选中任意控件,右侧会显示「属性窗口」(若未显示,点击「视图」→「属性窗口」),核心属性配置:
- 「常规」→「ID」:控件的唯一标识(如
IDC_BUTTON_TEST),用于消息映射和控件操作,建议命名见名知意。 - 「常规」→「Caption」:控件的显示文本(如按钮上的文字「测试按钮」)。
- 「样式」:控件的外观和行为设置(如按钮是否默认、Edit 是否多行)。
- 「布局」:控件的位置、大小、是否随对话框缩放。
(4)控件的基本控制(手动修改属性示例)
- 拖入一个
Static Text控件,修改「Caption」为「MFC 控件教程」,修改「ID」为IDC_STATIC_TITLE。 - 拖入一个
Button控件,修改「Caption」为「点击测试」,修改「ID」为IDC_BUTTON_CLICK。 - 点击顶部菜单栏「生成」→「生成解决方案」,无报错则控件布局生效。
4. 如何进行调试输出 ------AfxMessageBox 的使用
AfxMessageBox 是 MFC 提供的简易消息框函数,用于调试输出、提示用户信息,无需创建窗口对象,可直接调用,是 MFC 入门调试的常用工具。
(1)函数原型
cpp
int AfxMessageBox(
LPCTSTR lpszText, // 消息框显示的文本内容
UINT nType = MB_OK, // 消息框类型(按钮+图标)
UINT nIDHelp = 0 // 帮助文档ID,默认0即可
);
(2)常用参数说明(nType)
| 参数值 | 说明 |
|---|---|
MB_OK |
仅显示「确定」按钮(默认) |
MB_OKCANCEL |
显示「确定」「取消」按钮 |
MB_YESNO |
显示「是」「否」按钮 |
MB_ICONINFORMATION |
显示信息图标(i) |
MB_ICONWARNING |
显示警告图标(!) |
MB_ICONERROR |
显示错误图标(×) |
(3)使用示例
-
打开对话框类
CMfcDialogTutorialDlg的.cpp文件,找到OnInitDialog()函数(对话框初始化时执行)。 -
在函数末尾(
return TRUE;之前)添加以下代码:cpp
// 示例1:简单提示信息 AfxMessageBox(_T("对话框初始化完成!")); // 示例2:带警告图标和确定/取消按钮 int nRet = AfxMessageBox(_T("是否继续执行?"), MB_YESNO | MB_ICONWARNING); if (nRet == IDYES) { AfxMessageBox(_T("用户选择「是」"), MB_ICONINFORMATION); } else if (nRet == IDNO) { AfxMessageBox(_T("用户选择「否」"), MB_ICONINFORMATION); } // 示例3:调试输出变量值 int nTest = 100; CString strMsg; strMsg.Format(_T("测试变量 nTest 的值为:%d"), nTest); // 格式化字符串 AfxMessageBox(strMsg, MB_OK | MB_ICONINFORMATION); -
点击「本地 Windows 调试器」运行程序,对话框初始化时会依次弹出消息框,查看输出结果。
(4)注意事项
- 多字节字符集下,使用
_T()宏包裹字符串(兼容后续字符集切换)。 AfxMessageBox是模态的,弹出后必须关闭才能继续执行后续代码。- 仅用于简易调试和提示,大量复杂调试需使用 Visual Studio 调试工具(后续讲解)。
5. Visual Studio 调试操作 ------ 基于 MFC
(1)调试前准备
- 将项目配置从「Release」切换为「Debug」(顶部菜单栏左侧,默认可能是 Release,Debug 模式支持断点、变量监视等功能)。
- 确保项目无编译错误(生成解决方案无报错)。
(2)核心调试操作 ------ 断点
- 设置断点:在代码行左侧的灰色区域点击,出现红色圆点(或快捷键
F9),程序运行到该行会暂停。- 示例:在
OnInitDialog()中的AfxMessageBox行设置断点。
- 示例:在
- 启动调试:点击「本地 Windows 调试器」(或快捷键
F5),程序运行到断点处暂停,界面变为调试模式。 - 断点处操作:
- 单步执行(
F10):执行当前行,不进入函数内部(跳过函数调用)。 - 逐语句执行(
F11):执行当前行,若为函数调用则进入函数内部(如进入AfxMessageBox底层实现)。 - 继续运行(
F5):跳过当前断点,运行到下一个断点或程序结束。 - 停止调试(
Shift+F5):终止当前调试进程。 - 移除断点:再次点击红色圆点(或
F9),或右键断点选择「删除断点」。
- 单步执行(
(3)变量监视
- 程序暂停在断点处时,选中要监视的变量(如
nTest),右键选择「添加监视」。 - 右侧弹出「监视」窗口,可查看变量的当前值、类型,且在单步执行时实时更新。
- 补充:可使用「即时窗口」(调试 → 窗口 → 即时),直接输入变量名或表达式(如
nTest+10),查看计算结果。
(4)MFC 专属调试技巧
- 查看控件状态:在断点处,可通过「监视窗口」输入
GetDlgItem(IDC_BUTTON_CLICK)->GetWindowText(strMsg),查看控件的显示文本。 - 断点条件:右键断点 →「条件」,输入条件表达式(如
nTest > 200),仅当条件满足时程序才会暂停在该断点。 - 命中次数:右键断点 →「命中次数」,设置命中次数(如 5),程序第 5 次运行到该断点时才会暂停。
6. Button 控件的操作
Button 控件(按钮)是 MFC 中最常用的控件,用于触发用户操作,核心是「绑定点击事件处理函数」。
(1)绑定点击事件(两种方法)
方法一:可视化绑定(推荐新手)
- 打开对话框设计器,双击
IDC_BUTTON_CLICK按钮(之前创建的「点击测试」按钮)。 - Visual Studio 自动在对话框类中生成点击事件处理函数
OnBnClickedButtonClick(),并自动添加消息映射。- 头文件(
.h)中声明函数:afx_msg void OnBnClickedButtonClick(); - 源文件(
.cpp)中生成空函数体,且消息映射表中添加:ON_BN_CLICKED(IDC_BUTTON_CLICK, &CMfcDialogTutorialDlg::OnBnClickedButtonClick)
- 头文件(
方法二:手动绑定(进阶)
- 头文件中声明处理函数:
afx_msg void OnBnClickedButtonClick(); - 源文件的消息映射表中添加:
ON_BN_CLICKED(IDC_BUTTON_CLICK, &CMfcDialogTutorialDlg::OnBnClickedButtonClick) - 源文件中实现函数体:
void CMfcDialogTutorialDlg::OnBnClickedButtonClick() { /* 自定义代码 */ }
(2)实现按钮点击功能示例
在 OnBnClickedButtonClick() 函数中添加以下代码,实现点击按钮后弹出消息框,并修改按钮文本:
cpp
void CMfcDialogTutorialDlg::OnBnClickedButtonClick()
{
// 1. 弹出提示消息
AfxMessageBox(_T("按钮被点击了!"), MB_OK | MB_ICONINFORMATION);
// 2. 修改按钮的显示文本
CWnd* pBtn = GetDlgItem(IDC_BUTTON_CLICK); // 通过控件ID获取按钮指针
if (pBtn != nullptr) // 校验指针有效性,避免空指针异常
{
CString strCurText;
pBtn->GetWindowText(strCurText); // 获取按钮当前文本
if (strCurText == _T("点击测试"))
{
pBtn->SetWindowText(_T("已点击")); // 设置新文本
}
else
{
pBtn->SetWindowText(_T("点击测试")); // 恢复原文本
}
}
// 3. 禁用/启用按钮(可选)
// pBtn->EnableWindow(FALSE); // 禁用按钮(灰色,无法点击)
// pBtn->EnableWindow(TRUE); // 启用按钮
}
(3)Button 控件常用属性与方法
| 方法 / 属性 | 说明 |
|---|---|
SetWindowText(LPCTSTR lpszText) |
设置按钮显示文本 |
GetWindowText(CString& rString) |
获取按钮当前显示文本 |
EnableWindow(BOOL bEnable) |
启用 / 禁用按钮(TRUE 启用,FALSE 禁用) |
SetIcon(HICON hIcon) |
给按钮设置图标 |
GetDlgItem(IDC_BUTTON_XXX) |
通过控件 ID 获取按钮 CWnd 指针 |
7. 菜单的创建
MFC 对话框程序默认无菜单,需手动创建菜单资源并关联到对话框。
(1)创建菜单资源
- 切换到「资源视图」,右键项目目录 →「添加」→「资源」。
- 在弹出的「添加资源」窗口中,选择「Menu」,点击「新建」。
- Visual Studio 自动创建一个空白菜单资源(ID 默认为
IDR_MENU1),并打开菜单设计器。 - 菜单设计操作:
- 顶部空白菜单项:点击后输入菜单名称(如「文件 (&F)」,
&F表示快捷键Alt+F)。 - 子菜单项:右键顶部菜单项 →「添加子菜单」,输入子菜单名称(如「新建 (&N)」「退出 (&X)」)。
- 分隔线:右键子菜单 →「插入分隔符」,用于分组子菜单。
- 修改菜单 ID:选中子菜单项(如「退出」),在右侧属性窗口中修改「ID」为
ID_MENU_FILE_EXIT(见名知意)。
- 顶部空白菜单项:点击后输入菜单名称(如「文件 (&F)」,
(2)菜单设计示例
创建一个简单菜单结构:
- 顶部菜单 1:「文件 (&F)」
- 子菜单:「新建 (&N)」(ID:
ID_MENU_FILE_NEW) - 分隔线
- 子菜单:「退出 (&X)」(ID:
ID_MENU_FILE_EXIT)
- 子菜单:「新建 (&N)」(ID:
- 顶部菜单 2:「帮助 (&H)」
- 子菜单:「关于 (&A)」(ID:
ID_MENU_HELP_ABOUT)
- 子菜单:「关于 (&A)」(ID:
(3)保存菜单资源
菜单设计完成后,点击「保存」,菜单资源自动存入 .rc 文件,可在「资源视图」的「Menu」目录下查看 IDR_MENU1。
8. 菜单的绑定
创建菜单资源后,需将其绑定到主对话框,才能在程序运行时显示菜单。
(1)绑定方法(两种)
方法一:对话框属性绑定(推荐)
- 打开对话框设计器,选中主对话框(
IDD_MFCDIALOGTUTORIAL_DIALOG)。 - 右侧属性窗口中,找到「常规」→「Menu」,下拉选择已创建的菜单资源
IDR_MENU1。 - 点击「生成解决方案」,运行程序,主对话框顶部会显示绑定的菜单。
方法二:手动代码绑定(进阶)
在对话框类的 OnInitDialog() 函数中添加以下代码,手动加载并绑定菜单:
cpp
BOOL CMfcDialogTutorialDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// ... 其他 MFC 自动生成的代码 ...
// 手动绑定菜单
HMENU hMenu = LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MENU1)); // 加载菜单资源
if (hMenu != nullptr)
{
SetMenu(hMenu); // 为对话框设置菜单
}
// 之前的 AfxMessageBox 示例代码 ...
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
(2)注意事项
- 若绑定后菜单未显示,检查菜单 ID 是否正确,且对话框属性中的「Menu」是否已选择对应菜单。
- 基于对话框的程序菜单默认显示在对话框顶部,与文档 / 视图结构的程序菜单显示在窗口顶部一致。
9. 菜单的使用
菜单绑定后,需为子菜单项绑定「点击事件处理函数」,才能实现菜单功能。
(1)绑定菜单点击事件(两种方法)
方法一:可视化绑定(推荐新手)
- 「资源视图」中展开「Menu」,双击
IDR_MENU1打开菜单设计器。 - 双击子菜单项(如「退出」),Visual Studio 自动生成处理函数
OnMenuFileExit(),并添加消息映射。
方法二:手动绑定(进阶)
- 头文件中声明处理函数:
afx_msg void OnMenuFileExit(); - 源文件消息映射表中添加:
ON_COMMAND(ID_MENU_FILE_EXIT, &CMfcDialogTutorialDlg::OnMenuFileExit) - 源文件中实现函数体。
(2)菜单功能实现示例
-
「退出」菜单功能:关闭对话框,退出程序 cpp
void CMfcDialogTutorialDlg::OnMenuFileExit() { // 发送 IDCANCEL 消息,关闭对话框 OnCancel(); // 等价于点击「取消」按钮 } -
「新建」菜单功能:弹出提示消息 cpp
void CMfcDialogTutorialDlg::OnMenuFileNew() { AfxMessageBox(_T("新建操作被触发!"), MB_OK | MB_ICONINFORMATION); } -
「关于」菜单功能:弹出关于对话框 cpp
void CMfcDialogTutorialDlg::OnMenuHelpAbout() { AfxMessageBox(_T("MFC 对话框教程 v1.0\n作者:MFC 学习者"), MB_OK | MB_ICONINFORMATION); }
(3)菜单常用附加操作
- 设置菜单勾选标记:
CheckMenuItem(ID_MENU_FILE_NEW, MF_CHECKED | MF_BYCOMMAND); - 禁用菜单项:
EnableMenuItem(ID_MENU_FILE_NEW, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND); - 移除菜单:
SetMenu(nullptr);
10. 认识模态对话框
模态对话框(Modal Dialog)是 MFC 中最常用的对话框类型,具有以下特点:
- 对话框弹出后,阻塞父窗口(主对话框)的所有操作,用户必须先关闭模态对话框,才能操作父窗口。
- 模态对话框具有自己的消息循环,独立于父窗口,但依赖父窗口存在。
- 常用场景:文件选择、参数设置、提示确认(如「关于」对话框、「保存文件」对话框)。
- 核心创建函数:
DoModal(),该函数返回对话框的关闭方式(IDOK或IDCANCEL)。
11. 创建模态对话框
(1)步骤 1:创建对话框资源
- 「资源视图」右键「Dialog」→「添加资源」→「Dialog」→「新建」,创建一个空白对话框资源(ID 默认为
IDD_DIALOG1)。 - 设计模态对话框界面:
- 修改对话框「Caption」为「关于本程序」。
- 拖入
Static Text控件,修改「Caption」为「MFC 对话框教程 v1.0\n 基于 Visual Studio 2019」。 - 保留默认的「确定」按钮(ID:
IDOK),删除「取消」按钮(模态对话框若无需取消,可删除)。
- 修改对话框 ID 为
IDD_ABOUT_DIALOG(见名知意)。
(2)步骤 2:创建对话框类
- 右键对话框设计器中的空白区域 →「添加类」。
- 弹出「MFC 类向导」,填写类名(如
CAboutDialog),基类选择CDialogEx,点击「完成」。 - Visual Studio 自动生成
CAboutDialog.h和CAboutDialog.cpp文件,包含对话框类的声明和实现。
(3)步骤 3:在主对话框中调用模态对话框
在「关于」菜单的处理函数 OnMenuHelpAbout() 中添加以下代码,创建并显示模态对话框:
cpp
void CMfcDialogTutorialDlg::OnMenuHelpAbout()
{
// 1. 创建模态对话框对象
CAboutDialog dlgAbout;
// 2. 显示模态对话框(DoModal() 阻塞,直到对话框关闭)
INT_PTR nRet = dlgAbout.DoModal();
// 3. 处理对话框关闭结果
if (nRet == IDOK)
{
AfxMessageBox(_T("关于对话框已确认关闭"), MB_OK | MB_ICONINFORMATION);
}
}
(4)运行效果
点击「帮助」→「关于」,弹出「关于本程序」对话框,此时主对话框无法操作,点击「确定」关闭模态对话框后,才能继续操作主对话框。
12. 认识非模态对话框
非模态对话框(Modeless Dialog)与模态对话框相反,具有以下特点:
- 对话框弹出后,不阻塞父窗口的操作,用户可同时操作父窗口和非模态对话框。
- 非模态对话框没有自己独立的消息循环,依赖父窗口的消息循环。
- 常用场景:工具窗口、属性窗口、实时监控窗口(如 VS 的「解决方案资源管理器」)。
- 核心创建步骤:
Create()(创建对话框)+ShowWindow(SW_SHOW)(显示对话框),无需调用DoModal()。 - 注意事项:非模态对话框对象需为堆内存分配(避免局部变量销毁导致对话框关闭),且需手动管理销毁。
13. 创建非模态对话框
(1)步骤 1:创建对话框资源(同模态对话框)
- 「资源视图」右键「Dialog」→「添加资源」→「Dialog」→「新建」,创建空白对话框资源(ID:
IDD_TOOL_DIALOG)。 - 设计界面:修改「Caption」为「工具窗口」,拖入一个
Static Text控件,修改「Caption」为「非模态对话框示例」。 - 保留「确定」「取消」按钮(ID 默认为
IDOK、IDCANCEL)。
(2)步骤 2:创建对话框类
- 右键对话框设计器空白区域 →「添加类」,类名:
CToolDialog,基类:CDialogEx,点击「完成」。 - 自动生成
CToolDialog.h和CToolDialog.cpp文件。
(3)步骤 3:主对话框中声明非模态对话框指针
在主对话框类 CMfcDialogTutorialDlg 的 .h 文件中添加成员变量(堆指针),用于保存非模态对话框对象:
cpp
// MfcDialogTutorialDlg.h
class CMfcDialogTutorialDlg : public CDialogEx
{
// ... 其他声明 ...
private:
CToolDialog* m_pToolDlg; // 非模态对话框指针
};
在主对话框类的构造函数中初始化指针为 nullptr:
cpp
// MfcDialogTutorialDlg.cpp
CMfcDialogTutorialDlg::CMfcDialogTutorialDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MFCDIALOGTUTORIAL_DIALOG, pParent)
, m_pToolDlg(nullptr) // 初始化指针
{
// ... 其他代码 ...
}
(4)步骤 4:创建并显示非模态对话框
添加一个按钮(ID:IDC_BUTTON_SHOW_MODELESS,Caption:「显示非模态对话框」),绑定点击事件,实现非模态对话框的创建和显示:
cpp
void CMfcDialogTutorialDlg::OnBnClickedButtonShowModeless()
{
// 1. 判断对话框是否已创建,避免重复创建
if (m_pToolDlg == nullptr)
{
// 2. 堆内存分配,创建非模态对话框
m_pToolDlg = new CToolDialog(this); // 父窗口为当前主对话框
if (m_pToolDlg != nullptr)
{
// 3. 创建对话框资源(非模态对话框核心:Create())
m_pToolDlg->Create(IDD_TOOL_DIALOG, this);
// 4. 显示对话框
m_pToolDlg->ShowWindow(SW_SHOW);
}
}
else
{
// 若已创建,直接显示(避免重复创建)
m_pToolDlg->ShowWindow(SW_SHOW);
}
}
(5)步骤 5:销毁非模态对话框
-
处理非模态对话框的「确定」「取消」按钮事件,关闭对话框并释放内存:打开
CToolDialog.cpp,双击「确定」按钮,生成处理函数:cpp
void CToolDialog::OnBnClickedOk() { // 隐藏对话框(非模态对话框不调用 CDialogEx::OnOk(),避免自动销毁) ShowWindow(SW_HIDE); // 若需彻底销毁,可通知父窗口释放内存 GetParent()->PostMessage(WM_COMMAND, IDC_BUTTON_CLOSE_MODELESS); }同理,处理「取消」按钮事件,逻辑与「确定」按钮一致。
-
主对话框退出时,释放非模态对话框内存(在析构函数或
OnCancel()中):cpp
CMfcDialogTutorialDlg::~CMfcDialogTutorialDlg() { // 释放非模态对话框内存 if (m_pToolDlg != nullptr) { m_pToolDlg->DestroyWindow(); // 销毁对话框资源 delete m_pToolDlg; m_pToolDlg = nullptr; } }
(6)运行效果
点击「显示非模态对话框」,弹出「工具窗口」,此时可同时操作主对话框和非模态对话框,点击非模态对话框的「确定」,对话框隐藏,再次点击按钮可重新显示。
14. 认识多线程
(1)线程的基本概念
线程是程序执行的最小单位,一个进程可以包含多个线程,所有线程共享进程的内存空间、资源。MFC 程序默认只有一个「主线程」(UI 线程),负责处理界面绘制、用户输入、消息循环。
(2)MFC 中的两种线程类型
- UI 线程(用户界面线程):拥有自己的消息循环,可创建和操作控件,适用于复杂的独立界面模块(如多窗口程序)。
- 工作线程(后台线程):无消息循环,仅用于执行后台任务(如数据计算、文件读写、网络通信),不能直接操作 UI 控件,适用于耗时操作(避免阻塞主线程导致界面卡死)。
(3)多线程的核心用途(MFC 场景)
- 执行耗时操作(如 TCP 数据接收、大文件解析),避免主线程阻塞,保证界面流畅。
- 并行处理多个独立任务(如同时监听多个网络端口、同时处理多个文件)。
(4)注意事项
- 工作线程不能直接操作 UI 控件(如修改 Edit 文本、点击 Button),否则会导致程序崩溃、界面卡死,需通过消息传递让主线程操作 UI。
- 多个线程访问共享资源时,需加锁(如
CCriticalSection),避免数据竞争(脏数据)。
15. 如何创建 MFC 线程
MFC 提供 AfxBeginThread() 函数创建线程,支持创建 UI 线程和工作线程,此处重点讲解工作线程(最常用)。
(1)工作线程创建步骤
- 定义线程函数(全局函数或静态成员函数,格式固定)。
- 调用
AfxBeginThread()启动线程,传递线程函数和参数。 - 实现线程函数的后台任务逻辑。
(2)线程函数格式要求
工作线程函数的返回值和参数格式固定,原型如下:
cpp
UINT ThreadProc(LPVOID pParam); // pParam:线程参数,LPVOID 可转换为任意类型
- 返回值:
UINT,线程退出码(通常返回 0 表示正常退出)。 - 参数:
LPVOID,传递给线程的自定义参数(可传递对话框指针、数据缓冲区等)。
(3)创建工作线程示例(后台计数任务)
-
在主对话框类的
.cpp文件中定义全局线程函数:cpp
// 工作线程函数:后台计数,每秒计数一次 UINT CountThreadProc(LPVOID pParam) { // 转换参数(此处传递主对话框指针) CMfcDialogTutorialDlg* pDlg = (CMfcDialogTutorialDlg*)pParam; if (pDlg == nullptr) { return 1; // 参数无效,线程退出 } // 后台计数逻辑 int nCount = 0; while (true) { nCount++; // 输出计数(不能直接操作 UI,后续通过消息传递更新 UI) TRACE(_T("当前计数:%d\n"), nCount); // TRACE 仅在 Debug 模式输出到输出窗口 // 延时 1 秒(避免 CPU 占用过高) Sleep(1000); // 终止线程条件(示例:计数到 10 退出) if (nCount >= 10) { break; } } return 0; // 线程正常退出 } -
添加一个按钮(ID:
IDC_BUTTON_START_THREAD,Caption:「启动后台线程」),绑定点击事件,调用AfxBeginThread()启动线程:cpp
void CMfcDialogTutorialDlg::OnBnClickedButtonStartThread() { // 启动工作线程 CWinThread* pThread = AfxBeginThread( CountThreadProc, // 线程函数 this, // 传递给线程的参数(主对话框指针) THREAD_PRIORITY_NORMAL, // 线程优先级(默认正常) 0, // 线程栈大小(0 表示使用默认大小) CREATE_SUSPENDED, // 线程创建标志(0 表示创建后立即运行,CREATE_SUSPENDED 表示暂停创建) nullptr // 安全属性(默认 nullptr) ); if (pThread != nullptr) { pThread->ResumeThread(); // 若创建时为暂停状态,需调用此函数启动线程 AfxMessageBox(_T("后台线程已启动!"), MB_OK | MB_ICONINFORMATION); } else { AfxMessageBox(_T("后台线程创建失败!"), MB_OK | MB_ICONERROR); } }
(4)运行效果
点击「启动后台线程」,弹出提示消息,此时主线程界面流畅,可正常操作,Debug 模式下在「输出窗口」中查看每秒递增的计数(1~10)。
16. 线程中怎么互相传递消息 ------PostMessage
PostMessage 是 MFC 中用于异步消息传递 的函数,工作线程通过 PostMessage 向主线程发送消息,主线程收到消息后处理(如更新 UI),不会阻塞工作线程,是线程间通信的首选方式。
(1)核心特点
- 异步发送:消息放入主线程消息队列后立即返回,工作线程继续执行后续代码,不等待主线程处理。
- 无返回值:无法获取主线程处理消息的结果。
- 安全可靠:避免工作线程直接操作 UI,由主线程处理 UI 更新。
(2)使用步骤
- 定义自定义消息(在主对话框类的
.h文件中)。 - 声明并实现消息处理函数。
- 绑定消息映射。
- 工作线程中调用
PostMessage发送自定义消息。
(3)示例:线程计数更新 UI(Edit 控件)
-
步骤 1:定义自定义消息在
MfcDialogTutorialDlg.h文件顶部添加:cpp
#define WM_UPDATE_COUNT (WM_USER + 100) // 自定义消息,WM_USER + 100 避免与系统消息冲突 -
步骤 2:声明消息处理函数在主对话框类的
.h文件中添加:cpp
afx_msg LRESULT OnUpdateCount(WPARAM wParam, LPARAM lParam); -
步骤 3:绑定消息映射在主对话框类的
.cpp文件的消息映射表中添加:cpp
BEGIN_MESSAGE_MAP(CMfcDialogTutorialDlg, CDialogEx) // ... 其他消息映射 ... ON_MESSAGE(WM_UPDATE_COUNT, &CMfcDialogTutorialDlg::OnUpdateCount) // 绑定自定义消息 END_MESSAGE_MAP() -
步骤 4:实现消息处理函数(更新 Edit 控件)拖入一个
Edit Control控件(ID:IDC_EDIT_COUNT,样式:只读),实现消息处理函数:cpp
LRESULT CMfcDialogTutorialDlg::OnUpdateCount(WPARAM wParam, LPARAM lParam) { // wParam:传递计数数值(示例) int nCount = (int)wParam; CString strCount; strCount.Format(_T("当前计数:%d"), nCount); SetDlgItemText(IDC_EDIT_COUNT, strCount); // 更新 Edit 控件(主线程中操作,安全) return 0L; } -
步骤 5:修改工作线程函数,发送自定义消息 cpp
UINT CountThreadProc(LPVOID pParam) { CMfcDialogTutorialDlg* pDlg = (CMfcDialogTutorialDlg*)pParam; if (pDlg == nullptr) { return 1; } int nCount = 0; while (true) { nCount++; // 发送自定义消息给主线程,传递计数数值(通过 wParam 传递) pDlg->PostMessage(WM_UPDATE_COUNT, (WPARAM)nCount, 0); Sleep(1000); if (nCount >= 10) { // 发送计数完成消息 pDlg->PostMessage(WM_UPDATE_COUNT, 0, 0); break; } } return 0; }
(4)运行效果
点击「启动后台线程」,Edit 控件中每秒更新计数(1~10),主线程界面流畅,无卡顿,计数到 10 后停止更新。
17. SendMessage
SendMessage 是 MFC 中用于同步消息传递 的函数,工作线程通过 SendMessage 向主线程发送消息,主线程处理完消息后,工作线程才继续执行后续代码。
(1)核心特点
- 同步发送:阻塞工作线程,等待主线程处理完消息并返回结果后,才继续执行。
- 有返回值:可获取主线程处理消息的结果。
- 慎用场景:若主线程处理消息耗时较长,会导致工作线程阻塞,影响后台任务执行。
(2)与 PostMessage 的核心区别
| 特性 | PostMessage | SendMessage |
|---|---|---|
| 发送方式 | 异步 | 同步 |
| 阻塞性 | 不阻塞发送线程 | 阻塞发送线程,等待接收线程处理完成 |
| 返回值 | 无返回值(BOOL 仅表示消息是否放入队列) | 有返回值(LRESULT,接收线程处理结果) |
| 适用场景 | 线程间普通通信、UI 更新(推荐) | 需获取接收线程处理结果的场景(慎用) |
(3)使用示例(获取 UI 控件文本)
修改工作线程函数,使用 SendMessage 获取 Edit 控件的文本:
cpp
UINT CountThreadProc(LPVOID pParam)
{
CMfcDialogTutorialDlg* pDlg = (CMfcDialogTutorialDlg*)pParam;
if (pDlg == nullptr)
{
return 1;
}
int nCount = 0;
while (true)
{
nCount++;
// 异步更新 UI(PostMessage)
pDlg->PostMessage(WM_UPDATE_COUNT, (WPARAM)nCount, 0);
// 同步获取 Edit 控件文本(SendMessage)
CString strEditText;
CWnd* pEdit = pDlg->GetDlgItem(IDC_EDIT_COUNT);
if (pEdit != nullptr)
{
// 发送 WM_GETTEXT 消息,获取控件文本
int nTextLen = pEdit->SendMessage(WM_GETTEXT, (WPARAM)100, (LPARAM)strEditText.GetBuffer(100));
strEditText.ReleaseBuffer(nTextLen);
TRACE(_T("Edit 控件文本:%s\n"), strEditText);
}
Sleep(1000);
if (nCount >= 10)
{
break;
}
}
return 0;
}
(4)运行效果
工作线程每秒发送 PostMessage 更新 UI,同时通过 SendMessage 同步获取 Edit 控件文本并输出到「输出窗口」,工作线程会等待 SendMessage 返回后,才执行后续的 Sleep 操作。
18. Picture Control 控件的使用
Picture Control 控件用于显示图片、图标、矩形等,支持 BMP、ICO 等格式,核心属性是「Type」(显示类型)。
(1)核心属性设置
- 拖入
Picture Control控件(ID:IDC_PICTURE_TEST)。 - 右侧属性窗口「样式」→「Type」:
Frame:显示边框(无内容)。Static Image:显示静态图片(默认)。Icon:显示图标。Rectangle:显示实心矩形。Ellipse:显示椭圆。
(2)示例 1:显示图标
- 「Type」选择「Icon」。
- 「样式」→「Image」:下拉选择系统图标(或导入自定义图标)。
- 运行程序,Picture Control 控件显示选中的图标。
(3)示例 2:显示 BMP 图片(代码实现)
-
「Type」选择「Static Image」。
-
导入 BMP 图片资源:「资源视图」→「添加资源」→「Bitmap」→「导入」,选择本地 BMP 图片(ID:
IDB_BITMAP1)。 -
在主对话框的
OnInitDialog()函数中添加代码:cpp
// 显示 BMP 图片 CWnd* pPicture = GetDlgItem(IDC_PICTURE_TEST); if (pPicture != nullptr) { CBitmap bmp; bmp.LoadBitmap(IDB_BITMAP1); // 加载 BMP 资源 CDC* pDC = pPicture->GetDC(); BITMAP bmpInfo; bmp.GetBitmap(&bmpInfo); CDC dcMem; dcMem.CreateCompatibleDC(pDC); dcMem.SelectObject(&bmp); pPicture->GetClientRect(&rect); pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY); // 绘制图片 pPicture->ReleaseDC(pDC); }
(4)注意事项
- 仅支持 BMP 格式图片,若需显示 JPG/PNG 格式,需借助第三方库(如 FreeImage)或 GDI+。
- 图片大小应与 Picture Control 控件大小匹配,避免拉伸变形。
19. Edit Control 的使用
Edit Control 控件用于输入和显示文本,支持单行、多行、只读、密码框等样式,是 MFC 中最常用的输入输出控件。
(1)核心属性设置
- 拖入
Edit Control控件(ID:IDC_EDIT_TEST)。 - 右侧属性窗口:
- 「常规」→「ID」:
IDC_EDIT_TEST。 - 「样式」→「多行」(Multiline):勾选后支持多行文本输入 / 显示。
- 「样式」→「垂直滚动」(Vertical Scroll):多行时显示垂直滚动条。
- 「样式」→「只读」(Read-only):勾选后无法编辑,仅可显示。
- 「样式」→「密码」(Password):勾选后输入内容显示为
*。
- 「常规」→「ID」:
(2)常用操作(代码实现)
-
设置文本:
SetDlgItemText(IDC_EDIT_TEST, _T("Hello MFC!")); -
获取文本: cpp
CString strText; GetDlgItemText(IDC_EDIT_TEST, strText); AfxMessageBox(_T("获取的文本:") + strText); -
追加文本(多行): cpp
CString strOld, strNew = _T("新追加的文本\r\n"); GetDlgItemText(IDC_EDIT_TEST, strOld); strOld += strNew; SetDlgItemText(IDC_EDIT_TEST, strOld); -
清空文本:
SetDlgItemText(IDC_EDIT_TEST, _T(""));
(3)示例:输入文本并显示
添加一个 Button 控件,点击后获取 Edit 控件的文本并弹出消息框:
cpp
void CMfcDialogTutorialDlg::OnBnClickedButtonGetEdit()
{
CString strText;
GetDlgItemText(IDC_EDIT_TEST, strText);
if (strText.IsEmpty())
{
AfxMessageBox(_T("Edit 控件为空!"), MB_OK | MB_ICONWARNING);
}
else
{
AfxMessageBox(_T("Edit 控件内容:") + strText, MB_OK | MB_ICONINFORMATION);
}
}
20. Check Box 的使用
Check Box 控件(复选框)用于选择一个或多个选项,支持「选中」和「未选中」两种状态,可单独使用,也可分组使用。
(1)核心属性设置
- 拖入
Check Box控件(ID:IDC_CHECK_TEST1,Caption:「选项 1」;ID:IDC_CHECK_TEST2,Caption:「选项 2」)。 - 右侧属性窗口:
- 「常规」→「ID」:
IDC_CHECK_TEST1/IDC_CHECK_TEST2。 - 「样式」→「自动复选」(Auto Check):勾选后点击自动切换选中 / 未选中状态(默认勾选)。
- 「样式」→「三态」(Tri-State):勾选后支持「选中」「未选中」「不确定」三种状态(默认不勾选)。
- 「常规」→「ID」:
(2)常用操作(代码实现)
-
设置选中状态:
CheckDlgButton(IDC_CHECK_TEST1, BST_CHECKED);(选中);CheckDlgButton(IDC_CHECK_TEST1, BST_UNCHECKED);(未选中)。 -
获取选中状态: cpp
UINT nState1 = IsDlgButtonChecked(IDC_CHECK_TEST1); if (nState1 == BST_CHECKED) { AfxMessageBox(_T("选项1已选中"), MB_OK | MB_ICONINFORMATION); } else { AfxMessageBox(_T("选项1未选中"), MB_OK | MB_ICONINFORMATION); }
(3)示例:批量获取复选框状态
添加一个 Button 控件,点击后获取两个复选框的状态并弹出消息框:
cpp
void CMfcDialogTutorialDlg::OnBnClickedButtonGetCheck()
{
CString strMsg;
strMsg += IsDlgButtonChecked(IDC_CHECK_TEST1) == BST_CHECKED ? _T("选项1已选中\r\n") : _T("选项1未选中\r\n");
strMsg += IsDlgButtonChecked(IDC_CHECK_TEST2) == BST_CHECKED ? _T("选项2已选中") : _T("选项2未选中");
AfxMessageBox(strMsg, MB_OK | MB_ICONINFORMATION);
}
21. List Box 的使用
List Box 控件(列表框)用于显示一组文本选项,支持单选、多选,用户可选择其中一个或多个选项。
(1)核心属性设置
- 拖入
List Box控件(ID:IDC_LIST_TEST)。 - 右侧属性窗口:
- 「样式」→「多选」(Multi-Sel):勾选后支持多选(默认单选)。
- 「样式」→「垂直滚动」(Vertical Scroll):显示垂直滚动条(默认勾选)。
- 「样式」→「排序」(Sort):勾选后自动按字母顺序排序(默认勾选,可取消)。
(2)常用操作(代码实现)
-
添加选项: cpp
CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST_TEST); if (pList != nullptr) { pList->AddString(_T("选项1")); pList->AddString(_T("选项2")); pList->AddString(_T("选项3")); // 插入选项到指定位置 pList->InsertString(1, _T("插入选项")); } -
删除选项: cpp
CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST_TEST); if (pList != nullptr) { pList->DeleteString(0); // 删除索引为 0 的选项 // 清空所有选项 // pList->ResetContent(); } -
获取选中选项: cpp
CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST_TEST); if (pList != nullptr) { int nSelIndex = pList->GetCurSel(); // 获取当前选中项索引(单选) if (nSelIndex != LB_ERR) { CString strSel; pList->GetText(nSelIndex, strSel); AfxMessageBox(_T("当前选中:") + strSel, MB_OK | MB_ICONINFORMATION); } else { AfxMessageBox(_T("未选中任何选项"), MB_OK | MB_ICONWARNING); } }
(3)示例:添加并获取列表框选项
在 OnInitDialog() 中添加选项,添加一个 Button 控件,点击后获取选中选项。
22. Combo Box 的使用
Combo Box 控件(组合框)结合了 Edit 控件和 List Box 控件的功能,分为「下拉组合框」「简单组合框」「下拉列表框」三种类型,既可以输入文本,也可以选择下拉列表中的选项。
(1)核心属性设置
- 拖入
Combo Box控件(ID:IDC_COMBO_TEST)。 - 右侧属性窗口:
- 「样式」→「类型」(Type):
Drop List:下拉列表框(仅可选择,不可输入)。Drop Down:下拉组合框(可选择,也可输入,默认)。Simple:简单组合框(列表始终展开)。
- 「样式」→「排序」(Sort):勾选后自动排序(默认勾选)。
- 「布局」→「高度」:设置下拉列表的高度(需手动调整,否则列表无法展开)。
- 「样式」→「类型」(Type):
(2)常用操作(代码实现)
-
添加选项: cpp
CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO_TEST); if (pCombo != nullptr) { pCombo->AddString(_T("选项1")); pCombo->AddString(_T("选项2")); pCombo->AddString(_T("选项3")); // 设置默认选中项 pCombo->SetCurSel(0); } -
获取选中选项: cpp
CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO_TEST); if (pCombo != nullptr) { int nSelIndex = pCombo->GetCurSel(); if (nSelIndex != CB_ERR) { CString strSel; pCombo->GetLBText(nSelIndex, strSel); AfxMessageBox(_T("当前选中:") + strSel, MB_OK | MB_ICONINFORMATION); } else { // 获取输入的文本(Drop Down 类型) CString strInput; pCombo->GetWindowText(strInput); AfxMessageBox(_T("输入的文本:") + strInput, MB_OK | MB_ICONINFORMATION); } } -
删除选项: cpp
CComboBox* pCombo = (CComboBox*)GetDlgItem(IDC_COMBO_TEST); if (pCombo != nullptr) { pCombo->DeleteString(0); // 删除索引为 0 的选项 // 清空所有选项 // pCombo->ResetContent(); }
(3)示例:选择组合框选项并显示
在 OnInitDialog() 中添加选项,添加一个 Button 控件,点击后获取选中选项或输入文本。
23. Radio Button 的使用
Radio Button 控件(单选按钮)用于从一组选项中选择唯一一个选项,必须分组使用(否则所有单选按钮视为一组,只能选中一个)。
(1)核心属性设置与分组
- 拖入两个
Radio Button控件(ID:IDC_RADIO1,Caption:「男」;ID:IDC_RADIO2,Caption:「女」)。 - 分组设置(关键):
- 选中第一个单选按钮(
IDC_RADIO1),右侧属性窗口「样式」→「组」(Group):勾选(表示该控件是一组的开始)。 - 第二个单选按钮(
IDC_RADIO2)的「组」属性:不勾选(表示属于上一组)。
- 选中第一个单选按钮(
- 「样式」→「自动单选」(Auto Radio):勾选后点击自动切换选中状态(默认勾选)。
(2)常用操作(代码实现)
-
设置选中状态: cpp
CheckDlgButton(IDC_RADIO1, BST_CHECKED); // 选中「男」 // CheckDlgButton(IDC_RADIO2, BST_CHECKED); // 选中「女」 -
获取选中状态: cpp
CString strMsg; if (IsDlgButtonChecked(IDC_RADIO1) == BST_CHECKED) { strMsg = _T("选中:男"); } else if (IsDlgButtonChecked(IDC_RADIO2) == BST_CHECKED) { strMsg = _T("选中:女"); } else { strMsg = _T("未选中任何选项"); } AfxMessageBox(strMsg, MB_OK | MB_ICONINFORMATION);
(3)示例:获取单选按钮选中状态
添加一个 Button 控件,点击后获取单选按钮的选中状态并弹出消息框。
24. Slider Control 的使用
Slider Control 控件(滑块控件)用于通过拖动滑块选择一个数值,支持水平 / 垂直方向,可设置最小值、最大值、步长。
(1)核心属性设置
- 拖入
Slider Control控件(ID:IDC_SLIDER_TEST)。 - 右侧属性窗口:
- 「样式」→「方向」(Orientation):
Horizontal(水平,默认)/Vertical(垂直)。 - 「样式」→「刻度」(Tick Marks):勾选后显示刻度。
- 「样式」→「自动刻度」(Auto Ticks):勾选后自动生成刻度。
- 「样式」→「滑动块」(Thumb):勾选后显示滑动块(默认勾选)。
- 「样式」→「方向」(Orientation):
(2)常用操作(代码实现)
-
设置滑块范围和初始值: cpp
CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_TEST); if (pSlider != nullptr) { pSlider->SetRange(0, 100); // 设置最小值 0,最大值 100 pSlider->SetPos(50); // 设置初始位置 50 pSlider->SetTickFreq(10); // 设置刻度步长 10 } -
获取滑块当前值: cpp
CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_TEST); if (pSlider != nullptr) { int nPos = pSlider->GetPos(); CString strMsg; strMsg.Format(_T("当前滑块值:%d"), nPos); AfxMessageBox(strMsg, MB_OK | MB_ICONINFORMATION); } -
响应滑块拖动事件:绑定
NM_CUSTOMDRAW事件(滑块拖动时触发),实时获取滑块值:cpp
void CMfcDialogTutorialDlg::OnNMCustomdrawSliderTest(NMHDR *pNMHDR, LRESULT *pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); // 获取滑块当前值 CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_TEST); if (pSlider != nullptr) { int nPos = pSlider->GetPos(); CString strPos; strPos.Format(_T("当前值:%d"), nPos); SetDlgItemText(IDC_EDIT_SLIDER, strPos); // 显示在 Edit 控件中 } *pResult = 0; }
(3)示例:实时显示滑块值
拖入一个 Edit Control 控件(ID:IDC_EDIT_SLIDER),滑块拖动时,实时在 Edit 控件中显示当前值。
总结
本教程覆盖了 MFC 基于对话框程序的核心知识点,从项目创建到各类控件、对话框、线程、菜单的使用,提供了完整的操作步骤和代码示例。关键要点回顾:
- MFC 核心是「消息映射机制」,控件 / 菜单 / 线程的操作均依赖消息绑定。
- 工作线程不能直接操作 UI,需通过
PostMessage/SendMessage与主线程通信。 - 模态对话框阻塞父窗口,非模态对话框不阻塞,且需手动管理内存。
- 各类控件的核心是「属性设置」+「代码操作」,掌握常用方法即可满足大部分开发需求。
通过本教程的学习,可快速入门 MFC 对话框程序开发,后续可基于此扩展复杂功能(如网络通信、数据库操作、图形绘制等)。