MFC入门教程 最简版

MFC 基于对话框程序完整教程(涵盖控件、对话框、线程、菜单)

前言

本教程基于 Visual Studio(推荐 2017/2019/2022),以「基于对话框的 MFC 应用程序」为核心,从项目创建到各类控件、对话框、线程、菜单的使用,进行分步详细讲解,适合 MFC 入门学习者,兼顾实用性和可操作性,所有示例均可直接编译运行。

目录

  1. 创建基于对话框程序
  2. MFC 对话框程序整体构造理解
  3. 控件界面创建(拖入与基本控制)
  4. 调试输出 ------AfxMessageBox 的使用
  5. Visual Studio 基于 MFC 的调试操作
  6. Button 控件的操作
  7. 菜单的创建
  8. 菜单的绑定
  9. 菜单的使用
  10. 认识模态对话框
  11. 创建模态对话框
  12. 认识非模态对话框
  13. 创建非模态对话框
  14. 认识多线程
  15. 如何创建 MFC 线程
  16. 线程间消息传递 ------PostMessage
  17. 线程间同步消息 ------SendMessage
  18. Picture Control 控件的使用
  19. Edit Control 的使用(补充序号,对应原文 20)
  20. Check Box 的使用(对应原文 21)
  21. List Box 的使用(对应原文 22)
  22. Combo Box 的使用(对应原文 23)
  23. Radio Button 的使用(对应原文 24)
  24. Slider Control 的使用(对应原文 25)

1. 创建基于对话框程序

操作步骤

  1. 打开 Visual Studio,点击「创建新项目」。
  2. 在模板列表中搜索「MFC」,选择「MFC 应用程序」,点击「下一步」。
  3. 填写项目名称(如「MfcDialogTutorial」),选择项目保存路径,点击「创建」。
  4. 进入 MFC 应用程序向导:
    • 「应用程序类型」:选择「基于对话框」(默认已选中),取消「使用 Unicode 字符集」(新手推荐,避免字符转换问题,后续可修改为多字节字符集)。
    • 「复合文档支持」:选择「无」。
    • 「高级功能」:保持默认(可取消「ActiveX 控件」,简化项目)。
    • 「生成的类」:保持默认(对话框类名为 CMfcDialogTutorialDlg,继承自 CDialogEx)。
  5. 点击「完成」,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(如 IDOKIDC_BUTTON1)。
  • 菜单资源(Menu):后续创建的菜单存储在此。
  • 字符串资源(String Table):存储程序中使用的字符串常量。
复制代码
  #define IDM_ABOUTBOX                    0x0010
  #define IDD_ABOUTBOX                    100
  #define IDS_ABOUTBOX                    101
  #define IDD_MFCAPPLICATION1_DIALOG      102

(3)消息映射机制

MFC 采用「消息映射」将「窗口 / 控件事件」与「处理函数」绑定,核心流程:

  1. 控件 / 窗口触发事件(如按钮点击),系统发送对应消息(如 BN_CLICKED)。
  2. MFC 通过消息映射表,找到该消息对应的处理函数。
  3. 执行处理函数中的自定义代码。

消息映射表在对话框类的 .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)打开对话框设计器

  1. 切换到「资源视图」(若未显示,点击顶部菜单栏「视图」→「资源视图」)。
  2. 展开项目目录 →「Dialog」,双击 IDD_MFCDIALOGTUTORIAL_DIALOG,打开对话框设计界面。

(2)控件拖入与布局

  1. 显示「工具箱」(若未显示,点击「视图」→「工具箱」),工具箱中包含 MFC 所有常用控件(Button、Edit、Static Text 等)。
  2. 拖入控件:点击工具箱中的目标控件(如 Button),在对话框设计界面中点击 / 拖拽,即可创建控件。
  3. 控件基本操作:
    • 移动:选中控件,按住鼠标左键拖拽。
    • 调整大小:选中控件,拖动控件四周的控制点。
    • 对齐 / 分布:选中多个控件(按住 Ctrl 键点击),右键选择「对齐」「分布」,可实现控件整齐排列。
    • 复制 / 删除:选中控件,使用 Ctrl+C/Ctrl+V 复制,Delete 键删除。

(3)控件属性设置

