MFC中事件与消息有什么关联,区别与联系

文章目录

    • [1. 基本概念对比](#1. 基本概念对比)
    • [2. 关联关系:消息驱动事件](#2. 关联关系:消息驱动事件)
    • [3. MFC中的消息处理机制](#3. MFC中的消息处理机制)
      • [3.1 消息映射表](#3.1 消息映射表)
      • [3.2 消息循环](#3.2 消息循环)
    • [4. Windows消息类型与对应事件](#4. Windows消息类型与对应事件)
      • [4.1 鼠标事件](#4.1 鼠标事件)
      • [4.2 键盘事件](#4.2 键盘事件)
      • [4.3 窗口事件](#4.3 窗口事件)
    • [5. 命令消息与控件事件](#5. 命令消息与控件事件)
      • [5.1 命令消息映射](#5.1 命令消息映射)
      • [5.2 控件通知消息](#5.2 控件通知消息)
    • [6. 自定义消息和事件](#6. 自定义消息和事件)
      • [6.1 定义自定义消息](#6.1 定义自定义消息)
      • [6.2 处理自定义消息](#6.2 处理自定义消息)
    • [7. 消息路由机制](#7. 消息路由机制)
    • [8. 事件驱动编程模型](#8. 事件驱动编程模型)
    • [9. 消息与事件的对应关系表](#9. 消息与事件的对应关系表)
    • [10. 实际应用示例](#10. 实际应用示例)
    • [11. 重要结论](#11. 重要结论)
    • [12. 最佳实践](#12. 最佳实践)

在MFC中, 事件消息 是紧密相关的概念,但有不同的抽象层次。让我详细解释它们的关联和区别:

1. 基本概念对比

概念 定义 抽象层次 示例
消息 Windows操作系统与应用程序间通信的基本单位 底层机制 WM_PAINT, WM_COMMAND
事件 用户操作或系统状态变化产生的结果 高层抽象 按钮点击、菜单选择

2. 关联关系:消息驱动事件

cpp 复制代码
// 消息是事件的底层实现
事件发生 → 产生消息 → 消息处理 → 事件响应

// 示例:用户点击按钮
1. 用户点击按钮(事件)
2. 系统生成WM_LBUTTONDOWN、WM_LBUTTONUP消息
3. MFC的消息循环接收并分发消息
4. 消息映射调用相应处理函数
5. 按钮的OnClick事件处理函数被调用

3. MFC中的消息处理机制

3.1 消息映射表

cpp 复制代码
// MyDialog.h
class CMyDialog : public CDialog
{
    DECLARE_MESSAGE_MAP()  // 声明消息映射
    
public:
    afx_msg void OnButtonClick();  // 消息处理函数
    afx_msg void OnPaint();
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};

// MyDialog.cpp
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    ON_BN_CLICKED(IDC_BUTTON1, &CMyDialog::OnButtonClick)  // 按钮点击消息
    ON_WM_PAINT()  // 绘制消息
    ON_WM_ERASEBKGND()  // 擦除背景消息
END_MESSAGE_MAP()

void CMyDialog::OnButtonClick()
{
    // 这是对"按钮点击事件"的处理
    MessageBox(_T("按钮被点击了!"));
}

3.2 消息循环

cpp 复制代码
// MFC应用程序的消息循环(在CWinApp中)
int CMyApp::Run()
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);  // 翻译消息
        DispatchMessage(&msg);   // 分发消息
    }
    return (int)msg.wParam;
}

4. Windows消息类型与对应事件

4.1 鼠标事件

cpp 复制代码
// 消息映射
BEGIN_MESSAGE_MAP(CMyView, CView)
    ON_WM_LBUTTONDOWN()    // 鼠标左键按下
    ON_WM_LBUTTONUP()      // 鼠标左键释放
    ON_WM_RBUTTONDOWN()    // 鼠标右键按下
    ON_WM_MOUSEMOVE()      // 鼠标移动
    ON_WM_MOUSEWHEEL()     // 鼠标滚轮
END_MESSAGE_MAP()

// 处理函数
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // 对应"鼠标左键按下事件"
    CString str;
    str.Format(_T("鼠标在 (%d, %d) 位置按下"), point.x, point.y);
    TRACE(str);
    
    CView::OnLButtonDown(nFlags, point);
}

4.2 键盘事件

cpp 复制代码
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    ON_WM_KEYDOWN()      // 按键按下
    ON_WM_KEYUP()        // 按键释放
    ON_WM_CHAR()         // 字符输入
END_MESSAGE_MAP()

BOOL CMyDialog::PreTranslateMessage(MSG* pMsg)
{
    // 预处理消息
    if (pMsg->message == WM_KEYDOWN)
    {
        if (pMsg->wParam == VK_RETURN)  // Enter键
        {
            // 处理Enter键按下事件
            OnOK();
            return TRUE;
        }
    }
    return CDialog::PreTranslateMessage(pMsg);
}

4.3 窗口事件

cpp 复制代码
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_CREATE()        // 窗口创建
    ON_WM_SIZE()         // 窗口大小改变
    ON_WM_CLOSE()        // 窗口关闭
    ON_WM_PAINT()        // 窗口绘制
    ON_WM_DESTROY()      // 窗口销毁
END_MESSAGE_MAP()

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // 对应"窗口创建事件"
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    // 创建工具栏、状态栏等
    return 0;
}

void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
    CFrameWnd::OnSize(nType, cx, cy);
    // 对应"窗口大小改变事件"
    // 调整子控件位置
}

5. 命令消息与控件事件

5.1 命令消息映射

cpp 复制代码
// 菜单命令
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_COMMAND(ID_FILE_OPEN, &CMainFrame::OnFileOpen)    // 文件打开
    ON_COMMAND(ID_FILE_SAVE, &CMainFrame::OnFileSave)    // 文件保存
    ON_COMMAND(ID_EDIT_COPY, &CMainFrame::OnEditCopy)    // 编辑复制
END_MESSAGE_MAP()

void CMainFrame::OnFileOpen()
{
    // 对应"文件打开事件"
    CFileDialog dlg(TRUE);
    if (dlg.DoModal() == IDOK)
    {
        // 处理打开文件
    }
}

5.2 控件通知消息

cpp 复制代码
// 按钮、列表框等控件事件
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    ON_BN_CLICKED(IDC_BUTTON1, &CMyDialog::OnButtonClick)  // 按钮点击
    ON_EN_CHANGE(IDC_EDIT1, &CMyDialog::OnEditChange)       // 编辑框内容改变
    ON_CBN_SELCHANGE(IDC_COMBO1, &CMyDialog::OnComboSelChange) // 组合框选择改变
    ON_LBN_SELCHANGE(IDC_LIST1, &CMyDialog::OnListSelChange)   // 列表框选择改变
END_MESSAGE_MAP()

void CMyDialog::OnEditChange()
{
    // 对应"编辑框内容改变事件"
    CString str;
    GetDlgItemText(IDC_EDIT1, str);
    UpdateData(FALSE);  // 更新关联变量
}

6. 自定义消息和事件

6.1 定义自定义消息

cpp 复制代码
// 定义自定义消息
#define WM_USER_MESSAGE (WM_USER + 100)  // WM_USER是用户自定义消息的起始值
#define WM_CUSTOM_EVENT (WM_USER + 101)

// 发送自定义消息
void CMyDialog::SendCustomMessage()
{
    // 发送消息给自己
    PostMessage(WM_USER_MESSAGE, 0, 0);
    
    // 发送消息给其他窗口
    CWnd* pWnd = AfxGetMainWnd();
    if (pWnd)
        pWnd->SendMessage(WM_CUSTOM_EVENT, 0, 0);
}

6.2 处理自定义消息

cpp 复制代码
// 在头文件中声明处理函数
afx_msg LRESULT OnUserMessage(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnCustomEvent(WPARAM wParam, LPARAM lParam);

// 在cpp文件中
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    ON_MESSAGE(WM_USER_MESSAGE, &CMyDialog::OnUserMessage)
    ON_MESSAGE(WM_CUSTOM_EVENT, &CMyDialog::OnCustomEvent)
END_MESSAGE_MAP()

LRESULT CMyDialog::OnUserMessage(WPARAM wParam, LPARAM lParam)
{
    // 处理自定义消息
    MessageBox(_T("收到自定义消息!"));
    return 0;
}

7. 消息路由机制

cpp 复制代码
// MFC消息路由流程
1. 消息产生(系统或用户操作)
2. PreTranslateMessage() 预处理
3. WindowProc() 窗口过程
4. 消息映射表查找对应处理函数
5. 调用相应的处理函数
6. 如果未处理,传递给基类

// 重写PreTranslateMessage预处理消息
BOOL CMyDialog::PreTranslateMessage(MSG* pMsg)
{
    // 预处理所有消息
    if (pMsg->message == WM_KEYDOWN)
    {
        if (pMsg->wParam == VK_ESCAPE)
        {
            // 拦截ESC键
            return TRUE;  // 消息已处理,不继续传递
        }
    }
    return CDialog::PreTranslateMessage(pMsg);
}

8. 事件驱动编程模型

cpp 复制代码
// 典型的事件驱动程序结构
class CMyDocument : public CDocument
{
    // 数据模型
};

class CMyView : public CView
{
protected:
    // 消息处理函数(对应各种事件)
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnDraw(CDC* pDC);
    
    DECLARE_MESSAGE_MAP()
};

// 程序执行流程
应用程序启动
↓
进入消息循环
↓
等待事件发生(用户输入、系统消息)
↓
事件产生对应消息
↓
消息被分发到相应窗口
↓
调用消息处理函数
↓
更新视图/数据

9. 消息与事件的对应关系表

事件类型 产生的消息 消息参数 处理函数
鼠标左键点击 WM_LBUTTONDOWN WM_LBUTTONUP 坐标位置 OnLButtonDown() OnLButtonUp()
键盘输入 WM_KEYDOWN WM_CHAR 键码 OnKeyDown() OnChar()
窗口重绘 WM_PAINT 设备上下文 OnPaint()
菜单选择 WM_COMMAND 命令ID OnCommand()
定时器到期 WM_TIMER 定时器ID OnTimer()
控件通知 WM_COMMAND + 通知码 控件ID 对应通知处理函数

10. 实际应用示例

cpp 复制代码
// 一个完整的MFC对话框示例
class CLoginDialog : public CDialog
{
    DECLARE_MESSAGE_MAP()
    
public:
    CLoginDialog(CWnd* pParent = nullptr);
    
protected:
    CString m_username;
    CString m_password;
    
    // 控件变量
    CEdit m_editUsername;
    CEdit m_editPassword;
    
    // 消息处理函数
    afx_msg void OnBnClickedLogin();     // 登录按钮点击事件
    afx_msg void OnBnClickedCancel();    // 取消按钮点击事件
    afx_msg void OnEnChangeUsername();   // 用户名改变事件
    afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
    
    virtual BOOL OnInitDialog();         // 对话框初始化事件
    virtual void OnOK();                  // 回车键事件
};

BEGIN_MESSAGE_MAP(CLoginDialog, CDialog)
    ON_BN_CLICKED(IDC_BUTTON_LOGIN, &CLoginDialog::OnBnClickedLogin)
    ON_BN_CLICKED(IDCANCEL, &CLoginDialog::OnBnClickedCancel)
    ON_EN_CHANGE(IDC_EDIT_USERNAME, &CLoginDialog::OnEnChangeUsername)
    ON_WM_CTLCOLOR()
END_MESSAGE_MAP()

void CLoginDialog::OnBnClickedLogin()
{
    // 处理登录按钮点击事件
    UpdateData(TRUE);  // 从控件更新变量
    
    if (m_username.IsEmpty() || m_password.IsEmpty())
    {
        MessageBox(_T("用户名和密码不能为空!"), _T("提示"), MB_ICONWARNING);
        return;
    }
    
    // 验证用户名密码...
    
    EndDialog(IDOK);  // 关闭对话框
}

11. 重要结论

  1. 消息是事件的底层实现:事件是高级抽象,消息是具体实现机制
  2. 消息映射是桥梁:MFC通过消息映射将Windows消息连接到C++成员函数
  3. 事件驱动架构:MFC应用程序是事件驱动的,由消息循环驱动
  4. 层级传递:消息可以在父窗口和子窗口之间传递
  5. 可定制性:可以通过重写PreTranslateMessage等函数自定义消息处理

12. 最佳实践

  1. 使用ClassWizard:自动生成消息映射和处理函数
  2. 合理划分职责:模型-视图-控制器模式
  3. 避免长时处理:消息处理函数应尽快返回
  4. 使用工作线程:耗时操作放在工作线程中
  5. 消息转发:未处理的消息应传递给基类

理解MFC中消息与事件的关系对于Windows GUI编程至关重要,这种机制是MFC框架响应各种用户交互和系统事件的基础。

上一篇:MFC进程间消息通信深度解析:SendMessage、PostMessage与SendNotifyMessage的底层实现与实战指南


不积跬步,无以至千里。


代码铸就星河,探索永无止境

在这片由逻辑与算法编织的星辰大海中,每一次报错都是宇宙抛来的谜题,每一次调试都是与未知的深度对话。不要因短暂的"运行失败"而止步,因为真正的光芒,往往诞生于反复试错的暗夜。

请铭记

  • 你写下的每一行代码,都在为思维锻造韧性;
  • 你破解的每一个Bug,都在为认知推开新的门扉;
  • 你坚持的每一分钟,都在为未来的飞跃积蓄势能。

技术的疆域没有终点,只有不断刷新的起点。无论是递归般的层层挑战,还是如异步并发的复杂困局,你终将以耐心为栈、以好奇心为指针,遍历所有可能。

向前吧,开发者

让代码成为你攀登的绳索,让逻辑化作照亮迷雾的灯塔。当你在终端看到"Success"的瞬间,便是宇宙对你坚定信念的回响------
此刻的成就,永远只是下一个奇迹的序章! 🚀


(将技术挑战比作宇宙探索,用代码、算法等意象强化身份认同,传递"持续突破"的信念,结尾以动态符号激发行动力。)

cpp 复制代码
//c++ hello world示例
#include <iostream>  // 引入输入输出流库

int main() {
    std::cout << "Hello World!" << std::endl;  // 输出字符串并换行
    return 0;  // 程序正常退出
}

print("Hello World!")  # 调用内置函数输出字符串

package main  // 声明主包
py 复制代码
#python hello world示例
import "fmt"  // 导入格式化I/O库
go 复制代码
//go hello world示例
func main() {
    fmt.Println("Hello World!")  // 输出并换行
}
C# 复制代码
//c# hello world示例
using System;  // 引入System命名空间

class Program {
    static void Main() {
        Console.WriteLine("Hello World!");  // 输出并换行
        Console.ReadKey();  // 等待按键(防止控制台闪退)
    }
}
相关推荐
仰泳的熊猫2 小时前
1109 Group Photo
数据结构·c++·算法·pat考试
青山是哪个青山2 小时前
第二节:CMake 命令行工具与工程生命周期
c++·cmake
ozyzo2 小时前
局部变量和全局变量
c++
夏幻灵3 小时前
C++ 里 什么时候不用指针,而选择值拷贝/深拷贝 ?
开发语言·c++·算法
superman超哥3 小时前
仓颉语言中字典的增删改查:深度剖析与工程实践
c语言·开发语言·c++·python·仓颉
青山是哪个青山3 小时前
第一节:CMake 简介
linux·c++·cmake
晨晖23 小时前
二叉树遍历,先中后序遍历,c++版
开发语言·c++
M__333 小时前
动规入门——斐波那契数列模型
数据结构·c++·学习·算法·leetcode·动态规划
wangchen_03 小时前
C/C++时间操作(ctime、chrono)
开发语言·c++