文章目录
-
- [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. 重要结论
- 消息是事件的底层实现:事件是高级抽象,消息是具体实现机制
- 消息映射是桥梁:MFC通过消息映射将Windows消息连接到C++成员函数
- 事件驱动架构:MFC应用程序是事件驱动的,由消息循环驱动
- 层级传递:消息可以在父窗口和子窗口之间传递
- 可定制性:可以通过重写PreTranslateMessage等函数自定义消息处理
12. 最佳实践
- 使用ClassWizard:自动生成消息映射和处理函数
- 合理划分职责:模型-视图-控制器模式
- 避免长时处理:消息处理函数应尽快返回
- 使用工作线程:耗时操作放在工作线程中
- 消息转发:未处理的消息应传递给基类
理解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(); // 等待按键(防止控制台闪退)
}
}