选中任意控件,右侧会显示「属性窗口」(若未显示,点击「视图」→「属性窗口」),核心属性配置:

  • 「常规」→「ID」:控件的唯一标识(如 IDC_BUTTON_TEST),用于消息映射和控件操作,建议命名见名知意。
  • 「常规」→「Caption」:控件的显示文本(如按钮上的文字「测试按钮」)。
  • 「样式」:控件的外观和行为设置(如按钮是否默认、Edit 是否多行)。
  • 「布局」:控件的位置、大小、是否随对话框缩放。

(4)控件的基本控制(手动修改属性示例)

  1. 拖入一个 Static Text 控件,修改「Caption」为「MFC 控件教程」,修改「ID」为 IDC_STATIC_TITLE
  2. 拖入一个 Button 控件,修改「Caption」为「点击测试」,修改「ID」为 IDC_BUTTON_CLICK
  3. 点击顶部菜单栏「生成」→「生成解决方案」,无报错则控件布局生效。

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)使用示例

  1. 打开对话框类 CMfcDialogTutorialDlg.cpp 文件,找到 OnInitDialog() 函数(对话框初始化时执行)。

  2. 在函数末尾(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);
  3. 点击「本地 Windows 调试器」运行程序,对话框初始化时会依次弹出消息框,查看输出结果。

(4)注意事项

  • 多字节字符集下,使用 _T() 宏包裹字符串(兼容后续字符集切换)。
  • AfxMessageBox 是模态的,弹出后必须关闭才能继续执行后续代码。
  • 仅用于简易调试和提示,大量复杂调试需使用 Visual Studio 调试工具(后续讲解)。

5. Visual Studio 调试操作 ------ 基于 MFC

(1)调试前准备

  1. 将项目配置从「Release」切换为「Debug」(顶部菜单栏左侧,默认可能是 Release,Debug 模式支持断点、变量监视等功能)。
  2. 确保项目无编译错误(生成解决方案无报错)。

(2)核心调试操作 ------ 断点

  1. 设置断点:在代码行左侧的灰色区域点击,出现红色圆点(或快捷键 F9),程序运行到该行会暂停。
    • 示例:在 OnInitDialog() 中的 AfxMessageBox 行设置断点。
  2. 启动调试:点击「本地 Windows 调试器」(或快捷键 F5),程序运行到断点处暂停,界面变为调试模式。
  3. 断点处操作:
    • 单步执行(F10):执行当前行,不进入函数内部(跳过函数调用)。
    • 逐语句执行(F11):执行当前行,若为函数调用则进入函数内部(如进入 AfxMessageBox 底层实现)。
    • 继续运行(F5):跳过当前断点,运行到下一个断点或程序结束。
    • 停止调试(Shift+F5):终止当前调试进程。
    • 移除断点:再次点击红色圆点(或 F9),或右键断点选择「删除断点」。

(3)变量监视

  1. 程序暂停在断点处时,选中要监视的变量(如 nTest),右键选择「添加监视」。
  2. 右侧弹出「监视」窗口,可查看变量的当前值、类型,且在单步执行时实时更新。
  3. 补充:可使用「即时窗口」(调试 → 窗口 → 即时),直接输入变量名或表达式(如 nTest+10),查看计算结果。

(4)MFC 专属调试技巧

  1. 查看控件状态:在断点处,可通过「监视窗口」输入 GetDlgItem(IDC_BUTTON_CLICK)->GetWindowText(strMsg),查看控件的显示文本。
  2. 断点条件:右键断点 →「条件」,输入条件表达式(如 nTest > 200),仅当条件满足时程序才会暂停在该断点。
  3. 命中次数:右键断点 →「命中次数」,设置命中次数(如 5),程序第 5 次运行到该断点时才会暂停。

6. Button 控件的操作

Button 控件(按钮)是 MFC 中最常用的控件,用于触发用户操作,核心是「绑定点击事件处理函数」。

(1)绑定点击事件(两种方法)

方法一:可视化绑定(推荐新手)
  1. 打开对话框设计器,双击 IDC_BUTTON_CLICK 按钮(之前创建的「点击测试」按钮)。
  2. Visual Studio 自动在对话框类中生成点击事件处理函数 OnBnClickedButtonClick(),并自动添加消息映射。
    • 头文件(.h)中声明函数:afx_msg void OnBnClickedButtonClick();
    • 源文件(.cpp)中生成空函数体,且消息映射表中添加:ON_BN_CLICKED(IDC_BUTTON_CLICK, &CMfcDialogTutorialDlg::OnBnClickedButtonClick)
