MFC - 消息和事件
应用程序由各种对象组成。 大多数时候,计算机上运行着多个应用程序,操作系统不断被要求执行一些任务。 由于可能会出现如此多不可预测的请求,因此操作系统将其留给对象来指定它们想要什么、何时想要以及它们期望什么行为或结果。
概述
Microsoft Windows 操作系统无法预测一个对象需要处理哪种类型的请求以及另一个对象需要哪种类型的分配。
为了管理所有这些分配和请求,对象会发送消息。
每个对象都有责任决定发送什么消息以及何时发送。
为了发送消息,控件必须创建事件。
为了区分两者,消息的名称通常以 WM_ 开头,它代表"窗口消息"。
事件的名称通常以 On 开头,表示一个操作。
事件是发送消息的动作。
消息映射
由于 Windows 是面向消息的操作系统,因此 Windows 环境的编程很大一部分都涉及消息处理。 每次发生击键或鼠标单击等事件时,都会向应用程序发送一条消息,然后应用程序必须处理该事件。
为了让编译器管理消息,它们应该包含在类定义中。
应在类定义的末尾提供 DECLARE_MESSAGE_MAP 宏,如以下代码所示。
class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: DECLARE_MESSAGE_MAP() };
实际消息应列在 DECLARE_MESSAGE_MAP 行的正上方。
要实现消息,您需要创建程序正在使用的消息表。
该表使用两个定界宏;
它以 BEGIN_MESSAGE_MAP 开始,以 END_MESSAGE_MAP 宏结束。
BEGIN_MESSAGE_MAP 宏采用两个参数,即类的名称和派生类的 MFC 类,如以下代码所示。
#include <afxwin.h> class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: DECLARE_MESSAGE_MAP() }; CMainFrame::CMainFrame() { // Create the window's frame Create(NULL, L"MFC Messages Demo", WS_OVERLAPPEDWINDOW, CRect(120, 100, 700, 480), NULL); } class CMessagesApp : public CWinApp { public: BOOL InitInstance(); }; BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) END_MESSAGE_MAP() BOOL CMessagesApp::InitInstance(){ m_pMainWnd = new CMainFrame; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } CMessagesApp theApp;
让我们通过创建一个新的 Win32 项目来研究一个简单的示例。

步骤 1 − 要创建 MFC 项目,请右键单击该项目并选择"属性"。
步骤 2 − 在左侧部分中,单击 Configuration Properties → General。
步骤 3 − 在"项目默认值"部分中选择"在共享 DLL 中使用 MFC"选项,然后单击"确定"。
步骤 4 − 我们需要添加一个新的源文件。
步骤 5 − 右键单击您的项目并选择 Add → New Item。
步骤 6 − 在"模板"部分中,单击"C++ 文件 (.cpp)"。

