图3-21 位图编辑器属性窗口
? 修改位图的大小:默认位图为48×48(像素宽×像素高),而菜单项的位图要
求是12×11。可以在属性窗口中修改Height和Width属性,也可以直接拖动位图的边框来修改其大小,参见图3-20 b)。
? 绘制图形:可以利用IDE中新增加的“图像”菜单、图像编辑器工具条和颜
色窗口(若看不见颜色窗口,可用“图像\\显示颜色窗口”菜单项来打开它),在中部的图像编辑器窗口中绘制位图(如)。
? 粘贴图像:也可以在其他地方抓取一幅位图,粘贴到现在的空白位图中,然后
再做适当的修改。
2.相关类和成员函数
为了为菜单项添加图标,需要用到CMenu类及其设置菜单项位图的成员函数:
BOOL SetMenuItemBitmaps( // 成功返回TRUE UINT nPosition, // 菜单项的位置(ID或序号)
UINT nFlags, // 标志 = MF_BYCOMMAND(位置为ID,默认值)
// = MF_BYPOSITION(位置为序号)
const CBitmap* pBmpUnchecked, // 未选中的位图 const CBitmap* pBmpChecked // 已选中的位图 );
其中,后两个参数是用于表示菜单项图标两种状态的位图对象的指针,要用到CBitmap类及其缺省构造函数和装入位图成员函数:
CBitmap( );
BOOL LoadBitmap( UINT nIDResource );
3.添加重写型初始化函数
设置菜单项图标的代码,一般放在视图类的初始化函数OnInitialUpdate中。该函数是一个重写型成员函数,并不在MFC向导自动生成的初始视图类代码中,需要另外添加。
下面介绍为视图类添加初始化函数OnInitialUpdate的方法:
? 在项目工作区的“类视图”页中选中应用程序的视图类(如Student项目的
26
CStudentView)。
? 在属性窗口中按其工具条上的重写按钮,切换到重写页。
? 滚动视图父类的“通用”栏的函数列表,寻找到OnInitialUpdate函数项,单击该项
的右栏,在出现的下拉式列表,选其唯一的选项“ OnInitialUpdate”,参见图3-22。
图3-22 添加重写型函数OnInitialUpdate 图3-23 运行结果
下面是在Student例中IDE自动为其视图类添加的初始化函数代码:
void CStudentView::OnInitialUpdate() { }
CView::OnInitialUpdate();
// TODO: 在此添加专用代码和/或调用基类
4.设置菜单项图标
使用位图资源来设置菜单项图标的主要步骤为:
? 创建位图对象(CBitmap):CBitmap *pBmp = new CBitmap(); ? 装入位图资源(LoadBitmap):pBmp->LoadBitmap(IDB_MATH); ? 设置菜单项的标识(SetMenuItemBitmaps):
pSubMenu->SetMenuItemBitmaps(ID_SCORE_MATH,
MF_BYCOMMAND, pBmp, pBmp);
说明:如果需要添加多个菜单项图标,则必须创建多个不同的位图对象,不然会出现系统错误。
为了获取应用程序的主框架窗口和菜单条与下拉菜单,还需要使用3.1.1小节的第4部
27
分中介绍的方法与函数。
例如,为了给Student程序的“成绩\\数学”菜单项添加标识,可以在上面添加视图类重写型初始化函数OnInitialUpdate中的“// TODO??”行后添加如下代码段:
CFrameWnd *pFrmWnd = GetTopLevelFrame(); // 获取住框架窗口对象 CMenu *pMenu = pFrmWnd->GetMenu(); // 获取菜单条
CMenu *pSubMenu = pMenu->GetSubMenu(4); // 获取成绩菜单,菜单序号
// 从0开始,具体数值应与你自己菜单条中的一致
CBitmap *pBmp = new CBitmap(); // 创建空位图对象 pBmp->LoadBitmap(IDB_MATH); // 装入位图资源
// 设置菜单项图标,前一语句使用菜单项ID,后一注释掉的语句使用菜单项序号
pSubMenu->SetMenuItemBitmaps(ID_SCORE_MATH, MF_BYCOMMAND, pBmp, pBmp); //pSubMenu->SetMenuItemBitmaps(0, MF_BYPOSITION, pBmp, pBmp);
运行结果参见图3-23。
3.3 响应菜单项
当用户选中菜单项时,Windows会发送一个消息给对应的事件处理程序。Windows应用程序的主要任务,就是响应用户对各菜单项的选中操作,以完成对应的特定任务。因此,程序员的主要工作,自然就是编写各菜单项对应的事件处理函数。
3.3.1 消息映射
在传统API编程中,Windows的消息处理是由窗口过程函数来完成的,使用的是带有大量case的switch语句,参见2.3.3。由于每个消息的处理都对应于一个case语句块,如果要处理的消息众多,或者某些消息的处理代码很多,则会造成庞大和冗长的窗口过程函数,非常难以编写、修改和维护。
为了解决这一问题,MFC提供了消息映射机制,将原来过程函数中处理每个消息的case块都映射成一个独立的消息响应函数。这样的消息响应函数一般是某个类的成员函数,除了标准成员函数所需的(在类的头文件中定义的)函数原型和(在类的代码文件中编写的)函数体外,MFC中的消息映射函数还需在类的代码文件中定义消息映射宏,一般格式为:
BEGIN_MESSAGE_MAP(类名, 基类名)
28
ON_COMMAND(ID_?, &类名::On?) // 菜单项的命令消息映射宏 ON_UPDATE_COMMAND_UI(ID_?, &类名::On?) // 菜单项的用户界面
// 更新消息映射宏
ON_WM_?() // 窗口消息的消息映射宏
END_MESSAGE_MAP() 例如:
BEGIN_MESSAGE_MAP(CStudentView, CView)
……
ON_COMMAND(ID_NAME_LS, &CStudentView::OnNameLs)
ON_UPDATE_COMMAND_UI(ID_NAME_LS, &CStudentView::OnUpdateNameLs) ON_WM_MOUSEMOVE() ……
END_MESSAGE_MAP()
对应的函数原型如(其中额外的前缀afx_msg表示该函数为MFC的消息映射类型):
afx_msg void OnNameLs();
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
空函数体如:
void CStudentView::OnNameLs() { }
void CStudentView::OnMouseMove(UINT nFlags, CPoint point) { }
// TODO: 在此添加消息处理程序代码和/或调用默认值 CView::OnMouseMove(nFlags, point); // TODO: 在此添加命令处理程序代码
3.3.2 添加响应函数
因为在Windows这样的GUI操作系统中,菜单和鼠标等事件的触发和处理,是通过对
29
应消息的产生、传递和响应来完成的,所以在Windows编程中,常将事件处理程序叫做消息响应函数。
为菜单项添加响应函数的方法有两种:1.利用“事件处理程序向导”对话框添加、2.利用“属性”窗口添加。
1.利用向导对话框
利用事件处理程序向导对话框添加菜单项响应的具体步骤为:
1) 打开菜单编辑器窗口[或快捷键编辑器窗口],选中对应的菜单项[条目]。 2) 按鼠标右键,在弹出的快捷菜单中,选“添加事件处理程序”菜单项;或选“编辑
\\菜单\\添加事件处理程序”菜单项(参见图3-24);会弹出“事件处理程序向导”对话框(参见图3-25)。
图3-24 为菜单项添加事件处理程序 3) 设置对话框的栏目与选项:
? “命令名”栏的值为菜单项的ID,不能修改。 ? “消息类型”栏中有两个选项:
图3-25 事件处理程序向导对话框
? COMMAND(命令)——对应于用户选中菜单项的命令事件/消息,是通常的
(默认)选项。
? UPDATE_COMMAND_UI(更新命令用户接口)——在系统重新绘制菜单前
产生的事件所发出的信息,可用于菜单项的选中和禁用的设置。
? “类列表”栏中罗列了本程序中可以响应该菜单项事件的所有类。一般是在视图类
中处理菜单和鼠标事件(响应菜单和鼠标消息)。
30