MFC界面编程基础(09):菜单(二)

上一篇:MFC界面编程基础(08):菜单(一)下一篇:MFC界面编程基础(10):基于对话框的MFC应用程序

快捷菜单

我们平时在使用程序时,经常会用到单击鼠标右键显示快捷菜单(也称上下文菜单,右键菜单)这一功能。如果想要自己实现这个功能,需要通过以下步骤来完成:

1.为程序添加一个新的菜单资源。

可在【资源视图】选项卡上的Menu分支上单击鼠标右键,从弹出的菜单中选择【添加资源】菜单命令
在这里插入图片描述
在这里插入图片描述
这时,在Menu分支下就多了一个名为IDR_MENU1的菜单资源,并同时在VS开发界面窗口的右边窗口中打开了这个菜单资源。接着就要为这个菜单资源添加菜单项了。因为在显示快捷菜单时顶级菜单是不出现的,所以可以给它设置任意的文本。
2.给视图类添加WM_RBUTTONDOWN消息响应函数。
我们可以使用TrackPopupMenu函数来显示一个快捷菜单。该函数声明形式如下:
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd,
LPCRECT lpRect = NULL );

参数:

  • nFlags 指定屏幕位置标志或鼠标键标志。

屏幕位置标志可以为下列值之一:

  • TPM_CENTERALIGN 使弹出菜单在水平方向相对于x指定的坐标居中。
  • TPM_LEFTALIGN 放置弹出菜单,以便弹出菜单在由坐标值x指定的位置左对齐。
  • TPM_RIGHTALIGN 放置弹出菜单,以便弹出菜单在由坐标值x指定的位置右对齐。

鼠标键标志可以为下列值之一:

  • TPM_LEFTBUTTON 导致弹出菜单追踪鼠标左键。
  • TPM_RIGHTBUTTON 导致弹出菜单追踪鼠标右键。
  • x 指定弹出菜单屏幕坐标的水平位置。根据参数nFlags的值,该菜单可以左对齐、右对齐或在该位置上居中。
  • y 指定弹出菜单屏幕坐标的垂直位置
  • pWnd 标识拥有弹出菜单的窗口。该窗口接收来自菜单的所有WM_COMMAND消息。在Windows 3.1或以后版本中,窗口直到TrackPopupMenu返回才接收WM_COMMAND消息。在Windows 3.0中,窗口在TrackPopupMenu返回之前接收WM_COMMAND消息。
  • lpRect 指向RECT结构或包含矩形的屏幕坐标CRect对象,用户在该矩形内部单击鼠标也不会消除弹出菜单。若该参数为NULL ,那么用户在该矩形外部单击鼠标,弹出菜单将消失。对于Windows 3.0,该值必须为NULL。

在这里插入图片描述

问题:
运行后发现这个快捷菜单显示的位置好像不太对,并不是在鼠标右键单击点处显示的。这是因为TrackPopupMenu函数中的x和y参数都是屏幕坐标,而鼠标单击点处的坐标是窗口客户区坐标,即以程序窗口左上角为坐标原点。

解决:
需要把客户区坐标转换为屏幕坐标。我们须在调用TrackPopupMenu函数之前调用ClientToScreen函数。函数原型如下:
void ClientToScreen( LPPOINT lpPoint ) const;
void ClientToScreen( LPRECT lpRect ) const;
参数:
lpPoint 指向一个POINT结构或CPoint对象,其中包含了要转换的客户区坐标。
lpRect 指向一个RECT结构或CRect对象,其中包含了要转换的客户区坐标。

在这里插入图片描述

3.为程序添加快捷菜单上各菜单项命令的响应函数。
在菜单资源编辑器中,分别为CMainFrame类和CXXView类添加一个响应快捷菜单项的函数,运行后你会发现是CXXView类中添加的响应在处理这个消息。我们将CXXView类对快捷菜单项命令消息的响应函数删除,再次运行,可是发现CMainFrame类对快捷菜单命令消息的响应函数还是没有反应。这主要是因为在创建快捷菜单时,即调用TrackPopupMenu函数时,对这个快捷菜单的拥有者参数传递的是this值,也就是视类窗口拥有这个快捷菜单。因此,只有视类才能对快捷菜单项命令做出响应。如果想让CMainFrame类能对这个快捷菜单项进行响应的话,就应该在调用TrackPopupMenu函数时把快捷菜单的拥有者指定为CMainFrame类窗口,为此我们可以修改CXXView类OnRButtonDown函数中队TrackPopupMenu函数的调用,如下:
在这里插入图片描述
注意:
即便如此,框架类窗口能有机会获得对该快捷菜单中的菜单项的命令响应,但最先响应还应是CXXView类窗口,除非CXXView类窗口没有设置快捷菜单的函数,才能由框架类窗口做出响应。这可由菜单命令消息路由的过程来解释。