步骤 7 − 单击"添加"以继续。
步骤 8 − 现在,在 *.cpp 文件中添加以下代码。
#include <afxwin.h> class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: DECLARE_MESSAGE_MAP() }; CMainFrame::CMainFrame() { // Create the window's frame Create(NULL, L"MFC Messages Demo", WS_OVERLAPPEDWINDOW, CRect(120, 100, 700, 480), NULL); } class CMessagesApp : public CWinApp { public: BOOL InitInstance(); }; BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) END_MESSAGE_MAP() BOOL CMessagesApp::InitInstance() { m_pMainWnd = new CMainFrame; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } CMessagesApp theApp;
Windows 消息
Windows 消息有不同类型,例如创建窗口、显示窗口等。以下是一些常用的 Windows 消息。
消息 | 映射条目 | 描述 |
---|---|---|
WM_ACTIVATE | ON_WM_ACTIVATE() | 当CWnd对象被激活或停用时,框架调用此成员函数。 |
WM_ACTIVATEA PP | ON_WM_ACTIVATEAPP() | 框架向正在激活的任务的所有顶级窗口以及正在停用的任务的所有顶级窗口调用此成员函数。 |
WM_APPCOMM 和 | ON_WM_APPCOMMAND() | 当用户生成应用程序命令事件时,框架调用此成员函数。 |
WM_CANCELMODE | WM_CANCELMODE() | 框架调用该成员函数来通知CWnd取消任何内部模式。 |
WM_CHILDACTIVATE | ON_WM_CHILDACTIVATE() | 如果 CWnd 对象是多文档界面 (MDI) 子窗口,则当用户单击窗口的标题栏或激活、移动窗口或调整窗口大小时,框架将调用 OnChildActivate。 |
WM_CLIPBOAR DUPDATE | ON_WM_CLIPBOARDUPDATE() | 当剪贴板的内容发生更改时,框架调用此成员函数。 |
WM_CLOSE | ON_WM_CLOSE() | 框架调用此成员函数作为 CWnd 或应用程序要终止的信号。 |
WM_CONTEXTMENU | ON_WM_CONTEXTMENU() | 当用户在窗口中单击鼠标右键(右键单击)时由框架调用。 |
WM_COPYDATA | ON_WM_COPYDATA() | 框架调用此成员函数将数据从一个应用程序复制到另一个应用程序。 |
WM_CREATE | ON_WM_CREATE() | 当应用程序请求通过调用Create或CreateEx成员函数创建Windows窗口时,框架调用此成员函数。 |
WM_CTLCOLOR | ON_WM_CTLCOLOR() | 当子控件即将被绘制时,框架调用此成员函数。 |
WM_DELETEITEM | ON_WM_DELETEITEM() | 框架调用此成员函数来通知所有者绘制列表框或组合框的所有者该列表框或组合框已被销毁或项目已被删除。 |
WM_DESTROY | ON_WM_DESTROY() | 框架调用此成员函数来通知 CWnd 对象它正在被销毁。 |
WM_DRAWITEM | ON_WM_DRAWITEM() | 当控件或菜单的视觉方面发生更改时,框架为所有者绘制按钮控件、组合框控件、列表框控件或菜单的所有者调用此成员函数。 |
WM_DROPFILES | ON_WM_DROPFILES() | 当用户在已将自身注册为拖放文件接收者的窗口上释放鼠标左键时,框架将调用此成员函数。 |
WM_ENABLE | ON_WM_ENABLE() | 当应用程序更改 CWnd 对象的启用状态时,框架调用此成员函数。 语法。 |
WM_HELPINFO | ON_WM_HELPINFO() | 处理应用程序中的 F1 帮助(使用当前上下文)。 |
WM_HOTKEY | ON_WM_HOTKEY() | 当用户按下系统范围的热键时,框架调用此成员函数。 |
WM_HSCROLL | ON_WM_HSCROLL() | 当用户单击窗口的水平滚动条时,框架调用此成员函数。 |
WM_KEYDOWN | ON_WM_KEYDOWN() | 当按下非系统键时,框架调用此成员函数。 |
WM_KEYUP | ON_WM_KEYUP() | 当非系统密钥被释放时,框架调用此成员函数。 |
WM_KILLFOCUS | ON_WM_KILLFOCUS() | 框架在失去输入焦点之前立即调用此成员函数。 |
WM_LBUTTONDBLCLK | ON_WM_LBUTTONDBLCLK() | 当用户双击鼠标左键时,框架调用此成员函数。 |
WM_LBUTTONDOWN | ON_WM_LBUTTONDOWN() | 当用户按下鼠标左键时,框架调用此成员函数。 |
WM_LBUTTONUP | ON_WM_LBUTTONUP() | 当用户释放鼠标左键时,框架调用此成员函数。 |
WM_MBUTTONDBLCLK | ON_WM_MBUTTONDBLCLK() | 当用户双击鼠标中键时,框架调用此成员函数。 |
WM_MBUTTONDOWN | ON_WM_MBUTTONDOWN() | 当用户按下鼠标中键时,框架调用此成员函数。 |
WM_MBUTTONUP | ON_WM_MBUTTONUP() | 当用户释放鼠标中键时,框架调用此成员函数。 |
WM_MENUSELECT | ON_WM_MENUSELECT() | 如果 CWnd 对象与菜单关联,则当用户选择菜单项时,框架会调用 OnMenuSelect。 |
WM_MOUSEACTIVATE | ON_WM_MOUSEACTIVATE() | 当光标位于非活动窗口并且用户按下鼠标按钮时,框架调用此成员函数。 |
WM_MOUSEHOVER | ON_WM_MOUSEHOVER() | 当光标悬停在窗口的客户区域上并达到先前调用 TrackMouseEvent 中指定的时间段时,框架将调用此成员函数。 |
WM_MOUSEHWHEEL | ON_WM_MOUSEHWHEEL() | 当当前窗口由桌面窗口管理器(DWM)组成并且该窗口最大化时,框架调用此成员。 |
WM_MOUSELEAVE | ON_WM_MOUSELEAVE() | 当光标离开先前调用 TrackMouseEvent 时指定的窗口客户区时,框架调用此成员函数。 |
WM_MOUSEMOVE | ON_WM_MOUSEMOVE() | 当鼠标光标移动时,框架调用此成员函数。 |
WM_MOVE | ON_WM_MOVE() | 框架在 CWnd 对象移动后调用此成员函数。 |
WM_PAINT | ON_WM_PAINT() | 当 Windows 或应用程序请求重绘应用程序窗口的一部分时,框架调用此成员函数。 |
WM_SETFOCUS() | ON_WM_SETFOCUS( ) | 框架在获得输入焦点后调用此成员函数。 |
WM_SIZE( ) | ON_WM_SIZE( ) | 框架在窗口大小更改后调用此成员函数。 |
WM_TIMER | ON_WM_TIMER() | 框架在用于安装计时器的SetTimer成员函数中指定的每个时间间隔后调用此成员函数。 |
WM_VSCROLL | ON_WM_VSCROLL() | 当用户单击窗口的垂直滚动条时,框架调用此成员函数。 |
WM_WINDOWPOSCHANGED | ON_WM_WINDOWPOSCHANGED() | 当由于调用 SetWindowPos 成员函数或其他窗口管理函数而导致大小、位置或 Z 顺序发生更改时,框架将调用此成员函数。 |
让我们看一个窗口创建的简单示例。
WM_CREATE − 当创建一个称为窗口的对象时,创建该对象的框架会发送一条标识为 ON_WM_CREATE 的消息。
步骤 1 − 要创建 ON_WM_CREATE,请添加 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); 在 DECLARE_MESSAGE_MAP() 之前,如下所示。
class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); DECLARE_MESSAGE_MAP() };
步骤 2 − 在 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 之后、END_MESSAGE_MAP() 之前添加 ON_WM_CREATE()
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP()
步骤 3 − 下面是 OnCreate() 的实现
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Call the base class to create the window if (CFrameWnd::OnCreate(lpCreateStruct) == 0) { // If the window was successfully created, let the user know MessageBox(L"The window has been created!!!"); // Since the window was successfully created, return 0 return 0; } // Otherwise, return -1 return -1; }
步骤 4 − 现在您的 *.cpp 文件将如以下代码所示。
#include <afxwin.h> class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); DECLARE_MESSAGE_MAP() }; CMainFrame::CMainFrame() { // Create the window's frame Create(NULL, L"MFC Messages Demo", WS_OVERLAPPEDWINDOW, CRect(120, 100, 700, 480), NULL); } class CMessagesApp : public CWinApp { public: BOOL InitInstance(); }; BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP() int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Call the base class to create the window if (CFrameWnd::OnCreate(lpCreateStruct) == 0) { // If the window was successfully created, let the user know MessageBox(L"The window has been created!!!"); // Since the window was successfully created, return 0 return 0; } // Otherwise, return -1 return -1; } BOOL CMessagesApp::InitInstance() { m_pMainWnd = new CMainFrame; m_pMainWnd -> ShowWindow(SW_SHOW); m_pMainWnd -> UpdateWindow(); return TRUE; } CMessagesApp theApp;
步骤 5 − 当上面的代码被编译并执行时,您将看到以下输出。

步骤 6 − 当您单击"确定"时,它将显示主窗口。

命令消息
图形应用程序的主要功能之一是提供允许用户与计算机交互的 Windows 控件和资源。 我们将学习的控件示例有按钮、列表框、组合框等。
我们在上一课中介绍的一种资源是菜单。 当用户单击此类控件和资源时,它们可以启动自己的消息。 从 Windows 控件或资源发出的消息称为命令消息。
让我们看一个命令消息的简单示例。
为了使您的应用程序能够创建新文档,CWinApp 类提供了 OnFileNew() 方法。
afx_msg void OnFileNew(); BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_COMMAND(ID_FILE_NEW, CMainFrame::OnFileNew) END_MESSAGE_MAP()
Here is the method definition −
void CMainFrame::OnFileNew() { // Create New file }
键盘消息
键盘是连接到计算机的硬件对象。 默认情况下,它用于在控件上输入可识别的符号、字母和其他字符。 键盘上的每个键都会显示一个符号、字母或这些符号的组合,以指示该键的用途。 用户通常按下一个键,该键会向程序发送信号。
每个键都有一个操作系统可以识别的代码。 此代码称为虚拟键代码。
序号 | 常数/值和描述 |
---|---|
1 | VK_LBUTTON 鼠标左键 |
2 | VK_RBUTTON 鼠标右键 |
3 | VK_CANCEL 控制中断处理 |
4 | VK_MBUTTON 鼠标中键(三键鼠标) |
5 | VK_BACK 退格键 |
6 | VK_RETURN ENTER键 |
7 | VK_TAB TAB键 |
8 | VK_CLEAR 清除键 |
9 | VK_SHIFT SHIFT键 |
10 | VK_CONTROL CTRL键 |
11 | VK_MENU ALT键 |
12 | VK_PAUSE 暂停键 |
13 | VK_CAPITAL 大写锁定键 |
14 | VK_ESCAPE ESC键 |
15 | VK_SPACE 空格键 |
16 | VK_PRIOR 翻页键 |
17 | VK_NEXT 向下翻页键 |
18 | VK_END END键 |
19 | VK_HOME HOME键 |
20 | VK_LEFT 左箭头键 |
21 | VK_UP 向上箭头键 |
22 | VK_RIGHT 右箭头键 |
23 | VK_DOWN 向下键 |
24 | VK_SELECT SELECT键 |
25 | VK_PRINT PRINT 键 |
26 | VK_EXECUTE 执行键 |
27 | VK_SNAPSHOT 打印屏幕键 |
28 | VK_INSERT INS键 |
29 | VK_DELETE DEL键 |
30 | VK_NUMPAD0 数字键盘0键 |
31 | VK_NUMPAD1 数字键盘1键 |
32 | VK_NUMPAD2 数字键盘2键 |
33 | VK_NUMPAD3 数字键盘3键 |
34 | VK_NUMPAD4 数字键盘4键 |
35 | VK_NUMPAD5 数字键盘 5 键 |
36 | VK_NUMPAD6 数字键盘6键 |
37 | VK_NUMPAD7 数字键盘7键 |
38 | VK_NUMPAD8 数字键盘8键 |
39 | VK_NUMPAD9 数字键盘9键 |
40 | VK_MULTIPLY 乘号键 |
41 | VK_ADD 添加密钥 |
42 | VK_SEPARATOR 分隔键 |
43 | VK_SUBTRACT 减法键 |
44 | VK_DECIMAL 十进制键 |
45 | VK_DIVIDE 分割键 |
46 | VK_F1 F1键 |
47 | VK_F2 F2键 |
48 | VK_F3 F3键 |
49 | VK_F4 F4键 |
50 | VK_F5 F5键 |
52 | VK_F6 F6键 |
53 | VK_F7 F7键 |
54 | VK_F8 F8键 |
55 | VK_F9 F9键 |
56 | VK_F10 F10键 |
57 | VK_F11 F11键 |
58 | VK_F12 F12键 |
59 | VK_NUMLOCK NUM LOCK 键 |
60 | VK_SCROLL 滚动锁定键 |
61 | VK_LSHIFT 左SHIFT键 |
62 | VK_RSHIFT 右SHIFT键 |
63 | VK_LCONTROL 左CONTROL键 |
64 | VK_RCONTROL 右 CONTROL 键 |
按下某个键会导致 WM_KEYDOWN 或 WM_SYSKEYDOWN 消息被放置在线程消息中。 这可以定义如下 −
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
让我们看一个简单的例子。
步骤 1 − 这是消息。
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() ON_WM_KEYDOWN() END_MESSAGE_MAP()
步骤 2 − 这是OnKeyDown()的实现。
void CMainFrame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_RETURN: MessageBox(L"You pressed Enter"); break; case VK_F1: MessageBox(L"Help is not available at the moment"); break; case VK_DELETE: MessageBox(L"Can't Delete This"); break; default: MessageBox(L"Whatever"); } }
步骤 3 − 当上面的代码被编译并执行时,您将看到以下输出。