方法二:手动绑定(进阶)
  1. 头文件中声明处理函数:afx_msg void OnBnClickedButtonClick();
  2. 源文件的消息映射表中添加:ON_BN_CLICKED(IDC_BUTTON_CLICK, &CMfcDialogTutorialDlg::OnBnClickedButtonClick)
  3. 源文件中实现函数体: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)创建菜单资源

  1. 切换到「资源视图」,右键项目目录 →「添加」→「资源」。
  2. 在弹出的「添加资源」窗口中,选择「Menu」,点击「新建」。
  3. Visual Studio 自动创建一个空白菜单资源(ID 默认为 IDR_MENU1),并打开菜单设计器。
  4. 菜单设计操作:
    • 顶部空白菜单项:点击后输入菜单名称(如「文件 (&F)」,&F 表示快捷键 Alt+F)。
    • 子菜单项:右键顶部菜单项 →「添加子菜单」,输入子菜单名称(如「新建 (&N)」「退出 (&X)」)。
    • 分隔线:右键子菜单 →「插入分隔符」,用于分组子菜单。
    • 修改菜单 ID:选中子菜单项(如「退出」),在右侧属性窗口中修改「ID」为 ID_MENU_FILE_EXIT(见名知意)。

(2)菜单设计示例

创建一个简单菜单结构:

  • 顶部菜单 1:「文件 (&F)」
    • 子菜单:「新建 (&N)」(ID:ID_MENU_FILE_NEW
    • 分隔线
    • 子菜单:「退出 (&X)」(ID:ID_MENU_FILE_EXIT
  • 顶部菜单 2:「帮助 (&H)」
    • 子菜单:「关于 (&A)」(ID:ID_MENU_HELP_ABOUT

(3)保存菜单资源

菜单设计完成后,点击「保存」,菜单资源自动存入 .rc 文件,可在「资源视图」的「Menu」目录下查看 IDR_MENU1

8. 菜单的绑定

创建菜单资源后,需将其绑定到主对话框,才能在程序运行时显示菜单。

(1)绑定方法(两种)

方法一:对话框属性绑定(推荐)
  1. 打开对话框设计器,选中主对话框(IDD_MFCDIALOGTUTORIAL_DIALOG)。
  2. 右侧属性窗口中,找到「常规」→「Menu」,下拉选择已创建的菜单资源 IDR_MENU1
  3. 点击「生成解决方案」,运行程序,主对话框顶部会显示绑定的菜单。
方法二:手动代码绑定(进阶)

在对话框类的 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)绑定菜单点击事件(两种方法)

方法一:可视化绑定(推荐新手)
  1. 「资源视图」中展开「Menu」,双击 IDR_MENU1 打开菜单设计器。
  2. 双击子菜单项(如「退出」),Visual Studio 自动生成处理函数 OnMenuFileExit(),并添加消息映射。
方法二:手动绑定(进阶)
  1. 头文件中声明处理函数:afx_msg void OnMenuFileExit();
  2. 源文件消息映射表中添加:ON_COMMAND(ID_MENU_FILE_EXIT, &CMfcDialogTutorialDlg::OnMenuFileExit)
  3. 源文件中实现函数体。

(2)菜单功能实现示例

  1. 「退出」菜单功能:关闭对话框,退出程序 cpp

    复制代码
    void CMfcDialogTutorialDlg::OnMenuFileExit()
    {
        // 发送 IDCANCEL 消息,关闭对话框
        OnCancel(); // 等价于点击「取消」按钮
    }
  2. 「新建」菜单功能:弹出提示消息 cpp

    复制代码
    void CMfcDialogTutorialDlg::OnMenuFileNew()
    {
        AfxMessageBox(_T("新建操作被触发!"), MB_OK | MB_ICONINFORMATION);
    }
  3. 「关于」菜单功能:弹出关于对话框 cpp

    复制代码
    void CMfcDialogTutorialDlg::OnMenuHelpAbout()
    {
        AfxMessageBox(_T("MFC 对话框教程 v1.0\n作者:MFC 学习者"), MB_OK | MB_ICONINFORMATION);
    }

(3)菜单常用附加操作

  1. 设置菜单勾选标记:CheckMenuItem(ID_MENU_FILE_NEW, MF_CHECKED | MF_BYCOMMAND);
  2. 禁用菜单项:EnableMenuItem(ID_MENU_FILE_NEW, MF_DISABLED | MF_GRAYED | MF_BYCOMMAND);
  3. 移除菜单:SetMenu(nullptr);

10. 认识模态对话框

模态对话框(Modal Dialog)是 MFC 中最常用的对话框类型,具有以下特点:

  1. 对话框弹出后,阻塞父窗口(主对话框)的所有操作,用户必须先关闭模态对话框,才能操作父窗口。
  2. 模态对话框具有自己的消息循环,独立于父窗口,但依赖父窗口存在。
  3. 常用场景:文件选择、参数设置、提示确认(如「关于」对话框、「保存文件」对话框)。
  4. 核心创建函数:DoModal(),该函数返回对话框的关闭方式(IDOKIDCANCEL)。

11. 创建模态对话框

(1)步骤 1:创建对话框资源

  1. 「资源视图」右键「Dialog」→「添加资源」→「Dialog」→「新建」,创建一个空白对话框资源(ID 默认为 IDD_DIALOG1)。
  2. 设计模态对话框界面:
    • 修改对话框「Caption」为「关于本程序」。
    • 拖入 Static Text 控件,修改「Caption」为「MFC 对话框教程 v1.0\n 基于 Visual Studio 2019」。
    • 保留默认的「确定」按钮(ID:IDOK),删除「取消」按钮(模态对话框若无需取消,可删除)。
  3. 修改对话框 ID 为 IDD_ABOUT_DIALOG(见名知意)。

(2)步骤 2:创建对话框类

  1. 右键对话框设计器中的空白区域 →「添加类」。
  2. 弹出「MFC 类向导」,填写类名(如 CAboutDialog),基类选择 CDialogEx,点击「完成」。
  3. Visual Studio 自动生成 CAboutDialog.hCAboutDialog.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)与模态对话框相反,具有以下特点:

  1. 对话框弹出后,不阻塞父窗口的操作,用户可同时操作父窗口和非模态对话框。
  2. 非模态对话框没有自己独立的消息循环,依赖父窗口的消息循环。
  3. 常用场景:工具窗口、属性窗口、实时监控窗口(如 VS 的「解决方案资源管理器」)。
  4. 核心创建步骤:Create()(创建对话框)+ ShowWindow(SW_SHOW)(显示对话框),无需调用 DoModal()
  5. 注意事项:非模态对话框对象需为堆内存分配(避免局部变量销毁导致对话框关闭),且需手动管理销毁。

13. 创建非模态对话框

(1)步骤 1:创建对话框资源(同模态对话框)

  1. 「资源视图」右键「Dialog」→「添加资源」→「Dialog」→「新建」,创建空白对话框资源(ID:IDD_TOOL_DIALOG)。
  2. 设计界面:修改「Caption」为「工具窗口」,拖入一个 Static Text 控件,修改「Caption」为「非模态对话框示例」。
  3. 保留「确定」「取消」按钮(ID 默认为 IDOKIDCANCEL)。

(2)步骤 2:创建对话框类

  1. 右键对话框设计器空白区域 →「添加类」,类名:CToolDialog,基类:CDialogEx,点击「完成」。
  2. 自动生成 CToolDialog.hCToolDialog.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:销毁非模态对话框

  1. 处理非模态对话框的「确定」「取消」按钮事件,关闭对话框并释放内存:打开 CToolDialog.cpp,双击「确定」按钮,生成处理函数:

    cpp

    复制代码
    void CToolDialog::OnBnClickedOk()
    {
        // 隐藏对话框(非模态对话框不调用 CDialogEx::OnOk(),避免自动销毁)
        ShowWindow(SW_HIDE);
    
        // 若需彻底销毁,可通知父窗口释放内存
        GetParent()->PostMessage(WM_COMMAND, IDC_BUTTON_CLOSE_MODELESS);
    }

    同理,处理「取消」按钮事件,逻辑与「确定」按钮一致。

  2. 主对话框退出时,释放非模态对话框内存(在析构函数或 OnCancel() 中):

    cpp

    复制代码
    CMfcDialogTutorialDlg::~CMfcDialogTutorialDlg()
    {
        // 释放非模态对话框内存
        if (m_pToolDlg != nullptr)
        {
            m_pToolDlg->DestroyWindow(); // 销毁对话框资源
            delete m_pToolDlg;
            m_pToolDlg = nullptr;
        }
    }

(6)运行效果

点击「显示非模态对话框」,弹出「工具窗口」,此时可同时操作主对话框和非模态对话框,点击非模态对话框的「确定」,对话框隐藏,再次点击按钮可重新显示。

14. 认识多线程

(1)线程的基本概念

线程是程序执行的最小单位,一个进程可以包含多个线程,所有线程共享进程的内存空间、资源。MFC 程序默认只有一个「主线程」(UI 线程),负责处理界面绘制、用户输入、消息循环。

(2)MFC 中的两种线程类型

  1. UI 线程(用户界面线程):拥有自己的消息循环,可创建和操作控件,适用于复杂的独立界面模块(如多窗口程序)。
  2. 工作线程(后台线程):无消息循环,仅用于执行后台任务(如数据计算、文件读写、网络通信),不能直接操作 UI 控件,适用于耗时操作(避免阻塞主线程导致界面卡死)。

(3)多线程的核心用途(MFC 场景)

  • 执行耗时操作(如 TCP 数据接收、大文件解析),避免主线程阻塞,保证界面流畅。
  • 并行处理多个独立任务(如同时监听多个网络端口、同时处理多个文件)。

(4)注意事项

  • 工作线程不能直接操作 UI 控件(如修改 Edit 文本、点击 Button),否则会导致程序崩溃、界面卡死,需通过消息传递让主线程操作 UI。
  • 多个线程访问共享资源时,需加锁(如 CCriticalSection),避免数据竞争(脏数据)。

15. 如何创建 MFC 线程

MFC 提供 AfxBeginThread() 函数创建线程,支持创建 UI 线程和工作线程,此处重点讲解工作线程(最常用)。

(1)工作线程创建步骤

  1. 定义线程函数(全局函数或静态成员函数,格式固定)。
  2. 调用 AfxBeginThread() 启动线程,传递线程函数和参数。
  3. 实现线程函数的后台任务逻辑。

(2)线程函数格式要求

工作线程函数的返回值和参数格式固定,原型如下:

cpp

复制代码
UINT ThreadProc(LPVOID pParam); // pParam:线程参数,LPVOID 可转换为任意类型
  • 返回值:UINT,线程退出码(通常返回 0 表示正常退出)。
  • 参数:LPVOID,传递给线程的自定义参数(可传递对话框指针、数据缓冲区等)。

(3)创建工作线程示例(后台计数任务)

  1. 在主对话框类的 .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; // 线程正常退出
    }
  2. 添加一个按钮(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)核心特点

  1. 异步发送:消息放入主线程消息队列后立即返回,工作线程继续执行后续代码,不等待主线程处理。
  2. 无返回值:无法获取主线程处理消息的结果。
  3. 安全可靠:避免工作线程直接操作 UI,由主线程处理 UI 更新。

