首页 > 代码库 > 菜单编写(VC_Win32)
菜单编写(VC_Win32)
http://blog.csdn.net/tcjiaan/article/details/8497535
http://blog.csdn.net/kz_ang/article/details/8130955
菜单在 .rc 文件中的格式
.rc 中的菜单格式
虽然现在微软的编译器中都会自动生成好用的 rc 资源但是还是可以了解下它内部代码的意义.
这里是不太建议直接在 .rc 文件中修改菜单因为修改了.rc 文件后还需在其他文件中修改对应地方,否则在编译中会报错.所以还是建议在编译器的资源管理器中修改对话框.
格式:
menuID MENU [,载入特性选项]
{
菜单项列表
}说明:
- menuID: 菜单资源标识
- MEMU: 关键字
- 载入特性:
- DISCARDABLE 当不再需要菜单时候菜单可丢弃
- FIXED 将菜单保存在内存中固定位置
- LOADONCALL 需要时加载菜单
- MOVEABLE 菜单在内存中可移动
- PRELOAD 立即加载菜单
- 菜单项列表:
- 弹出菜单/子菜单(POPUP)
- 格式:
- POPUP"子菜单名"[,选项]
BEGIN
…(菜单项成员)
END- 说明:
- POPUP: 关键字
- 子菜单名: "子菜单的名字&热键"
- BEGIN: 子菜单中菜单项开始的标识
- 选项:
- MENUBARBREAK 菜单项纵向分隔标识
- CHECKED 显示选中标识
- INACTIVE 禁止一个菜单项
- GRAYED 禁止一个菜单项并使其显示灰色
- 菜单项成员: 子菜单或菜单项(定义如下所示)
- END: 子菜单中菜单项结束的标识
- 菜单项(MENUITEM)
- 格式: MENUITEM "菜单项名",菜单项标识符(ID)[,选项]
- 说明:
- MENUITEM: 关键字
- 菜单项名: "菜单项名字&热键"
- 选项:
- MENUBARBREAK 菜单项纵向分隔标识
- CHECKED 显示选中标识
- INACTIVE 禁止一个菜单项
- GRAYED 禁止一个菜单项并使其显示灰色
菜单组成部分
- 主菜单栏
- 下拉式菜单框
- 菜单项热键标识
- 菜单项加速键标识
- 菜单项分割线(占据菜单索引)
加载/卸载菜单
加载菜单
在 win32 界面程序中加载菜单有以下几种方式:
- 在窗口类设计时候进行加载
在定义 WNDCLASS 时对成员 lpszMenuName 赋予相对应的值- 在创建窗口时候进行加载
- 动态加载菜单
代码示例:
.rc 资源内容
- IDR_MENU1 MENU
- BEGIN
- POPUP "菜单1"
- BEGIN
- POPUP "子菜单1.1"
- BEGIN
- MENUITEM "菜单项1.1.1", ID_40001
- MENUITEM "菜单项1.2.1", ID_40002
- END
- MENUITEM "菜单项1.2", ID_40003
- MENUITEM SEPARATOR
- MENUITEM "菜单项1.3", ID_40004
- MENUITEM "菜单项1.4", ID_40005
- END
- POPUP "菜单2"
- BEGIN
- MENUITEM "菜单项2.1", ID_40006
- MENUITEM "菜单项2.2", ID_40007
- END
- END
加载菜单:
- 第一种加载方式(类设计时):
- WNDCLASS wndclass;
- ....
- wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);
- //这里省略了窗体类创建时需要填写的其他信息.
- 第二种加载方式(窗体创建时):
- HMENU hmenu;
- WNDCLASS wndclass;
- ....
- wndclass.lpszMenuName=NULL;
- //这里省略了一些窗体类的必要信息填写,和注册窗口类等操作
- //加载菜单到菜单句柄中
- hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
- //在创建窗体时候载入菜单
- hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
- CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);
- 第三种加载方式(窗体创建后):
- HMENU hmenu;
- WNDCLASS wndclass;
- ....
- wndclass.lpszMenuName=NULL;
- //这里省略了一些窗体类的必要信息填写,和注册窗口类等操作
- //创建窗体
- hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
- CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
- //加载菜单到菜单句柄中
- hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
- //动态的加载菜单到窗体中去
- SetMenu(hwnd,hmenu);
程序源码:
- #include<windows.h>
- #include"resource.h"
- LRESULT CALLBACK textprom(
- HWND hwnd, // handle to window
- UINT uMsg, // message identifier
- WPARAM wParam, // first message parameter
- LPARAM lParam // second message parameter
- );
- int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance
- HINSTANCE hPrevInstance, // handle to previous instance
- LPSTR lpCmdLine, // pointer to command line
- int nCmdShow // show state of window
- )
- {
- WNDCLASS wndclass;
- HWND hwnd;
- HMENU hmenu;
- MSG msg;
- //设计窗口类
- wndclass.cbClsExtra=0;
- wndclass.cbWndExtra=0;
- wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
- wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
- wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);
- wndclass.hInstance=hInstance;
- wndclass.lpfnWndProc=textprom;
- wndclass.lpszClassName="text";
- wndclass.lpszMenuName=NULL;
- //wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);
- wndclass.style=CS_HREDRAW | CS_VREDRAW;
- //注册窗口类
- if(!RegisterClass(&wndclass))
- {
- MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);
- }
- //创建无菜单资源的窗口窗口
- hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,0,0,
- 500,300,NULL,NULL,hInstance,NULL);
- /*
- //载入菜单资源
- hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
- //创建有菜单资源的窗口
- hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
- CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);*/
- //载入菜单资源,并在窗口加载菜单资源
- hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
- SetMenu(hwnd,hmenu);
- //显示更新窗口
- ShowWindow(hwnd,nCmdShow);
- UpdateWindow(hwnd);
- //消息循环
- while(GetMessage(&msg,NULL,0,0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- return msg.wParam;
- }
- LRESULT CALLBACK textprom(
- HWND hwnd, // handle to window
- UINT uMsg, // message identifier
- WPARAM wParam, // first message parameter
- LPARAM lParam // second message parameter
- )
- {
- switch(uMsg)
- {
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
- }
- return DefWindowProc(hwnd,uMsg,wParam,lParam);
- }
运行结果:
卸载菜单
步骤:
代码示例:
- HMENU hmenu;
- switch(uMsg)
- {
- //添加鼠标右键单击事件响应处理,即卸载菜单
- case WM_LBUTTONDOWN:
- hmenu = GetMenu(hwnd);
- if(hmenu != NULL)
- {
- SetMenu(hwnd,NULL);
- DestroyMenu(hmenu);
- }
- break;
- .....
运行结果:
单击鼠标右键后
菜单常用的操作
菜单项常用操作
- 设置默认菜单项 SetMenuDefaultItem
- 禁止或激活菜单项 EnableMenuItem
- 设置默认菜单项 SetMenuDefaultItem
- 修改菜单项 ModifyMenu
- 设置或或取消菜单项选择标志 CheckMenuItem
菜单常用操作
- 获取子菜单句柄 GetSubMenu
- 获得窗口主菜单句柄 GetMenu
- 动态创建空的弹出菜单 CreateMenu
- 删除菜单项 DeleteMenu
- 在菜单中插入菜单项 InsertMenu
- 在菜单尾部增加菜单项 AppendMenu
菜单索引
在编写菜单操作前还需了解个很重要的概念,即了解菜单的结构,只有了解了这个结构,才能找到对应的菜单,子菜单或菜单项进行对应的操作.因为在操作菜单时需要获得对应的菜单句柄(如在上图子菜单1.1进行插入菜单项1.3.1操作时要获得子菜单1.1句柄),或操作菜单项时要获得对应的子菜单项的菜单句柄(如让上图菜单项1.1.1禁用时候需要获得子菜单1.1的句柄).然而在获取子菜单句柄时需要子菜单索引,所以对索引号的一些规则必须要有一定的了解.
简单规则如下:
- 菜单索引基于0开始;
- 分隔符也算一个菜单项,所以他也占据一个索引号
即如下图所示:
两个简单样例:
- 在上图子菜单1.1进行插入菜单项1.3.1操作
程序运行结果:
- //在程序源码的显示更新窗口
- ShowWindow(hwnd,nCmdShow);
- UpdateWindow(hwnd);
- //在程序源码的显示更新窗口后插入
- //插入菜单项
- AppendMenu(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),MF_ENABLED,40012,"菜单项1.1.3");
- 让上图菜单项1.1.1禁用:
运行结果:
- //在程序源码的显示更新窗口
- ShowWindow(hwnd,nCmdShow);
- UpdateWindow(hwnd);
- //在程序源码的显示更新窗口后插入
- //插入菜单项
- AppendMenu(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),MF_ENABLED,40012,"菜单项1.1.3");
- //禁用菜单项
- EnableMenuItem(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),0,MF_BYPOSITION | MF_GRAYED);
创建弹出菜单
步骤:
- 载入菜单资源
(用 GetSubMenu,非 LoadMenu 或 getMenu,因为后两种获得的菜单句柄都是主菜单的句柄,而主菜单句柄不适合用 TrackPopupMenu 显示弹出菜单,若用的是主菜单句柄作为弹出菜单句柄时候效果如下图所示)调用 TrackPopupMenu 显示弹出菜单
流程图如下:
代码示例:
.rc 资源内容
- /***********************************************/
- //主菜单
- IDR_MENU1 MENU
- BEGIN
- POPUP "菜单1"
- BEGIN
- POPUP "子菜单1.1"
- BEGIN
- MENUITEM "菜单项1.1.1", ID_40001
- MENUITEM "菜单项1.2.1", ID_40002
- END
- MENUITEM "菜单项1.2", ID_40003
- MENUITEM SEPARATOR
- MENUITEM "菜单项1.3", ID_40004
- MENUITEM "菜单项1.4", ID_40005
- END
- POPUP "菜单2"
- BEGIN
- MENUITEM "菜单项2.1", ID_40006
- MENUITEM "菜单项2.2", ID_40007
- END
- END
- /***********************************************/
- //弹出菜单
- IDR_MENU2 MENU
- BEGIN
- POPUP "弹出菜单"
- BEGIN
- MENUITEM "弹出菜单项1.1", ID_A_40012
- MENUITEM "弹出菜单项1.2", ID_A_40013
- END
- END
加载弹出菜单
- hmenuPop=GetSubMenu(LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2)),0);
显示弹出菜单
- POINT p;
- switch(uMsg)
- {
- //添加鼠标左键单击事件响应处理,即显示弹出菜单
- case WM_RBUTTONDOWN:
- p.x=LOWORD(lParam);
- p.y=HIWORD(lParam);
- //将窗口坐标转换成屏幕坐标
- ClientToScreen(hwnd,&p);
- TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);
- break;
- .....
程序运行结果(在鼠标右键单击后):
菜单加速键
.rc 中的菜单格式
格式:
- 加速键ID ACCELERATORS
BEGIN
键名,命令ID [,类型] [,选项]
…
END说明:
- 加速键ID: 一个字符串或者是1~65535之间的数字
- ACCELERATORS: 关键字
- BEGIN: 关键字,表示加速键列表的开始
- 键名: 表示加速键对应的按钮,可以有3种方式定义
- “^字母”:表示Ctrl键加上字母键.
- “字母”:表示字母,这时类型必须指明是VIRTKEY
- 数值:表示ASCII码为该数值的字母,这时类型必须指明为ASCII
- 命令ID: 按下加速键后,Windows向程序发送的命令ID.如果想把加速键和菜单项关联起来,这里就是要关联期间项的命令ID
- 类型: 用来指定键的定义方式,可以是 VIRTKEY 和 ASCII,分别用来表示“键名”字段定义的是虚拟键还是ASCII码
- 选项: 可以是 Alt, Control 或 Shift 中的单个或多个,如果指定多个,则中间用逗号隔开,表示加速键是按键加上这些控制键的组合键.这些选项只能在类型是VIRTKEY的情况下才能使用
- END 关键字,表示加速键列表的结束
编写菜单资源加速键
编写步骤:
- 加载菜单加速键资源: LoadAccelerators
- 修改消息循环: (即在消息循环中先把消息派送给转换菜单加速键,然后在派送给转换消息最后分配消息,如下图所示)
代码样例(为菜单项1.4增加快捷键 Crt+Alt+K):
.rc资源:
- IDR_ACCELERATOR1 ACCELERATORS
- BEGIN
- "K", ID_40009, VIRTKEY, CONTROL, ALT, NOINVERT
- END
加载菜单加速资源:
- HACCEL haccel;
- haccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));
更改消息循环:
- while(GetMessage(&msg,NULL,0,0))
- {
- if(!TranslateAccelerator(hwnd,haccel,&msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
添加菜单项1.4 事件响应:
- switch(uMsg)
- {
- //添加鼠标左键单击事件响应处理,即卸载对话框
- case WM_RBUTTONDOWN:
- p.x=LOWORD(lParam);
- p.y=HIWORD(lParam);
- //将窗口坐标转换成屏幕坐标
- ClientToScreen(hwnd,&p);
- TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);
- break;
- //添加菜单响应事件
- case WM_COMMAND:
- switch(LOWORD(wParam))
- {
- //添加菜单项1.4响应事件
- case ID_40009:
- MessageBox(hwnd,"success!","test",MB_OK);
- break;
- }
- break;
- .....
程序源码:
- #include<windows.h>
- #include"resource.h"
- HMENU hmenuPop;//弹出菜单句柄
- LRESULT CALLBACK textprom(
- HWND hwnd, // handle to window
- UINT uMsg, // message identifier
- WPARAM wParam, // first message parameter
- LPARAM lParam // second message parameter
- );
- int WINAPI WinMain(
- HINSTANCE hInstance, // handle to current instance
- HINSTANCE hPrevInstance, // handle to previous instance
- LPSTR lpCmdLine, // pointer to command line
- int nCmdShow // show state of window
- )
- {
- WNDCLASS wndclass;
- HWND hwnd;
- HMENU hmenu;
- MSG msg;
- HACCEL haccel;
- //设计窗口类
- wndclass.cbClsExtra=0;
- wndclass.cbWndExtra=0;
- wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
- wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
- wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);
- wndclass.hInstance=hInstance;
- wndclass.lpfnWndProc=textprom;
- wndclass.lpszClassName="text";
- wndclass.lpszMenuName=NULL;
- //wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);
- wndclass.style=CS_HREDRAW | CS_VREDRAW;
- //注册窗口类
- if(!RegisterClass(&wndclass))
- MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);
- //创建无菜单资源的窗口窗口
- hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,0,0,500,300,NULL,NULL,hInstance,NULL);
- /*
- //载入菜单资源
- hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
- //创建有菜单资源的窗口
- hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
- CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);*/
- //载入菜单资源,并在窗口加载菜单资源
- hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));
- SetMenu(hwnd,hmenu);
- //载入弹出菜单资源
- hmenuPop=GetSubMenu(LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2)),0);
- //hmenuPop=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2));
- //显示更新窗口
- ShowWindow(hwnd,nCmdShow);
- UpdateWindow(hwnd);
- //加载菜单加速资源
- haccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));
- //消息循环
- while(GetMessage(&msg,NULL,0,0))
- {
- if(!TranslateAccelerator(hwnd,haccel,&msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- return msg.wParam;
- }
- LRESULT CALLBACK textprom(
- HWND hwnd, // handle to window
- UINT uMsg, // message identifier
- WPARAM wParam, // first message parameter
- LPARAM lParam // second message parameter
- )
- {
- POINT p;
- switch(uMsg)
- {
- //添加鼠标左键单击事件响应处理,即卸载对话框
- case WM_RBUTTONDOWN:
- p.x=LOWORD(lParam);
- p.y=HIWORD(lParam);
- //将窗口坐标转换成屏幕坐标
- ClientToScreen(hwnd,&p);
- TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);
- break;
- //添加菜响应事件
- case WM_COMMAND:
- switch(LOWORD(wParam))
- {
- //添加菜单项1.4响应事件
- case ID_40009:
- MessageBox(hwnd,"success!","test",MB_OK);
- break;
- }
- break;
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
- }
- return DefWindowProc(hwnd,uMsg,wParam,lParam);
- }
运行结果(在单击菜单项1.4或者按下Ctrl+Alt+K的组合键时):
菜单编写(VC_Win32)