步骤 4 − 当您按 Enter 时,将显示以下消息。

鼠标消息
鼠标是连接到计算机的另一个对象,允许用户与机器交互。
如果按下鼠标左键,则会发送 ON_WM_LBUTTONDOWN 消息。 该消息的语法是 −
afx_msg void OnLButtonDown(UINT nFlags, CPoint point)
如果按下鼠标右键,则会发送 ON_WM_RBUTTONDOWN 消息。 它的语法是 −
afx_msg void OnRButtonDown(UINT nFlags, CPoint point)
同样,如果释放鼠标左键,则发送 ON_WM_LBUTTONUP 消息。 它的语法是 −
afx_msg void OnLButtonUp(UINT nFlags, CPoint point)
如果释放鼠标右键,则发送 ON_WM_TBUTTONUP 消息。 它的语法是 −
afx_msg void OnRButtonUp(UINT nFlags, CPoint point)
让我们看一个简单的例子。
步骤 1 − 在 CMainFrame 类定义中添加以下两个函数,如以下代码所示。
class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnRButtonUp(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP() };
步骤 2 − 添加以下两个消息映射。
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_KEYDOWN() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONUP() END_MESSAGE_MAP()
步骤 3 − 这是函数定义。
void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point) { CString MsgCoord; MsgCoord.Format(L"Left Button at P(%d, %d)", point.x, point.y); MessageBox(MsgCoord); } void CMainFrame::OnRButtonUp(UINT nFlags, CPoint point) { MessageBox(L"Right Mouse Button Up"); }
步骤 4 − 当您运行此应用程序时,您将看到以下输出。

步骤 5 − 单击"确定"后,您将看到以下消息。

步骤 6 − 右键单击该窗口。 现在,当您释放鼠标右键时,将显示以下消息。