菜单项动态操作

添加菜单项

通过代码来动态地在已有菜单项目后面添加新的菜单项。
我们可以利用CMenu类提供的一个成员函数:AppendMenu来完成。该函数的作用是把一个新菜单项目添加到一个指定菜单项目的末尾。该函数具体声明形式如下:
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0,
LPCTSTR lpszNewItem = NULL );
参数:

  • nFlags
    指定了增加到菜单中的新菜单项状态的有关信息。它包括说明中列出的一个或多个值。下面列出的是nFlags可以设置的值:
    MF_CHECKED 该值的行为如同使用MF_UNCHECKED来作为一个标记,用于替换项前的检测标记。若应用支持检测标记位图(请参阅SetMenuItemBitmaps成员函数),那么将显示“检测标记打开”位图。

  • MF_UNCHECKED 该值的行为如同使用MF_CHECKED来作为一个标记,用于删除项前的检测标记。若应用支持检测标记位图(请参阅SetMenuItemBitmaps成员函数),那么将显示“检测标记关闭”位图。

  • MF_DISABLED 使菜单项无效以便它不能被选择,但菜单项不变灰。

  • MF_ENABLED 使菜单项有效以便它能够被选择,并从灰色状态中恢复原样。

  • MF_GRAYED 使菜单项无效以便它不能被选择,同时使菜单项变灰。

  • MF_MENUBARBREAK 在静态菜单里的新行中或弹出菜单的新列中放置菜单项。新的弹出菜单列与老的菜单列将由垂直分割线分开。

  • MF_MENUBREAK 在静态菜单里的新行中或弹出菜单的新列中放置菜单项。列与列之间没有分割线。

  • MF_OWNERDRAW 指定菜单项为一个拥有者描绘的项。当菜单首次显示时,拥有该菜单的窗口将接收WM_MEASUREITEM消息,以获取菜单项的高度与宽度。WM_DRAWITEM消息将使属主窗口必须更新菜单项的可视界面。该选择项对于顶层菜单项无效。

  • MF_POPUP 指定菜单项有与之相关联的弹出菜单。参数ID指定了与项相关联的弹出菜单的句柄。它用于增加顶层弹出菜单项或用于增加弹出菜单项的下一级弹出菜单。

  • MF_SEPARATOR 绘制一条水平的分割线。它仅仅能用于弹出菜单项。该线不能变灰、无效或高亮度显示。其它的参数将被忽略。

  • MF_STRING 指定菜单项为一个字符串。
    下面列出的各组标志互相排斥,不能一起使用:

  • MF_DISABLED, MF_ENABLED,和 MF_GRAYED

  • MF_STRING, MF_OWNERDRAW, MF_SEPARATOR和位图版本。

  • MF_MENUBARBREAK和MF_MENUBREAK

  • MF_CHECKED 和MF_UNCHECKED

  • nIDNewItem
    指定了新菜单项的命令ID号,或如果nFlags被设置为MF_POPUP,该参数指定弹出菜单的菜单句柄(HMENU),否则就是添加新菜单项的命令ID。如果nFlags被设置为MF_SEPARATOR,那么参数NewItem将被忽略。

  • lpszNewItem
    lpszNewItem指定了新菜单项的内容。参数nFlags以下列方式解释lpszNewItem:
    在这里插入图片描述

插入菜单项

