菜单的使用

一、Windows菜单的基本知识:

1)顶级菜单:紧贴在标题栏下面的菜单称为顶级菜单,也可以叫做程序的主菜单;

2)弹出式菜单:一般在顶级菜单上都有很多菜单项,单击这些菜单项时会弹出一个下拉式的菜单项,我们点击的这个菜单称为弹出式菜单

3)菜单项:每一个可选菜单项被赋予一个唯一的ID,当用户单击某个菜单项时Windows会将该菜单项的ID发送给父窗口,父窗口通过WM_COMMAND消息处理菜单的单击消息,但是弹出式菜单没有ID,WM_COMMAND消息也不处理弹出式菜单的点击信息

4)菜单加速键:主要是多个键的组合,当同时按下这些键的时候相当于点击了菜单的某个菜单项

5)菜单项一般具有“可用”(Enabled)、“不可用”(disabled)、“变灰”(gray)几种选项,其中变灰选项将菜单项变成不可用的同时也会将菜单项变成灰色,所以当我们需要禁用某个菜单项的时候最好将它变灰,以便提示用户;

6)菜单句柄:每一种菜单都有一个菜单句柄,包括弹出式菜单的菜单项,顶级菜单,弹出式菜单;

二、菜单的创建:

Windows中菜单有两种方式,一种是通过资源的方式通过可视化或者编写rc文件来创建一个菜单资源,并在代码中显示的加载,另一种是通过调用CreateMenu、AppendMenu、InsertMenu等函数创建菜单并插入相应的菜单项,下面对这两种方式一一进行说明:

1)采用rc文件的方式:可以在visual studio中利用可视化的方式编辑菜单,在这里就不在说明,而需要手工编写rc文件请参考我的另外一篇博文http://blog.csdn.net/lanuage/article/details/46897191

当我们编辑好了rc文件之后有三种方法添加菜单:

通过在创建窗口类的时候在lpszMenuName项的后面添加一个用于标示菜单的字符串,若菜单使用的是ID号作为标示那么可以使用宏MAKEINTRESOURCE;

在函数CreateWindow或者CreateWindowEx中的相应参数中填入菜单句柄,为了获取这个句柄需要提前使用LoadMenu函数加载菜单,这个函数的功能是将资源文件中的菜单加载到内存,并返回一个菜单句柄,函数的原型如下:

HMENU LoadMenu(
  HINSTANCE hInstance,  // 当前应用程序的实例句柄
  LPCTSTR lpMenuName    // 菜单唯一标示,可以是字符串或者用MAKEINTRESOURCE转化而来的字符串
);

第三种方式是先通过LoadMenu函数获取菜单句柄后在窗口创建后通过SetMenu函数设置菜单,该函数用于为指定窗口加载一个顶级菜单、该函数原型如下:

BOOL SetMenu(
  HWND hWnd,  // 需加载菜单的窗口句柄
  HMENU hMenu // 菜单句柄
);

各个方式的源代码如下:

WNDCLASS wd = {0};
	wd.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wd.hCursor = LoadCursor(NULL, IDC_ARROW);
	wd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wd.hInstance = hInstance;
	wd.lpfnWndProc = WindowProc;
	wd.lpszClassName = "MenuClass";
	//第一种方式
	//wd.lpszMenuName = MAKEINTRESOURCE(IDM_MENU);
	wd.style = CS_HREDRAW | CS_VREDRAW;
	HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDM_MENU));
	
	//加载加速键
	HACCEL hAccelerator = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDA_MAIN));
	if (!RegisterClass(&wd))
	{
		int nErr = GetLastError();
		return nErr;
	}

	//第二种方式
	//HWND hWnd = CreateWindow("MenuClass", "Menu", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, hMenu, hInstance, NULL);
	
	//第三种方式
	HWND hWnd = CreateWindow("MenuClass", "Menu", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
	SetMenu(hWnd, hMenu);

如果采用函数动态创建的方式,需要如下几个步骤:

1)通过函数CreateMenu()创建一个顶级菜单;

2)通过CreateMenu()创建一个弹出式菜单;

3)利用AppendMenu()或者InsertMenu()向弹出式菜单中插入菜单项;