(2)使用步骤

  1. 定义自定义消息(在主对话框类的 .h 文件中)。
  2. 声明并实现消息处理函数。
  3. 绑定消息映射。
  4. 工作线程中调用 PostMessage 发送自定义消息。

(3)示例:线程计数更新 UI(Edit 控件)

  1. 步骤 1:定义自定义消息在 MfcDialogTutorialDlg.h 文件顶部添加:

    cpp

    复制代码
    #define WM_UPDATE_COUNT (WM_USER + 100) // 自定义消息,WM_USER + 100 避免与系统消息冲突
  2. 步骤 2:声明消息处理函数在主对话框类的 .h 文件中添加:

    cpp

    复制代码
    afx_msg LRESULT OnUpdateCount(WPARAM wParam, LPARAM lParam);
  3. 步骤 3:绑定消息映射在主对话框类的 .cpp 文件的消息映射表中添加:

    cpp

    复制代码
    BEGIN_MESSAGE_MAP(CMfcDialogTutorialDlg, CDialogEx)
        // ... 其他消息映射 ...
        ON_MESSAGE(WM_UPDATE_COUNT, &CMfcDialogTutorialDlg::OnUpdateCount) // 绑定自定义消息
    END_MESSAGE_MAP()
  4. 步骤 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. 步骤 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)核心特点

  1. 同步发送:阻塞工作线程,等待主线程处理完消息并返回结果后,才继续执行。
  2. 有返回值:可获取主线程处理消息的结果。
  3. 慎用场景:若主线程处理消息耗时较长,会导致工作线程阻塞,影响后台任务执行。