通过代码来动态地在已有菜单项目之间添加新的菜单项。
我们可以利用CMenu类的InsertMenu成员函数来实现,该函数的声明形式如下:
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0,
LPCTSTR lpszNewItem = NULL );
参数:

  • nPosition
    指定将要插入的新菜单项前的菜单项。参数nFlags被用于以下列方式解释:
    在这里插入图片描述
  • nFlags
    指定nPosition值如何被解释,并指定要增加到菜单中新菜单项的状态。对于能够被设置的标志,请参阅成员函数AppendMenu。如果需要指定多个值,需使用位与操作来组合MF_BYCOMMAND或MF_BYPOSITION标志。
  • nIDNewItem
    指定新菜单项的命令ID号,或者,若nFlags被设置为MF_POPUP,则指定为弹出菜单的菜单句柄(HMENU)。若nFlags被设置为MF_SEPARATOR,那么参数nIDNewItem将被忽略。
  • lpszNewItem
    指定新菜单项的文本。nFlags被用于以下列方式解释lpszNewItem:

在这里插入图片描述

删除菜单项

CMenu提供一个DeleteMenu成员函数,函数声明如下:
BOOL DeleteMenu( UINT nPosition, UINT nFlags );
参数:

  • nPosition
    指定由nFlags决定的将要删除的菜单项。
  • nFlags
    以下列方式解释nPosition:

在这里插入图片描述

利用这个函数可以删除一个菜单项目,包括子菜单,以及子菜单下的菜单项,主要取决于调用这个函数的对象,如果该对象是程序的菜单栏对象,那么删除的就是指定的子菜单;如果该对象是一个子菜单对象,那么删除的就是该子菜单的下一个菜单项。

知识拓展:模拟动画图标

当我们的程序启动之后,让程序的图标不断的变化,给人一种动画效果。

1.加载图标资源

  • 在程序的【资源视图】中导入图标资源:
    在这里插入图片描述
  • 在程序的CMainFrame类中,定义一个图标句柄数组成员变量,用来存放在资源视图中添加的图标的句柄:

private:
HICON m_hIcons[4];

  • 在CMainFrame类的OnCreate函数中利用LoadIcon加载添加的图标资源:
    使用LoadIcon函数如果加载的是系统图标第一个参数应该设置为NULL,如果需要使用自定义图标,该函数的第一个参数应该设置为改程序当前的实例句柄。
    HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);

获取当前程序实例句柄的方式:

①使用AfxGetInstanceHandle()函数
②使用全局实例对象theApp访问其数据成员m_hInstance
注意:在使用之前必须声明这个变量是在外部定义的
extern CMyApp theApp
③使用全局函数AfxGetApp()获取当前应用程序指针,通过该指针来访问应用程序的成员变量m_hInstance

	m_hIcons[0] = LoadIcon(AfxGetInstanceHandle(),
 MAKEINTRESOURCE(IDI_ICON1));
	m_hIcons[1] = LoadIcon(AfxGetInstanceHandle(),
 MAKEINTRESOURCE(IDI_ICON2));
	m_hIcons[2] = LoadIcon(AfxGetInstanceHandle(), 
MAKEINTRESOURCE(IDI_ICON3));
	    m_hIcons[3] = LoadIcon(AfxGetInstanceHandle(),
 MAKEINTRESOURCE(IDI_ICON4));

MAKEINTRESOURCE宏:
将资源ID转换为相应的资源标识符字符串

2.定时器处理

  • 在CMainFrame类的OnCreate函数中设置定时器:
    SetTimer(1, 1000, NULL);
  • 为CMainFrame类添加WM_TIMER消息相应函数OnTimer
  • 在OnTimer函数中使用SetClassLong函数为应用程序设置自定义图标
    函数原型如下:
DWORD SetClassLong(HWND hWnd,int nlndex,LONG dwNewLong)

参数:
hWnd:窗口句柄及间接给出的窗口所属的类。
nIndex: 指定要设置的索引
GCL_HBRBACKGROUND 设置新的背景画刷
GCL_HCURSOR 设置新的光标
GCL_HICON 设置新的图标
GCL_STYLE 设置新的窗口样式

void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
		// TODO:  在此添加消息处理程序代码和/或调用默认值
		static int nIndex = 0;
		SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[nIndex]);
		nIndex = ++nIndex % 4;
		CFrameWnd::OnTimer(nIDEvent);
}
上一篇:MFC界面编程基础(08):菜单(一)下一篇:MFC界面编程基础(10):基于对话框的MFC应用程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值