4)利用AppendMenu()将弹出式菜单插入到顶级菜单中;

5)用SetMenu函数将创建好的菜单加到程序

下面分别说明这些函数的功能和用法:

CreateMenu()用于创建一个菜单(函数会将菜单初始化为空菜单),并返回一个菜单句柄,函数原型如下:

HMENU CreateMenu(VOID)

AppendMenu()用于在顶级菜单、弹出式菜单的最后面的菜单项后查入新菜单项,函数原型如下:

BOOL AppendMenu(
  HMENU hMenu,      // 菜单项的句柄
  UINT uFlags,      // 新菜单项的类型,主要使用的是MF_STRING、MF_POUP(弹出式菜单)
  UINT uIDNewItem,  // 新菜单项的ID,如果是弹出式菜单、则使用菜单的句柄
  LPCTSTR lpNewItem //该值取决于第二个参数,若为MF_STRING则应该是一个以0结尾的字符串
);

InsterMenu()函数作用与AppendMenu相同,函数原型如下:

BOOL InsertMenu(
  HMENU hMenu,      // 菜单项的句柄
  UINT uPosition,   // 新菜单项的识别方式,主要有两种MF_BYCOMMAND和MF_BYPOSITION,在以后我们取菜单项的句柄或者对菜单项做其他操作,需要辨认时会有一定的作用,主要表明是靠ID号辨别还是靠在菜单中的相对位置(以0为第一个菜单项)
  UINT uFlags,     // 新菜单项的类型,主要使用的是MF_STRING、MF_POUP(弹出式菜单)
  UINT uIDNewItem,  // 新菜单项的ID,如果是弹出式菜单、则使用菜单的句柄
  LPCTSTR lpNewItem //该值取决于第三个个参数,若为MF_STRING则应该是一个以0结尾的字符串

);

下面是一个使用这种方式的例子

#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#define IDM_FILE			100
#define IDM_ABOUT			200
#define IDM_CLOSE			300
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	WNDCLASS wd = {0};
	wd.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wd.hCursor = LoadCursor(NULL, IDC_ARROW);
	wd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wd.hInstance = hInstance;
	wd.lpfnWndProc = WindowProc;
	wd.lpszClassName = "MenuClass";
	wd.style = CS_HREDRAW | CS_VREDRAW;
	
	if (!RegisterClass(&wd))
	{
		int nErr = GetLastError();
		return nErr;
	}

	HWND hWnd = CreateWindow("MenuClass", "Menu", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	//创建主菜单
	HMENU hMenu = CreateMenu();
	
	//创建弹出式菜单
	HMENU hPopup = CreateMenu();
	
	//向弹出式菜单中插入菜单项
	AppendMenu(hPopup, MF_STRING, IDM_FILE, TEXT("文件"));
	AppendMenu(hPopup, MF_STRING, IDM_ABOUT, TEXT("关于"));
	InsertMenu(hPopup, MF_BYCOMMAND, MF_STRING, IDM_CLOSE, TEXT("关闭"));

	//将弹出式菜单插入到主菜单中
	AppendMenu(hMenu, MF_POPUP,(UINT_PTR)hPopup, TEXT("系统"));

	SetMenu(hWnd,hMenu);

	if (NULL == hWnd)
	{
		int nErr = GetLastError();
		return nErr;
	}

	ShowWindow(hWnd, nShowCmd);

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_COMMAND:
		{
			if (IDM_ABOUT == LOWORD(wParam))
			{
				MessageBox(hWnd, TEXT("About"), TEXT("TEST"), MB_OK);
			}
		}
		break;
	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	return 0;
}

三、键菜单的使用:

创建一个右键菜单有如下步骤(在WM_RBUTTONDOWN消息下处理):

1)创建一个可用的菜单(一般是主菜单);

2)根据主菜单获取弹出式菜单的句柄,使用函数GetSubMenu()

2)加载菜单项

3)获取鼠标点击的位置

4)将客户区坐标转化为屏幕坐标(这一步千万别忘了)

5)调用TrackPopupMenu函数,该函数用来显示一个快捷菜单,这个函数中需要填入菜单显示的位置,这个位置值为屏幕坐标,这也就是我们为什么需要转化坐标的原因;该函数的原型为:

BOOL TrackPopupMenu(
  HMENU hMenu,         // 快捷菜单的句柄
  UINT uFlags,         // 快捷菜单显示的类型
  int x,               // 
  int y,               //菜单显示点的坐标,根据第二个参数确定如何显示,一般有左对齐(最左边顶点为该坐标)、右对齐(右上角坐标为该坐标)、中间对齐(上边线的中点坐标为该坐标);
  int nReserved,       // 该参数必须给0
  HWND hWnd,           // 显示快捷菜单的窗口句柄
  CONST RECT *prcRect  // 该参数被忽略,一般给NNULL
);

下面是一段例子代码:

HMENU hMenu = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDM_MENU));
			hMenu = GetSubMenu(hMenu, 0);
			POINT ptChick = {LOWORD(lParam), HIWORD(lParam)};
			ClientToScreen(hWnd, &ptChick);
			TrackPopupMenu(hMenu, TPM_LEFTALIGN, ptChick.x, ptChick.y, 0, hWnd, NULL);

其他菜单操作的函数主要有:

GetSystemMenu()获取系统菜单句柄;

Deletemenu()从菜单中删除某一菜单项并销毁它

RemoveMenu()从菜单中移出某一菜单项但不销毁它

InsertMenu()在菜单中插入一个菜单项

NodifyMenu()修改一个已存在的菜单项

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IT派

Javascript 将 HTML 页面生成 PDF 并下载

最近碰到个需求,需要把当前页面生成 pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :)

3141
来自专栏Albert陈凯

Markdown 语法和 MWeb 写作使用说明既然都整理了,捎带手把这个也整理了吧第一级标题<h1>

既然都整理了,捎带手把这个也整理了吧 快捷键汇总 预览 CMD + R 在 MWeb 中的快捷键为: 标题快捷键:Control + 1 -- Control ...

3727
来自专栏龙首琴剑庐

IntelliJ IDEA 快捷键简摘

Ctrl+Shift+A (查找所有Intellij的命令) 智能提示 Ctrl+空格 (代码提示) Ctrl+Shift+空格(智能提...

32611
来自专栏非著名程序员

基础篇章:关于 React Native 之 Picker 组件的讲解

(友情提示:RN学习,从最基础的开始,大家不要嫌弃太基础,会的同学请自行略过,希望不要耽误已经会的同学的宝贵时间) 今天我们就讲Picker ,顾名思义就是选择...

1918
来自专栏Petrichor的专栏

Markdown 操作

  最开始是因为看到网上的教程写得很简洁漂亮,所以学的Markdown。个人感觉Markdown是一种学会了就回不去的语法,特别方便简洁。

2822
来自专栏Golang语言社区

Go语言操作mysql数据库简单例子

Go语言操作数据库非常的简单, 他也有一个类似JDBC的东西"database/sql" 实现类是"github.com/go-sql-driver/mysql...

3684
来自专栏xingoo, 一个梦想做发明家的程序员

快捷键整理

Eclipse 跳转到指定行:ctrl+l 1几个最重要的快捷键 代码助手:Ctrl+Space(简体中文操作系统是Alt+/) 快速修正:Ctrl+1 单词补...

1919
来自专栏我有一个梦想

ClistCtrl用法及总结(由怎样隐藏ListCtrl列表头的排序小三角形这个bug学习到的知识)

1 怎样隐藏ListCtrl列表头的排序小三角形 在创建控件是加入|LVS_NOSORTHEADER风格即可。 一下是用法总结: 本文根据本人在项目中的应用,来...

3335
来自专栏Windows Community

New UWP Community Toolkit - ImageEx

概述 UWP Community Toolkit  中有一个图片的扩展控件 - ImageEx,本篇我们结合代码详细讲解  ImageEx 的实现。 Image...

3237
来自专栏Bingo的深度学习杂货店

HTML5新特性

本章的主要内容有: ---- [1] 用于媒体回放的 video 和audio 元素 [2] HTML5拖放 [3] canvas简单应用 [4] Web存储:...

5135

扫码关注云+社区

领取腾讯云代金券