(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)核心属性设置

  1. 拖入 Picture Control 控件(ID:IDC_PICTURE_TEST)。
  2. 右侧属性窗口「样式」→「Type」:
    • Frame:显示边框(无内容)。
    • Static Image:显示静态图片(默认)。
    • Icon:显示图标。
    • Rectangle:显示实心矩形。
    • Ellipse:显示椭圆。

(2)示例 1:显示图标

  1. 「Type」选择「Icon」。
  2. 「样式」→「Image」:下拉选择系统图标(或导入自定义图标)。
  3. 运行程序,Picture Control 控件显示选中的图标。

(3)示例 2:显示 BMP 图片(代码实现)

  1. 「Type」选择「Static Image」。

  2. 导入 BMP 图片资源:「资源视图」→「添加资源」→「Bitmap」→「导入」,选择本地 BMP 图片(ID:IDB_BITMAP1)。

  3. 在主对话框的 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)核心属性设置

  1. 拖入 Edit Control 控件(ID:IDC_EDIT_TEST)。
  2. 右侧属性窗口:
    • 「常规」→「ID」:IDC_EDIT_TEST
    • 「样式」→「多行」(Multiline):勾选后支持多行文本输入 / 显示。
    • 「样式」→「垂直滚动」(Vertical Scroll):多行时显示垂直滚动条。
    • 「样式」→「只读」(Read-only):勾选后无法编辑,仅可显示。
    • 「样式」→「密码」(Password):勾选后输入内容显示为 *

