![[VC-MFC编程实例]03.doc_第1页](http://file.renrendoc.com/FileRoot1/2020-1/20/1e52d209-4de4-421e-bcf6-156fc7259b34/1e52d209-4de4-421e-bcf6-156fc7259b341.gif)
![[VC-MFC编程实例]03.doc_第2页](http://file.renrendoc.com/FileRoot1/2020-1/20/1e52d209-4de4-421e-bcf6-156fc7259b34/1e52d209-4de4-421e-bcf6-156fc7259b342.gif)
![[VC-MFC编程实例]03.doc_第3页](http://file.renrendoc.com/FileRoot1/2020-1/20/1e52d209-4de4-421e-bcf6-156fc7259b34/1e52d209-4de4-421e-bcf6-156fc7259b343.gif)
![[VC-MFC编程实例]03.doc_第4页](http://file.renrendoc.com/FileRoot1/2020-1/20/1e52d209-4de4-421e-bcf6-156fc7259b34/1e52d209-4de4-421e-bcf6-156fc7259b344.gif)
![[VC-MFC编程实例]03.doc_第5页](http://file.renrendoc.com/FileRoot1/2020-1/20/1e52d209-4de4-421e-bcf6-156fc7259b34/1e52d209-4de4-421e-bcf6-156fc7259b345.gif)
已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
本文由sha_shoushou贡献 pdf文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。 下载 下载 第3章 消 息 处 理 第1章讨论了 M F C用户界面的基本要素:窗口,窗口类和 C W n d;第2章讨论了构成 M F C 库的其他类,尤其是那些构成 M F C应用程序内核的类.在本章中,我们将讨论 M F C类和它们 的窗口怎样进行互相通信的.我们发现有三种类型的消息:窗口,命令 ( C o m m a n d )和控件通 知(Control Notification),并且这些消息既可以发送 ( s e n t ),也可以寄送 ( p o s t );接着,将跟踪 一个被MFC窗口进程处理的消息;最后,将讨论重定向消息的方法. 3.1 发送或寄送一个消息 第1章已提及,每个窗口使用窗口进程处理发送给它的消息.消息可以来自系统,你的应 用程序或别的应用程序.消息告诉窗口进程执行某个任务 (如初始化自己,绘制或销毁一个窗 口等),或者通知它发生某个事件 (如鼠标正单击窗口 ). 系统或应用程序有两种传输消息的方法:发送消息或寄送消息. 3.1.1 发送一个消息 发送一个消息时,直接调用窗口的窗口进程.通信是即时的,直到窗口进程为调用函数 返回一个结果后,应用程序才能继续. 3.1.2 寄送一个消息 寄送一个消息时,把消息发送到拥有那个窗口的应用程序消息队列中.一有空闲,应用 程序就搜索消息队列,并在消息队列中处理消息,即从队列中删除它们,并将它们发送到即 定窗口.通信将可能延迟,直到目标应用程序获得处理消息的时间.调用函数发送消息后即 返回,但结果只是表示消息寄送成功与否,而不是被调用窗口进程的结果 (见图3-1). 窗口对象 WndProc 地址 被发送的消息直接 调用该窗口的窗口 进程 消息泵 消息n+1 消息n+2 消息n+3 消息n+4 消息队列 当应用程序空闲时, 抽出寄送到队列中 的消息并调用该窗 口的窗口进程 被寄送的消息延迟在 应用程序消息队列中 图3-1 发送消息时通信是即时的,而寄送消息时通信可能延迟 3.1.3 发送一个消息与寄送一个消息的比较 鼠标和键盘消息通常是寄送的,而所有其他消息通常都是发送的.在消息队列中,寄送 下载 有鼠标和键盘消息被处理之后. 第3章 消 息 处 理 33 的消息接受特殊的鼠标和键盘处理.通常,应该尽量发送一个消息,除非想把动作延迟到所 3.2 怎样使用MFC发送一个消息 用MFC发送一个消息的方法是,首先,应获取接收消息的 CWnd类对象的指针;然后,调 用CWnd的成员函数 SendMessage( ). LRESULT Res=pWnd-SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam); p W n d指针指向目标 C W n d类对象.变量 M s g是消息, w P a r a m和l P a r a m变量包含消息的参 数,如鼠标单击哪里或选择了什么菜单项.目标窗口返回的消息结果放在变量 R e s中.发送消 息到一个没有CWnd类对象的窗口,可以用下列目标窗口的句柄直接调用 Windows API: LRESULT Res=:SendMessage(HWND hWnd , UINT Msg , WPARAM wParam , LPARAM lParam); 这里的hWnd是目标窗口的句柄. 3.3 怎样用MFC寄送一个消息 用M F C寄送一个消息与发送一个消息几乎相同,但寄送时用 PostMessage( ) ,而不是用 SendMessage( );返回值 R e s也不一样, R e s不是一个由目标窗口返回的值,而是一个布尔值, 用来表示消息是否成功地放到消息队列中. 检索一个寄送消息 正常情况下,一旦消息被寄送后,应用程序在后台发送它.但是在特殊情况下,需要你 自己去删除一个消息,例如想在应用程序接收到某种消息之前停止应用程序.有两种方法可 以从应用程序消息队列中删除一个消息,但这两种方法都没有涉及 MFC. 第一种方法:在不干扰任何事情之下窥视消息队列,看看一个消息是否在那里. BOOL res=:PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg); 第二种方法:实际上是等待,一直等到一个新的消息到达队列为止,然后删除并返回 该消息. BOOL res=:GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); 在这两种方法中,变量 h W n d指定要截获消息的窗口,如果该变量设为 N U L L,所有窗口 消息将被截获. w M s g F i l t e r M i n和w M s g F i l t e r M a x变量与SendMessage( )中的变量 M s g相对应, 指定查看消息的范围.如果用 0, 0 ,则所有的消息都将被截获.如果用 W M _ K E Y F I R S T, WM_KEYLAST或WM_MOUSEFIRST, WM_MOUSELAST,则所有键盘或鼠标的消息将被截 获. w R e m o v e M s g变 量 指 定 PeekMessage( )是 否 应 该 真 正 地 从 队 列 中 删 除 该 消 息 . (GetMessage( )总是删除消息).该变量可以取两个值: PM_REMOVE,PeekMessage( )将删除消息. PM_NOREMOVE,PeekMessage( )将把消息留在队列里,并返回它的一个拷贝. 当然,如果把消息留在消息队列中,然后再次调用 PeekMessage( )查看相同类型的消息, 则将返回完全相同的消息. 34 第一部分 基 础 知 识 下载 lpMsg变量是一个指向 MSG结构的指针,MSG包含检索到的消息. typedef struct tagMSG HWND hwnd; / window handle message is intended for UINT message; WPARAM wParam; LPARAM lParam; DWORD time; / the time the message was put in the queue POINT pt; / the location of the mouse cursor when the / message was put in the queue MSG; 3.4 三种类型的消息 在MFC应用程序中传输的消息有三种类型:窗口消息,命令消息和控件通知. 3.4.1 窗口消息 窗口消息(Window Message)一般与窗口的内部运作有关,如创建窗口,绘制窗口和销毁窗 口等.通常,消息是从系统发送到窗口,或从窗口发送到窗口. 当用 S e n d M e s s a g e ( )或P o s t M e s s a g e ( )发送一个窗口消息时,变量 M e s s a g e,w P a r a m和 lParam的格式如下: Message WM_XXX wParam 定义的命令 lParam 定义的命令 WM_XXX可以是许多窗口消息之一,如下列窗口: WM_CREATE,告诉窗口初始化自己. WM_PAINT,告诉窗口绘制自己. W M _ M O U S E M O V E,告诉窗口鼠标移经它. 有关某些公共窗口消息,参见附录 B.若需要窗口消息的完全的列表,请参考 MFC文档. 3.4.2 命令消息 命令消息一般与处理用户请求相关,当用户单击一个菜单项或工具栏时,命令消息产生, 并被发送到能处理该请求的类对象 (如,装载文件,编辑文本和保存选项等 ). 当用SendMessage( )或PostMessage( )发送窗口消息时,变量 Message,wParam和lParam的 格式如下: Message WM_COMMAND 0 wParam Command ID lParam 0 Command ID要么是选中菜单项的 I D,要么是被单击的工具栏按钮.注意 Command ID不 能大于一个字长,如果使它大于一个字长,系统就只用 0来填充高位字.某些控件通知也用 WM_COMMAND消息,区别两种消息的唯一方法是 lParam是否为NULL. 3.4.3 控件通知 通常,控件通知在某些重要事件发生时,由控件窗口发送到父窗口,如打开一个组合框. 下载 组合框初建时得不到的消息填充它. 第3章 消 息 处 理 35 控件通知为父窗口进一步控制子窗口提供了机会.例如,打开一个组合框时,父窗口可以用 控件通知经历了一个演变过程,因而 SendMessage( )的变量M e s s a g e,w P a r a m和l P a r a m有 三种格式. 1. 第一控件通知格式 第一控件通知格式只是窗口消息的子集. Message WM_XXX wParam 定义的命令 lParam 定义的命令 WM_XXX可以是下面消息中的任何一种: W M _ PA R E N T N O T I F Y表示一个控件窗口要么已被建立或销毁,要么鼠标已单击了该 窗口. W M _ C T L C O L O R,W M _ D R AW I T E M,W M _ M E A S U R E I T E M,W M _ D E L E T E I T E M, W M _ C H A RTO I T E M,W M _ V K E Y TO I T E M或WM_COMPAREITEM都是送往父窗口的 消息,用来绘制自身的控件窗口. W M _ H S C R O L L或W M _ V S C R O L L由滚动条控件发送,通知父窗口滚动窗口. 2. 第二控件通知格式 第二控件通知格式使用 WM_COMMAND消息,与命令消息共享. Message WM_COMMAND wParam XN_XXX 控件ID lParam 窗口句柄 l P a r a m变量用来区分是命令消息还是控件通知.控件通知在 l P a r a m中有一个有定义的句 柄,用来标识发出通知的控件;而命令消息中 lParam为NULL. X N _ X X X值因发出通知的控件不同而不同,例如, X N _ X X X值为E N _ C H A N G E,告诉父 窗口显示在编辑框控件中的文本已发生变化.还有其他一些例子列在附录 B中. 3. 第三控件通知格式 第三控件通知格式也是最灵活的通知格式,它用 WM_NOTIFY消息. Message WM_NOTIFY wParam 控件ID lParam 指向NMHDR的指针 l P a r a m值指向一个结构,该结构包括有关制作该通知的控件的任何内容,而不受空间和 类型的限制,该结构叫做 NMHDR. typedef struct tagNMHDR HWND hwndFrom; / Window handle of Control Window / making the notification. UINT idFrom; / Control ID of Control Window / making the notification. UINT code; / notification code ex: the user / has clicked the Control Window NMHDR; NMHDR代表通知消息头 (Notification Message Header).为什么要这个头?因为某些控件 用NMHDR作为头发送一个更大结构的消息,即使那些不知道更大结构内容的函数还是能处理 36 通知头. 第一部分 基 础 知 识 下载 3.5 MFC怎样接收一个寄送的消息 M F C处理一个寄送和发送消息的唯一明显不同是寄送的消息要在应用程序的消息队列中 花费一些时间.在消息泵 (message pump)弹出它之前,它要一直在队列中. 消息泵 MFC应用程序中的消息泵在 CWinApp的成员函数 Run( )中.应用程序开始运行时, Run( ) 就被调用, Run( )把时间分割成两部分.一部分用来执行后台处理,如取消临时 C W n d对象; 另一部分用来检查消息队列.当一个新的消息进来时, Run( )抽取它 即用GetMessage( )从 队列中取出该消息,运行两个消息翻译函数,然后用 DispatchMessage( )函数调用该消息预期 的目标窗口进程 (见图3-2). 空闲处理 (内务处理, 后台处理) 新的消息出 现在消息队 列中? 翻译字 符消息 发送消息到 窗口进程 图3-2 消息泵执行后台处理并检查消息队列 消息泵调用的两个翻译函数是 PreTranslateMessage( )和:TranslateMessage( ).目标窗口的 M F C类可调用 P r e Tr a n s l a t e M e s s a g e在发送消息给它之前进行消息翻译,例如, C F r a m e W n d用 P r e TranslateMessage( )将加速键 (如,C t r l + S存储文件 )转换为命令消息.翻译前的消息通常被 处理掉,而翻译后的消息 (如果有的话 )将被重新寄送到队列里. :TranslateMessage是一个窗口函数,将原始键码转换为键字符. 消息一旦被 DispatchMessage( )发送, M F C处理它就像处理 SendMessage( )发送的消息一 样. 3.6 MFC怎样处理一个接收到的消息 处理接收到的消息的目的非常简单:将消息指向一个函数,该函数通过消息中的消息标 识符处理它.非 MFC窗口用简单的case语句来实现该目标,每个 case语句执行一些函数,或调 用其他一些函数. MainWndProc(HWND hWnd, UINT message, W PARAM wParam, LPARAM lParam) switch(message) case WM_CREATE: : : : 下载 break; case WM_PAINT: : : : break; default: return(DefWindowProc(hWnd, message, wParam, lParam); return(NULL); 第3章 消 息 处 理 37 任何遗漏的消息将被传输到一个默认的消息处理函数,但是, c a s e语句不能很好地适应 C+和封装技术.在 C+环境中,要求消息被一个专门处理该类型消息的类的成员函数处理. 因此, M F C不采用 c a s e语句,而采用更加复杂和回旋的方法.但它允许用私有类处理消 息,而只需做下面三件事情: 从将要接收消息的 C W n d类对象派生类(对于命令消息是 CCmdTarget). 在派生类中写一个处理消息的成员函数. 在类中定义一个查找表 (叫做消息映像 ),该表具有成员函数的条目和它要处理的消息的 标识符. 然后,MFC依次调用下面的函数,指引输入消息到处理函数. 1) AfxWndProc( )接收消息,寻找消息所属的 CWnd对象,然后调用 AfxCallWndProc( ). 2) AfxCallWndProc( )存储消息(消息标识符和参数 )供未来参考,然后调用 WindowProc( ). 3) WindowProc( ) 发送消息给 OnWndMsg( ) ,然后,如果消息未被处理,则发送给 DefWindowproc( ). 4) OnWndMsg( )要么为WM_COMMAND消息调用OnCommand( ),要么为 WM_NOTIFY 消息调用 OnNotify( ).任何被遗漏的消息都将是一个窗口消息. OnWndMsg( )搜索类的消息 映像,以找到一个能处理任何窗口消息的处理函数.如果 OnWndMsg()不能找到这样的处理函 数,则把消息返回到 WindowProc( ),由它将消息发送给 DefWindowProc( ). 5) OnCommand( )查看这是不是一个控件通知 绝映射的消息, OnCommand( )就调用OnCmdMsg( ). 6) OnNotify( )也试图将消息映射到制造通知的控件;如果映射不成功, OnNotify( )就调 用相同的OnCmdMsg( )函数. 7) 根据接收消息的类, OnCmdMsg( )将在一个称为命令传递 (Command Routing)的过程中 潜在地传递命令消息和控件通知.例如,如果拥有该窗口的类是一个框架类,则命令和通知 消息也被传递到视图和文档类,并为该类寻找一个消息处理函数. 为什么要消息映像? 为什么要消息映像?这毕竟是C+语言;为什么OnWndMsg( )不为每个窗口消息调 用一个预定义的虚拟函数?因为它太占 CPU.若是那样,当扫描一个消息映像以加速 该过程时,OnWndMsg( )可能会做出意想不到的事情,并陷入汇编器. 注意 通过重载 WindowProc( ) ,OnWndMsg( ) ,OnCommand( ) ,OnNotify( ) 或 ( l P a r a m不是 N U L L );如果它是, OnCommand( )就试图将消息映射到制造通知的控件;如果它不是一个控件通知,或者控件拒 38 第一部分 基 础 知 识 下载 OnCmdMsg( )可以修改这一过程.重载OnWndMsg( )可以在窗口消息被排序之前插入该 过程.重载OnCommand( )或OnNotify( )可以在消息被反射之前插入该过程. 消息映射和命令传递将在下面部分进行详细讨论. 现在,一步一步地跟踪窗口进程接收和解释一个消息. 1. AfxWndProc( ) 所有MFC窗口的窗口进程是: LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam); 如果一个消息被发送,则 SendMessage( )本质上直接调用 AfxWndProc( );如果一个消息 被寄送,则消息泵通过 DispatchMessage( )调用AfxWndProc( ). AfxWndProc( )要做的第一件事是找到目标窗口的 C W n d对象.虽然一个窗口不知道它的 CWnd对象的任何情况,但应用程序在映像中跟踪窗口和类的配对. 一旦CWnd对象被找到,AfxCallWndProc( )就会被调用. 2. AfxCallWndProc( ) AfxCallWndProc( )的原型是: LRES U LT AfxCallWndProc(CWnd *pWnd, HWND hWnd, UINT nMsg, WPARAM wParam, LPA R A M lParam); AfxCallWndProc( )保存消息为将来引用.事实上,即使以后改变消息中的参数,当窗口 进程用 Default( )成员函数进行默认处理时,窗口进程也将引用保存在这里的参数;一旦消息 保存,WindowProc( )就被调用. 3. WindowProc( ) WindowProc( )的原型函数为: LRESULT WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam); WindowProc( )接着调用 OnWndMsg( ),它试图在类中为该消息寻找一个处理函数;任何 返回到WindowProc( )的未被处理的消息都被传输到 D e f WindowProc( );D e f WindowProc( )是 所有未被处理的消息的贮藏所.如果没有消息处理函数,并不意味着该消息不重要,例如, 几乎不需处理 WM_PAINT消息,但它是重要的,因为 D e f WindowProc( )用该消息来绘制窗口 的非客户区. 注意 MFC的CControlBar类(工具栏,对话条和状态栏的基类)重载WindowProc( ),如 果任何一个下面所列的消息未经处理就传给了控制条,它们将被发送回它的父类 (通常 是主框架类). WM_NOTIFY WM_DELETEITEM WM_COMMAND WM_COMPAREITEM WM_DRAWITEM WM_VKEYTOITEM WM_MEASUREITEM WM_CHARTOITEM 也可以在父窗口中自动地处理工具栏,状态栏或对话条的按钮.但是,应该尽可 能地将这些功能封装到工具栏,状态栏或对话条类中. CMainFrame( )类具有非常好的 集成特性. 4. OnWndMsg( ) OnWndMsg( )的原型函数为: BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pResult); 下载 第3章 消 息 处 理 39 当消息为WM_COMMAND时,OnWndMsg( )调用OnCommand( );当消息是WM_NOTIFY 时,则On WndMsg( )调用OnNotify( ).所有其他消息都被认为是窗口消息,并且 OnWndMsg( ) 搜索类中的消息映像以找到一个处理函数. 消息映像 OnWndMsg( )通过搜索类和派生类中的消息映像为窗口消息寻找一个消息处理函数.消 息映像是数据静态表,包含类中每个消息处理函数的一个条目.每个 CWnd的派生类可以有一 个消息映像 (见图3-3). CWnd2对象 如果CWnd在该消息映 像中不能找到一个消息 处理函数,则在下一个 派生CWnd的类中寻找 两个类从 CWnd 派生 CWnd1派生于 CWnd1对象 CWnd,CWnd2派生于 CWnd1.每个派生类 都有它自己的消息映 像 当消息进入 CWnd 中 时,首先在最顶层派 生类的消息映像中寻 找一个处理函数 CWnd对象 如果 CWnd找到一 个消息处理函数, 则用该消息调用它 图3-3 OnWndMsg( )搜索消息映像为窗口消息定位消息处理函数 每个消息映像都被括在两个宏之间 BEGIN_MESSAGE_MAP(CXxx,CYyy) : : : Message Map entries : : : END_MESSAGE_MAP C X x x是该类的派生类名, C Y y y是OnWndMsg( )要搜索的下一个派生类名,通常 C X x x派 生于C Y y y;在下一个类中的 B E G I N _ M E S S A G E _ M A P宏标识派生它的类,以此类推,直到所 有类中的消息映像都被搜索为止.除了为 OnWndMsg( )标识消息映像的位置外,可以忽略这 些宏是怎样工作的. 每个消息映像条目使用下面的结构: struct AFX_MSGMAP_ENTRY UINT nMessage; UINT nCode; UINT nID; UINT nLastID; UINT nSig; AFX_PMSG pfn; 40 ; 第一部分 基 础 知 识 下载 UNINT UNINT nMessage标识特定的 WM_XXX消息. nCode是控件通知代码,对于窗口消息该值为 0.我们将在下文中看到,处理命 令消息和控件通知的函数使用与此相同的消息映像. UINT UINT nID是命令ID,对于窗口消息该值为 0. nLastID是以nID开始的命令 ID范围内的最后一个命令 ID, 对于窗口消息该值为 0. nSig定义消息处理函数的调用变量.在 A F X M S G _ . H中,为n S i g预定义了 6 0多个 处理命令消息的函数能够处理某一范围的 ID. UINT 值,例如, n S i g值为i w w,则在调用消息处理函数前,使 OnWndMsg( )格式化 w P a r a m 和lParam为两个UINT变量,返回值为整型. AFX_PMSG pfn是消息处理函数的地址. 因此,当 OnWndMsg( )得到一个窗口消息时,在它的派生类中寻找第一个消息映像.然 后, OnWndMsg( ) 扫描那里的条目进行 n M e s s a g e匹配,如果找到,它就格式化 w P a r a m和 lParam为nSig指定的调用变量,并调用 pfn指定的函数. 如果没有在第一个消息映像中找到一个条目,它将检查 B E G I N _ M E S S A G E _ M A P宏找出 下一个要扫描的消息映像,以此类推,直到检查完一个对象中的所有消息映像为止.如果 OnWndMsg( )扫描一个对象中的所有消息映像后,没有发现消息处理函数,则窗口消息返回 给WindowProc( ),接着由它将消息发送给 DefWindowProc( ). 消息映像宏 A F X M S G _ . H不仅包含 n S i g的预定义值,还包含一些有助于简化创建消息映像条目的宏. 这些宏以两种格式出现: O N _ M E S S A G E宏用来处理任何 W M _ X X X消息. 一个以 O N _ W M _ X X X形式出现的窗口消息宏,这里的 W M _ X X X是任何当前定义的窗 口消息. 通常,应该用 Class Wi z a r d自动地将这些宏添加到类的消息映像中 (见例5 9 ).但是,不是 所有这些宏都可以被自动添加,这时,可以通过参考附录 B中宏的详细列表,手工添加这些 宏. 5. OnCommand( ) 下面,继续讨论WM_COMMAND消息,上面我们已经看到,它们被发送到 OnCommand( ) 进行处理. LRESULT OnCommand(WPARAM wParam, LPARAM lParam); 此时,一个 W M _ C O M M A N D 消息既可以是命令消息,也可以是一个控件通知. OnCommand( )查看l P a r a m是不是一个有效的窗口句柄,如果是,则 OnCommand( )以控件通 知处理该消息,这里 lParam是控件的句柄,并且将消息反射到发送它的窗口. 消息反射 前面已提及,当控件状态发生变化时,控件通知被发送到父窗口.因此,控件通知提供 给父窗口有限的能力,用来定制和补充控件工作的方式.例如,当一个组合框下拉时,父窗 口通过修改下拉列表内容作出反应. 然而,把这项功能交给父窗口违背了面向对象编程的意图,因为它要求每个对象都应该 下载 第3章 消 息 处 理 41 包括所有属于它自己的功能.在上面的例子中,每次组合框移到一个新的父窗口中,则下拉 功能也要移到新的父窗口.因此,将控件通知处理功能交给控件会更可取.因为每个控件已 经有一个可以通过重载添加其他功能的 M F C类,所以若能按照那列表来处理控件通知的话将 会是非常好的.这样,每当把控件移到一个新的父窗口时,不必考虑必须把哪些代码拷贝到 新的父窗口.MFC用一个叫做消息映射的过程来支持该项功能. 无论什么时候,当 M F C窗口刚刚接收到一个控件通知时,它知道该通知有一个控件窗口 在某处,并且很可能有一个 M F C派生类在控制它,因此, M F C窗口试图将那通知映射回到 M F C派生类,并给它处理该通知的机会 (见图3 - 4 ).用这种方法可以将任何一个控件的所有功 能放到一个井然有序的类包里. CMyButton 可能有一个 该消息的处理函数,若 没有,则CMyDialog使 它返回 CMyButton 类对象 CMyDialog 类对象 CMyDialog 用子分类截获该消 息,查看它是否是控件通知. 若是,则把它提供给控制该窗 口的MFC类,即CMyButton 如果 CMyDialog也不接收 该通知,则最后由对话窗 口进程处理它 创建一个 CMyButton类 和一个CMyDialog类, 以及它们的窗口对象, 使按钮控件成为该对 话的一个子窗口. 按钮窗口 进程 对话窗口 进程 按钮窗口进程发送控 件通知到它的父窗口, 如BN_CLICKED 图3-4 消息映射提供了使控件通知返回到控件窗口的功能 如果控件不想该消息返回, OnCommand( ) 如处理通用消息一样处理该消息,并调用 OnCmdMsg( ). 6. OnNotify( ) WM_NOTIFY消息被发送到OnNotify( )函数. BOOL OnNotify(WPARAM wParam , LPARAM lParam, LRESULT &lResult); 任何发送到这里的消息都自动地被认为是一个控件通知,并且消息被反射.如果控件类 不想该消息返回,则 OnNotify( )调用OnCmdMsg( ). OnCommand( )和OnNotify( )都调用ReflectLastMsg( ),以提供一个控件通知返回到它的 控件窗口. ReflectLastMsg( ) 获取控件窗口的窗口句柄,并在它的消息映像中寻找 W M _ C O M M A N D + W M _ R E F L E C T _ B A S E或W M _ N O T I F Y + W M _ R E F L E C T _ B A S E和通知代 码. 消息映像宏 有三组预定义的宏 (每种控件通知格式对应一组 ),在控件窗口消息映像中,可以用它来处 理它所反射的消息: ON_WM_XXX_REFLECT( )用于第一控件通知格式. 42 第一部分 基 础 知 识 下载 ON_CONTROL_REFLECT( ON_NOTIFY_REFLECT( )用于第二控件通知格式. )用于第三控件通知格式. 请参考附录 B,以获得有关的详细列表. 7. OnCmdMsg( ) 如果一个控件通知被它的控件窗口拒绝,则 OnCommand( )和OnNotify( )通过调用下面的 函数,像处理命令消息一样处理它: BOOL OnCmdMsg(UINT nID, int nCode ,void*pExtra, AFX_CMDHANDLERINFO * pHandlerInfo); 这里: UINT Int nID是命令 ID. nCode是通知代码.对于一个命令消息,该变量将赋值为 CN_COMMAND(相当于0). *pExtra取决于正被处理的消息的类型: Pextra包含的内容 指向NMHDR的AFX_NOTIFY结构的指针 指向CCmdUI派生类的指针 (后面将讨论 ) NULL Void 消息类型 WM_NOTIFY控件通知 菜单项和工具栏更新 (启用,禁用等 ) 任何其他类型 当OnCommand( ) 或OnNotify( ) 调用 OnCmdMsg( ) 时, A F X _ C M D H A N D L E R I N F O *pHandlerInfo总是为NULL;当该变量不包含一个 AFX_CMDHANDLERINFO结构指针 时,OnCmdMsg( )不执行任何它所找到的命令消息处理函数.相反, OnCmdMsg( )只 是返回它本应执行的处理函数的地址. struct AFX_CMDHANDLERINFO CCmdTarget* pTarget; / the object the function resides in void (AFX_MSG_CALL CCmdTarget:*pmf)(void); / the function address ; 当启用或禁用菜单项和工具栏按钮时,将使用该附加功能,我们将在下文中讨论.接着, 在一个称为命令传递的过程中,命令消息和控件通知被提供给一些类. 命令传递 OnCmdMsg( )实际上是 C C m d Ta rg e t的成员函数,而不是 C W n d的成员函数.认识这一点 很重要,因为它允许任何从 C C m d Ta rg e t派生的类接收一个命令消息,即使那些没有一个窗口 的类也可以.例如,文档类没有相关联的窗口,它依赖视图类显示它的文档.但是一个文档 类是处理一个装载或保存命令的最好的地方,因此,虽然一个文档类不能处理类似于 W M _ C R E AT E或 W M _ D E S T R O Y的 命 令 消 息 . 但 OnCmdMsg( )允 许 它 处 理 类 似 于 WM_COMMAND和WM_NOTIFY的命令消息.CWnd本身是从CCmdTarget派生而来的, 因此, 它也能支持命令消息,如图 3-5所示. 在所有的主应用程序类 (应用程序,框架,视图和文档类 )中,MFC通过重载OnCmdMsg( ) 执行命令传递.例如,视图类重载 OnCmdMsg( ) ,使它能为文档类提供命令和控件通知消 息;文档类重载 OnCmdMsg( ),使它能为文档模板类提供命令消息.下面的列表显示了命令 和控件通知怎样被传递到应用程序的主类: 下载 框架消息映 像 在查看它自己的消 息映像以前,框架 类中的OnCmdMsg( ) 首先调用视图类的 OnCmdMsg( ) 视图消息映 像 文档消息映 像 第3章 消 息 处 理 43 在初始的 ID_FILE_SAVE 消息被框架窗口接收以后, 该消息使用OnCmdMSg( ) 重载函数在类间传输 框架类中的 OnCmdMsg() 重载函数 视图类中的 OnCmdMsg() 重载函数 文档类中的 OnCmdMsg() 重载函数 文档类中的 OnCmdMsg( ) 检查消息映像,如果找到 OnSaveFile( ),则调用它 视图类中的OnCmdMsg()函 CWnd窗口 进程 数检查它的消息映像,如果 没找到任何东西,则调用文 档类中的OnCmdMsg( ) 主菜单发送 ID_ FILE_SAVE 消息到框架窗口进程 图3-5 CCmdTarget支持没有窗口的MFC对象的消息映像 OnCmdMsg( )命令传递 发送到MFC类的命令消息 CFrameWnd 按下面的顺序传递到所列类中 拥有输入焦点的所有 Cview的派生类 它自身的消息映像 所有CwinApp的派生类 CMDIFrameWnd 拥有输入焦点的所有 CMDICHildWnd的派生类 拥有输入焦点的所有 Cview的派生类 它自身的消息映像 所有CwinApp的派生类 CMDIChildWnd CView CDocument CDialog 它自身的消息映像 它自身的消息映像 所有CDocument的派生类 它自身的消息映像 所有CDocumentTemplate的派生类 它自身的消息映像 它的父类的消息映像 它的线程的消息映像 CPropertySheet 它自身的消息映像 它的父类的消息映像 它的线程的消息映像 传递效果具有累积性.被发送到 C M D I F r a m e W n d的命令消息,首先被提供给有效的 C M D I F r a m e W n d中的消息映像,接着给有效的 C Vi e w,有效 C Vi e w的C D o c u m e n t,那个文档 的CDocTemplate,CMDIFrameWnd自身的消息映像,最后给 CWinApp. 一旦 OnCmdMsg( ) 找到一个消息处理函数,命令传递就停止.在前面的例子中,如果 OnCmdMsg( )在C D o c u m e n t中找到一个处理函数,它将用命令消息调用该处理函数并返回结 果,而不用继续传递给 CDocTemplate. 消息映像宏 虽然命令消息和控件通知被传递的方法是相同的,但是它们预定义的消息映像宏却是不 同的.命令消息采用的形式为: 44 第一部分 基 础 知 识 下载 ON_COMMAND( ) 而控件通知采用的一般形式为: ON_CONTROL( ) ON_NOTIFY( ) 为WM_COMMAND通知 为WM_NOTIFY通知 参见附录B的详细列表. 参见图3-6. O n W n d M s g ( )发送W M _ C O M M A N D消息到 O n N o t i f y ( ),发 送WM_NOTIFY到OnNotify( ), 而为剩下的消息在找到的类的 消息映像中寻找一个处理函数 AfxWndProc( )找到拥有该窗 WindowProc( )调 口的类对象,并调用 用OnWndMsg( ) AfxWndCallProc( ) 根据类的类型, On C m d M s g ( )可能调用其 他类中OnCmdMsg()重 载函数.否则,在该 类的消息映像中搜索 一个处理函数 发送到所有由MFC 创建的窗口中的消息 都被AfxWndProc( ) 处理 AfxWndCallproc( )保 任何未被OnWnd Msg()处 存消息后调用找到的 类的WindowProc( ), 从现在起,所有被调 用的函数都可以被重 载 理的消息被发送到 DefWindowProc( ) OnCommand( ) 和OnNotify()把控件通 知反射回到控制类(未显示出来).任何 未被反射的消息被发送到OnCmdMsg( ) 通过完成所有这些过程,消息可以在类中被处理, 控件通知可以被反射回到它们的控件,而命令消息 可以被传递到应用程序的任何地方 图3-6 描述MFC如何处理接受的信息 3.7 处理用户界面的对象 刚刚讨论过的一些成员函数也被 M F C 用来更新用户界面的状态.具体地讲, M F C用 OnCmdMsg( )来启用,禁用或检查一个菜单中的所有菜单项和控制条中的所有控件 (工具栏, 对话条和状态栏 ).无论何时打开一个菜单项, M F C都将更新菜单项的状态;每当应用程序空 闲时,它就更新控制条中控件的状态. 对于任一事件, M F C 进入一个循环,在该循环中为每个菜单项或控制条控件调用 OnCmdMsg( ),最多可达两次.第一次调用 OnCmdMsg( )时,应用程序可以自己启用或禁用菜 单项或控件;如果不能为请求的行为提供一个特定的用户界面处理函数,则 OnCmdMsg( )被 第二次调用,以检查菜单项或控件究竟有没有一个消息处理函数,如果没有, OnCmdMsg( ) 将自动禁用菜单项或控件. 注意 如果想关闭这一自动禁用特性,可以将 CMainFrame类中的m_bAutoMenuEnable 设为FALSE.但是,这对工具栏不起作用. 当OnCmdMsg( )被调用,并允许应用程序更新界面时,它被赋予如下参数: OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, CCmdUI *pCmdUI, NULL); n I D是需要更新的菜单项或控制条控件的控件 ID. 下载 第3章 消 息 处 理 45 * p C m d U I指向一个类,该类包含允许应用程序更新菜单项和控件的成员函数. 接着,按命令消息中相同的次序, OnCmdMsg( ) 仔细查看相同的消息映像,直到发现一 个特定的用户界面消息处理函数. 处理CN_UPDATE_COMMAND_UI的预定义的宏是: ON_UPDATE_COMMAND_UI(id, Handler); 对于某一范围内的控件 ID,预定义的宏是: ON_UPDATE_COMMAND_UI_RANGE(id, idLast, Handler); 对于每个宏,处理函数有相同的格式: void Handler(CCmdUI *pCmdUI) pCmdUI-Enable(pFlag); CCmdUI对象包含如下重要的成员变量和函数: m _ n I D是控件 I D,当某个范围的控件 I D可以用相同的处理函数更新时,它对第二宏格 式是有用的. Enable (BOOL bOn),如果bOn设为FALSE,将使菜单项或控件无效. (int nCheck),如果nCheck为1,则将检查菜单项. (BOOL bOn),更新单选按钮. SetCheck SetRadio S
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年分级设备地矿勘测设备:钻探机合作协议书
- Heptadecanonyldethio-CoA-Heptadecanonyldethio-coenzyme-A-生命科学试剂-MCE
- Glycidyl-myristate-d5-Myristic-acid-glycidyl-ester-d-sub-5-sub-生命科学试剂-MCE
- 2025安徽滁州市明光市消防救援大队招聘政府专职消防员15人考前自测高频考点模拟试题参考答案详解
- 小学元旦安全教育培训课件
- 2025广东湛江市坡头区社会保险基金管理局招聘编外人员1人模拟试卷带答案详解
- 2025年琼海市校园招聘教育类专业技术人才(西安站)考前自测高频考点模拟试题及答案详解(名校卷)
- 生产安全管理制度执行记录表安全事故预防功能
- 2025年泉州德化县公办学校专项招聘编制内新任教师19人(二)模拟试卷及答案详解(考点梳理)
- 会议在线互动式管理与决策模板
- 预防老年误吸的课件
- 2025年国家能源投资集团有限责任公司校园招聘笔试备考题库附答案详解(综合题)
- 2025年零碳园区综合能源技术发展现状与展望报告-华电电科院
- 环保工程现场施工方案(3篇)
- 索尼微单相机A7 II(ILCE-7M2)使用说明书
- 中级护理真题题库及答案解析
- 一年级新生开学第一课常规训练
- 直播助农培训课件
- 长期照护师抗压考核试卷及答案
- 钢箱梁桥面铺装施工细节及专项方案研究
- 2025版自然人个人创业孵化器贷款协议
评论
0/150
提交评论