第 13 章
2)在类的实现文件中加入消息映shè表:
BEGIN_MESSAGE_MAP(类名, 父类名)
??
消息映shè表项
??
END_MESSAGE_MAP()
在CTestApp 类的实现文件TestApp.cpp 的开头、常量定义的后面,就有如下消息映shè表
的代码:
/////////////////////////////////////////////////////////////////////////////
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·44·
// CTestApp
BEGIN_MESSAGE_MAP(CTestApp, CWinApp)
//{{AFX_MSG_MAP(CTestApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file based doccomnt commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
MFC AppWizard 生成的应用程都已经自动包含消息映shè的代码,但用户也可以在源程序
中手动添加。同时Visual C++另一个强大的工具ClassWizard 也提供了处理消息映shè表的自动
化机制。当然,ClassWizard 只能够生成类和消息处理的框架,包括创建消息处理函数的声明,
而具体的实现代码还需要用户去补充。
在类中,消息处理函数都是类的成员函数。当程序要对一个消息进行响应时,消息映shè
机制就会自动调用该函数。用ClassWizard 添加消息处理函数时,会自动添加以下3 部分内
容:
? 在类的定义中添加该消息处理成员函数的函数声明;
? 在类的消息映shè表中加入相应的消息映shè表项;
? 在类的实现中加入该消息处理成员函数的函数实现。
注意ClassWizard 添加的函数实现中,只是一个不能完成任何cāo作的空函数框架,还需
要加入具体的实现代码。但当用户手动添加消息处理函数时,也必须添加上述3 部分内容。
对于标准Windows 消息,在CWnd 类中已经预定义了默认的处理函数,有确定的函数名,
一般以On 开头,以消息名为函数名的后半部分,例如WM_PAINT 消息的处理函数为
OnPaint(),WM_CREATE 消息处理函数是OnCreate()。这些预定义的消息处理函数根据消息
的不同有不同的参数和返回值。
对于控件通知消息和命令消息,一般没有默认的消息处理函数,可以由用户自定义。MFC
建议这类函数命名应遵循一些约定,一般以On 开头,函数名中应包含该消息的简要说明,
方便阅读代码。ClassWizard 在创建这类消息处理函数时会提供一个建议的函数名,一般无需
修改。例如OnFileOpen()函数表明处理命令标识符ID_FILE_OPEN 的命令消息。
本章和以后的章节都将主要利用ClassWizard 为程序添加消息处理函数。因此下面简要
介绍一下ClassWizard 对话框,如图3-1 所示。在IDE 主菜单栏中选择“View|ClassWizard”
菜单命令,或者在开发环境的界面中直接按快捷键Ctrl+W,即可打开“MFC ClassWizard”
对话框。选择“Message Maps”的选项卡,即可进行编辑消息映shè的设置。
枫叶文学网www.fywxw.com
第3 章 菜单与加速键
·45·
图3-1 消息映shè选项卡
ClassWizard 对话框的“Message Maps”(消息映shè)选项卡中的各项功能介绍如下:
? Project(工程)下拉列表框:用于选择需要编辑的工程名称。
? Class ncom(类名)下拉列表框:用于选择需要编辑消息映shè的类的名称。选择好类
后,在该下拉列表框 和“Project”下拉列表框的下一行将显示所选类的头文件和源文
件路径。
? Object IDs(对象ID)列表框:以列表形式显示可以产生消息的对象的ID,包括菜单
项、按钮等的ID。该列表框的第一项总是显示所选择的类。
? Messages(消息)列表框:列举在“Object IDs”列表框中选定的对象所能产生的各种
消息。
? Member functions(成员函数)列表框:以列表形式显示选定的类中的成员函数。该列
表框中只列出与消息处理函数以及MFC 中与消息处理有关的虚函数。前者标记为
“W”,并在函数名后显示对应的消息映shè宏;后者标记为“V”。
? Add Class(添加类)按钮:单击该按钮,添加新的类到工程中。该按钮在“MFC
ClassWizard”对话框中的所有选项卡中都存在。
? Add Function(添加函数)按钮:单击该按钮添加选定消息的新的消息处理函数,包
括在消息映shè定义中添加对应的宏指令,在选定的类中添加该消息处理函数的声明和
完成基本功能的代码实现。如果选定的消息已经有消息处理函数,则该按钮无效。对
于对象产生的消息,单击该按钮将弹出如图3-2 所示的对话框,要求用户输入消息处
理函数的名称,在“Member function ncom”(成员函数名称)输入框中输入函数名,
确定后ClassWizard 就会自动添加该消息处理函数。对于由该选定的类产生的消息,
MFC 已经定义好了消息处理函数名称,单击“Add Function”按钮将直接添加该消息
处理函数,不会提示用户输入自定义的函数名。注意,只有单击“MFC ClassWizard”
的“OK”按钮或者“Edit Code”按钮退出“MFC ClassWizard”对话框后,ClassWizard
才会真正修改源文件。
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·46·
图3-2 添加消息处理函数的对话框
? Delete Function(删除函数)按钮:单击该按钮删除选定消息的消息处理函数,包括函
数的声明和定义该消息映shè的宏语句。如果选定的消息没有消息处理函数,则该按钮
无效。注意,当该函数已经存在于源文件中时,单击该按钮会提示如图3-3 所示的对
话框,告诉用户ClassWizard 只能删除该函数的声明和定义该消息映shè的宏语句,而
无法删除该函数的实现代码,用户需要手动删除。
图3-3 提示用户手动删除消息处理函数的对话框
? Edit Code(编辑代码)按钮:单击该按钮退出“MFC ClassWizard”对话框,同时IDE
的代码编辑窗口直接转到选定消息的处理函数进行编辑。该按钮也使得在“MFC
ClassWizard”中所做的更改生效。如果选定的消息没有消息处理函数,则该按钮无效。
? OK(确定)按钮:单击该按钮使得在“MFC ClassWizard”中的编辑生效,并退出“MFC
ClassWizard”对话框。
? Cancel(取消)按钮:单击该按钮取消所有“MFC ClassWizard”对话框中的设置,并
退出“MFC ClassWizard”对话框。
3.1.3 消息处理过程
在Windows 程序设计中,消息的处理是通过消息循环进行接收与分发的。MFC 运用消
息映shè机制封装了这一处理过程。下面介绍MFC 中消息的处理过程。
1.标准Windows 消息和控件通知消息
标准Windows 消息和控件通知消息的处理过程比较简单。这类消息必须由CWnd 或其派
生类的对象处理,消息传递到封装对应的窗口或子窗口的类的对象中。也就是说,当关闭程
序的时候,会产生一个WM_CLOSE 消息并通知主框架类,主框架类调用相应的处理函数进
行程序关闭前的清理cāo作,如保存文件和数据等。
2.命令消息
命令消息与其他两个类型的消息不同,它可以由应用程序其他类的对象(例如应用类对
象、文档对象和文档模板对象)处理。这就涉及一个消息传递路径的问题,具体来说就是允
枫叶文学网www.fywxw.com
第3 章 菜单与加速键
·47·
许一个还是多个对象处理该消息、允许哪些对象处理等。MFC 中采取了简单的方法,只允许
其中一个对象处理该消息,当沿着消息传递路径找到第一个包含该消息的处理函数的对象时,
则处理该消息,然后中断传递过程,不再搜索后续的对象。
由于命令消息可以被多种类的对象处理,因此命令消息的传递路径比较复杂,但总体思
想是由子窗口到父窗口,由窗口到文档。下面举一个简单的例子。
假设是一个多文档MDI 应用程序,用户触发了“编辑”菜单下的“粘贴”菜单项的命令
消息,此时消息依照以下顺序传递。
(1)MDI 主框架窗口首先收到此命令消息。
(2)主框架将处理命令的机会先让给其当前活动的MDI 子框架窗口。
(3)子框架窗口又将处理该命令的机会让给其活动的视图。
(4)视图先搜索自己的消息映shè,寻找对应的处理函数,如果没找到,则将消息传递给
与其关联的文档,找到则调用并中止传递过程。
(5)文档搜索自己的消息映shè,寻找对应的处理函数。一般情况下文档中包含“粘贴”
消息的处理函数,调用该处理函数完成粘贴功能。如果没有找到,文档将消息传递给文档模
板。
(6)文档模板进行相同的过程,如果没找到,将消息传递回子框架窗口。
(7)子框架窗口搜索对应的处理函数,如果没找到,则将消息传回MDI 主框架窗口。
(8)如果主框架窗口中也没找到,则消息最后停留在应用对象,不再进行进一步的传递。
从上面的叙述可以看出,命令消息的传递过程很复杂。然而一般情况下不需要了解这么
详细的消息传递路径,只需根据完成功能的要求,在相应的类中添加实现代码,就不会产生
冲突,比如例子中提到的粘贴功能,一般就是由文档完成的,因此只需在文档类中添加这个
命令消息的处理函数,而不必在别的类中添加与
松语文学免费小说阅读_www.16sy.com
BEGIN_MESSAGE_MAP(类名, 父类名)
??
消息映shè表项
??
END_MESSAGE_MAP()
在CTestApp 类的实现文件TestApp.cpp 的开头、常量定义的后面,就有如下消息映shè表
的代码:
/////////////////////////////////////////////////////////////////////////////
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·44·
// CTestApp
BEGIN_MESSAGE_MAP(CTestApp, CWinApp)
//{{AFX_MSG_MAP(CTestApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file based doccomnt commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
MFC AppWizard 生成的应用程都已经自动包含消息映shè的代码,但用户也可以在源程序
中手动添加。同时Visual C++另一个强大的工具ClassWizard 也提供了处理消息映shè表的自动
化机制。当然,ClassWizard 只能够生成类和消息处理的框架,包括创建消息处理函数的声明,
而具体的实现代码还需要用户去补充。
在类中,消息处理函数都是类的成员函数。当程序要对一个消息进行响应时,消息映shè
机制就会自动调用该函数。用ClassWizard 添加消息处理函数时,会自动添加以下3 部分内
容:
? 在类的定义中添加该消息处理成员函数的函数声明;
? 在类的消息映shè表中加入相应的消息映shè表项;
? 在类的实现中加入该消息处理成员函数的函数实现。
注意ClassWizard 添加的函数实现中,只是一个不能完成任何cāo作的空函数框架,还需
要加入具体的实现代码。但当用户手动添加消息处理函数时,也必须添加上述3 部分内容。
对于标准Windows 消息,在CWnd 类中已经预定义了默认的处理函数,有确定的函数名,
一般以On 开头,以消息名为函数名的后半部分,例如WM_PAINT 消息的处理函数为
OnPaint(),WM_CREATE 消息处理函数是OnCreate()。这些预定义的消息处理函数根据消息
的不同有不同的参数和返回值。
对于控件通知消息和命令消息,一般没有默认的消息处理函数,可以由用户自定义。MFC
建议这类函数命名应遵循一些约定,一般以On 开头,函数名中应包含该消息的简要说明,
方便阅读代码。ClassWizard 在创建这类消息处理函数时会提供一个建议的函数名,一般无需
修改。例如OnFileOpen()函数表明处理命令标识符ID_FILE_OPEN 的命令消息。
本章和以后的章节都将主要利用ClassWizard 为程序添加消息处理函数。因此下面简要
介绍一下ClassWizard 对话框,如图3-1 所示。在IDE 主菜单栏中选择“View|ClassWizard”
菜单命令,或者在开发环境的界面中直接按快捷键Ctrl+W,即可打开“MFC ClassWizard”
对话框。选择“Message Maps”的选项卡,即可进行编辑消息映shè的设置。
枫叶文学网www.fywxw.com
第3 章 菜单与加速键
·45·
图3-1 消息映shè选项卡
ClassWizard 对话框的“Message Maps”(消息映shè)选项卡中的各项功能介绍如下:
? Project(工程)下拉列表框:用于选择需要编辑的工程名称。
? Class ncom(类名)下拉列表框:用于选择需要编辑消息映shè的类的名称。选择好类
后,在该下拉列表框 和“Project”下拉列表框的下一行将显示所选类的头文件和源文
件路径。
? Object IDs(对象ID)列表框:以列表形式显示可以产生消息的对象的ID,包括菜单
项、按钮等的ID。该列表框的第一项总是显示所选择的类。
? Messages(消息)列表框:列举在“Object IDs”列表框中选定的对象所能产生的各种
消息。
? Member functions(成员函数)列表框:以列表形式显示选定的类中的成员函数。该列
表框中只列出与消息处理函数以及MFC 中与消息处理有关的虚函数。前者标记为
“W”,并在函数名后显示对应的消息映shè宏;后者标记为“V”。
? Add Class(添加类)按钮:单击该按钮,添加新的类到工程中。该按钮在“MFC
ClassWizard”对话框中的所有选项卡中都存在。
? Add Function(添加函数)按钮:单击该按钮添加选定消息的新的消息处理函数,包
括在消息映shè定义中添加对应的宏指令,在选定的类中添加该消息处理函数的声明和
完成基本功能的代码实现。如果选定的消息已经有消息处理函数,则该按钮无效。对
于对象产生的消息,单击该按钮将弹出如图3-2 所示的对话框,要求用户输入消息处
理函数的名称,在“Member function ncom”(成员函数名称)输入框中输入函数名,
确定后ClassWizard 就会自动添加该消息处理函数。对于由该选定的类产生的消息,
MFC 已经定义好了消息处理函数名称,单击“Add Function”按钮将直接添加该消息
处理函数,不会提示用户输入自定义的函数名。注意,只有单击“MFC ClassWizard”
的“OK”按钮或者“Edit Code”按钮退出“MFC ClassWizard”对话框后,ClassWizard
才会真正修改源文件。
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·46·
图3-2 添加消息处理函数的对话框
? Delete Function(删除函数)按钮:单击该按钮删除选定消息的消息处理函数,包括函
数的声明和定义该消息映shè的宏语句。如果选定的消息没有消息处理函数,则该按钮
无效。注意,当该函数已经存在于源文件中时,单击该按钮会提示如图3-3 所示的对
话框,告诉用户ClassWizard 只能删除该函数的声明和定义该消息映shè的宏语句,而
无法删除该函数的实现代码,用户需要手动删除。
图3-3 提示用户手动删除消息处理函数的对话框
? Edit Code(编辑代码)按钮:单击该按钮退出“MFC ClassWizard”对话框,同时IDE
的代码编辑窗口直接转到选定消息的处理函数进行编辑。该按钮也使得在“MFC
ClassWizard”中所做的更改生效。如果选定的消息没有消息处理函数,则该按钮无效。
? OK(确定)按钮:单击该按钮使得在“MFC ClassWizard”中的编辑生效,并退出“MFC
ClassWizard”对话框。
? Cancel(取消)按钮:单击该按钮取消所有“MFC ClassWizard”对话框中的设置,并
退出“MFC ClassWizard”对话框。
3.1.3 消息处理过程
在Windows 程序设计中,消息的处理是通过消息循环进行接收与分发的。MFC 运用消
息映shè机制封装了这一处理过程。下面介绍MFC 中消息的处理过程。
1.标准Windows 消息和控件通知消息
标准Windows 消息和控件通知消息的处理过程比较简单。这类消息必须由CWnd 或其派
生类的对象处理,消息传递到封装对应的窗口或子窗口的类的对象中。也就是说,当关闭程
序的时候,会产生一个WM_CLOSE 消息并通知主框架类,主框架类调用相应的处理函数进
行程序关闭前的清理cāo作,如保存文件和数据等。
2.命令消息
命令消息与其他两个类型的消息不同,它可以由应用程序其他类的对象(例如应用类对
象、文档对象和文档模板对象)处理。这就涉及一个消息传递路径的问题,具体来说就是允
枫叶文学网www.fywxw.com
第3 章 菜单与加速键
·47·
许一个还是多个对象处理该消息、允许哪些对象处理等。MFC 中采取了简单的方法,只允许
其中一个对象处理该消息,当沿着消息传递路径找到第一个包含该消息的处理函数的对象时,
则处理该消息,然后中断传递过程,不再搜索后续的对象。
由于命令消息可以被多种类的对象处理,因此命令消息的传递路径比较复杂,但总体思
想是由子窗口到父窗口,由窗口到文档。下面举一个简单的例子。
假设是一个多文档MDI 应用程序,用户触发了“编辑”菜单下的“粘贴”菜单项的命令
消息,此时消息依照以下顺序传递。
(1)MDI 主框架窗口首先收到此命令消息。
(2)主框架将处理命令的机会先让给其当前活动的MDI 子框架窗口。
(3)子框架窗口又将处理该命令的机会让给其活动的视图。
(4)视图先搜索自己的消息映shè,寻找对应的处理函数,如果没找到,则将消息传递给
与其关联的文档,找到则调用并中止传递过程。
(5)文档搜索自己的消息映shè,寻找对应的处理函数。一般情况下文档中包含“粘贴”
消息的处理函数,调用该处理函数完成粘贴功能。如果没有找到,文档将消息传递给文档模
板。
(6)文档模板进行相同的过程,如果没找到,将消息传递回子框架窗口。
(7)子框架窗口搜索对应的处理函数,如果没找到,则将消息传回MDI 主框架窗口。
(8)如果主框架窗口中也没找到,则消息最后停留在应用对象,不再进行进一步的传递。
从上面的叙述可以看出,命令消息的传递过程很复杂。然而一般情况下不需要了解这么
详细的消息传递路径,只需根据完成功能的要求,在相应的类中添加实现代码,就不会产生
冲突,比如例子中提到的粘贴功能,一般就是由文档完成的,因此只需在文档类中添加这个
命令消息的处理函数,而不必在别的类中添加与
松语文学免费小说阅读_www.16sy.com