(2)常用操作(代码实现)

  1. 设置文本:SetDlgItemText(IDC_EDIT_TEST, _T("Hello MFC!"));

  2. 获取文本: cpp

    复制代码
    CString strText;
    GetDlgItemText(IDC_EDIT_TEST, strText);
    AfxMessageBox(_T("获取的文本:") + strText);
  3. 追加文本(多行): cpp

    复制代码
    CString strOld, strNew = _T("新追加的文本\r\n");
    GetDlgItemText(IDC_EDIT_TEST, strOld);
    strOld += strNew;
    SetDlgItemText(IDC_EDIT_TEST, strOld);
  4. 清空文本: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)核心属性设置

  1. 拖入 Check Box 控件(ID:IDC_CHECK_TEST1,Caption:「选项 1」;ID:IDC_CHECK_TEST2,Caption:「选项 2」)。
  2. 右侧属性窗口:
    • 「常规」→「ID」:IDC_CHECK_TEST1/IDC_CHECK_TEST2
    • 「样式」→「自动复选」(Auto Check):勾选后点击自动切换选中 / 未选中状态(默认勾选)。
    • 「样式」→「三态」(Tri-State):勾选后支持「选中」「未选中」「不确定」三种状态(默认不勾选)。

(2)常用操作(代码实现)

  1. 设置选中状态:CheckDlgButton(IDC_CHECK_TEST1, BST_CHECKED);(选中);CheckDlgButton(IDC_CHECK_TEST1, BST_UNCHECKED);(未选中)。

  2. 获取选中状态: 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)核心属性设置

  1. 拖入 List Box 控件(ID:IDC_LIST_TEST)。
  2. 右侧属性窗口:
    • 「样式」→「多选」(Multi-Sel):勾选后支持多选(默认单选)。
    • 「样式」→「垂直滚动」(Vertical Scroll):显示垂直滚动条(默认勾选)。
    • 「样式」→「排序」(Sort):勾选后自动按字母顺序排序(默认勾选,可取消)。

(2)常用操作(代码实现)

  1. 添加选项: 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("插入选项"));
    }
  2. 删除选项: cpp

    复制代码
    CListBox* pList = (CListBox*)GetDlgItem(IDC_LIST_TEST);
    if (pList != nullptr)
    {
        pList->DeleteString(0); // 删除索引为 0 的选项
        // 清空所有选项
        // pList->ResetContent();
    }
  3. 获取选中选项: 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)核心属性设置

  1. 拖入 Combo Box 控件(ID:IDC_COMBO_TEST)。
  2. 右侧属性窗口:
    • 「样式」→「类型」(Type):
      • Drop List:下拉列表框(仅可选择,不可输入)。
      • Drop Down:下拉组合框(可选择,也可输入,默认)。
      • Simple:简单组合框(列表始终展开)。
    • 「样式」→「排序」(Sort):勾选后自动排序(默认勾选)。
    • 「布局」→「高度」:设置下拉列表的高度(需手动调整,否则列表无法展开)。

(2)常用操作(代码实现)

  1. 添加选项: 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);
    }
  2. 获取选中选项: 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);
        }
    }
  3. 删除选项: 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)核心属性设置与分组

  1. 拖入两个 Radio Button 控件(ID:IDC_RADIO1,Caption:「男」;ID:IDC_RADIO2,Caption:「女」)。
  2. 分组设置(关键):
    • 选中第一个单选按钮(IDC_RADIO1),右侧属性窗口「样式」→「组」(Group):勾选(表示该控件是一组的开始)。
    • 第二个单选按钮(IDC_RADIO2)的「组」属性:不勾选(表示属于上一组)。
  3. 「样式」→「自动单选」(Auto Radio):勾选后点击自动切换选中状态(默认勾选)。

(2)常用操作(代码实现)

  1. 设置选中状态: cpp

    复制代码
    CheckDlgButton(IDC_RADIO1, BST_CHECKED); // 选中「男」
    // CheckDlgButton(IDC_RADIO2, BST_CHECKED); // 选中「女」
  2. 获取选中状态: 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)核心属性设置

  1. 拖入 Slider Control 控件(ID:IDC_SLIDER_TEST)。
  2. 右侧属性窗口:
    • 「样式」→「方向」(Orientation):Horizontal(水平,默认)/ Vertical(垂直)。
    • 「样式」→「刻度」(Tick Marks):勾选后显示刻度。
    • 「样式」→「自动刻度」(Auto Ticks):勾选后自动生成刻度。
    • 「样式」→「滑动块」(Thumb):勾选后显示滑动块(默认勾选)。

(2)常用操作(代码实现)

  1. 设置滑块范围和初始值: 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
    }
  2. 获取滑块当前值: 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);
    }
  3. 响应滑块拖动事件:绑定 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 基于对话框程序的核心知识点,从项目创建到各类控件、对话框、线程、菜单的使用,提供了完整的操作步骤和代码示例。关键要点回顾:

  1. MFC 核心是「消息映射机制」,控件 / 菜单 / 线程的操作均依赖消息绑定。
  2. 工作线程不能直接操作 UI,需通过 PostMessage/SendMessage 与主线程通信。
  3. 模态对话框阻塞父窗口,非模态对话框不阻塞,且需手动管理内存。
  4. 各类控件的核心是「属性设置」+「代码操作」,掌握常用方法即可满足大部分开发需求。

通过本教程的学习,可快速入门 MFC 对话框程序开发,后续可基于此扩展复杂功能(如网络通信、数据库操作、图形绘制等)。

相关推荐
王老师青少年编程2 小时前
信奥赛C++提高组csp-s之倍增算法
c++·csp·信奥赛·csp-s·提高组·倍增算法·rmq
低频电磁之道2 小时前
编译C++的几种方式(MSVC编译器)
开发语言·c++
Zsy_0510032 小时前
【C++】类和对象(一)
开发语言·c++
是娇娇公主~2 小时前
工厂模式详细讲解
数据库·c++
_OP_CHEN3 小时前
【从零开始的Qt开发指南】(二十三)Qt 界面优化之 QSS 实战指南:从入门到精通,让你的界面颜值飙升!
开发语言·c++·qt·前端开发·界面美化·qss·客户端开发
HellowAmy3 小时前
我的C++规范 - 跳跃的对象
开发语言·c++·代码规范
lucky-billy3 小时前
架构设计 - std::forward 条件转换配合万能引用(T&&)来实现完美转发
c++·完美转发·forward·万能引用
bkspiderx4 小时前
C/C++中float浮点型的存储方式与使用要点
c++
起个名字费劲死了4 小时前
QT + Socket 客户端/服务端 公网通讯
服务器·c++·qt·socket