首页 > 代码库 > 菜单编写(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 资源内容

  1. IDR_MENU1 MENU   
  2. BEGIN  
  3.     POPUP "菜单1"  
  4.     BEGIN  
  5.         POPUP "子菜单1.1"  
  6.         BEGIN  
  7.             MENUITEM "菜单项1.1.1",                    ID_40001  
  8.             MENUITEM "菜单项1.2.1",                    ID_40002  
  9.         END  
  10.         MENUITEM "菜单项1.2",                      ID_40003  
  11.         MENUITEM SEPARATOR  
  12.         MENUITEM "菜单项1.3",                      ID_40004  
  13.         MENUITEM "菜单项1.4",                      ID_40005  
  14.     END  
  15.     POPUP "菜单2"  
  16.     BEGIN  
  17.         MENUITEM "菜单项2.1",                      ID_40006  
  18.         MENUITEM "菜单项2.2",                      ID_40007  
  19.     END  
  20. END  

加载菜单:

  • 第一种加载方式(类设计时):
    1. WNDCLASS wndclass;  
    2.   
    3. ....  
    4. wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);  
    5. //这里省略了窗体类创建时需要填写的其他信息.  
  • 第二种加载方式(窗体创建时):
    1. HMENU hmenu;  
    2. WNDCLASS wndclass;  
    3.   
    4. ....  
    5. wndclass.lpszMenuName=NULL;  
    6. //这里省略了一些窗体类的必要信息填写,和注册窗口类等操作  
    7.   
    8. //加载菜单到菜单句柄中  
    9. hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));  
    10. //在创建窗体时候载入菜单  
    11. hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,  
    12.     CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);  
  • 第三种加载方式(窗体创建后):
    1. HMENU hmenu;  
    2. WNDCLASS wndclass;  
    3.   
    4. ....  
    5. wndclass.lpszMenuName=NULL;  
    6. //这里省略了一些窗体类的必要信息填写,和注册窗口类等操作  
    7.   
    8. //创建窗体  
    9. hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,  
    10.     CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);  
    11.   
    12. //加载菜单到菜单句柄中  
    13. hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));  
    14. //动态的加载菜单到窗体中去  
    15. SetMenu(hwnd,hmenu);  

程序源码:

  1. #include<windows.h>  
  2. #include"resource.h"  
  3.   
  4. LRESULT CALLBACK textprom(  
  5.   HWND hwnd,      // handle to window  
  6.   UINT uMsg,      // message identifier  
  7.   WPARAM wParam,  // first message parameter  
  8.   LPARAM lParam   // second message parameter  
  9. );  
  10.   
  11. int WINAPI WinMain(  HINSTANCE hInstance,  // handle to current instance  
  12.   HINSTANCE hPrevInstance,  // handle to previous instance  
  13.   LPSTR lpCmdLine,      // pointer to command line  
  14.   int nCmdShow          // show state of window  
  15.   )  
  16. {  
  17.     WNDCLASS wndclass;  
  18.     HWND hwnd;  
  19.     HMENU hmenu;  
  20.     MSG msg;  
  21.   
  22.     //设计窗口类  
  23.     wndclass.cbClsExtra=0;  
  24.     wndclass.cbWndExtra=0;  
  25.     wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  
  26.     wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);  
  27.     wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);  
  28.     wndclass.hInstance=hInstance;  
  29.     wndclass.lpfnWndProc=textprom;  
  30.     wndclass.lpszClassName="text";  
  31.     wndclass.lpszMenuName=NULL;  
  32.     //wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);  
  33.     wndclass.style=CS_HREDRAW | CS_VREDRAW;  
  34.       
  35.     //注册窗口类  
  36.     if(!RegisterClass(&wndclass))  
  37.     {  
  38.         MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);  
  39.     }  
  40.   
  41.     //创建无菜单资源的窗口窗口  
  42.     hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,0,0,  
  43.         500,300,NULL,NULL,hInstance,NULL);  
  44.       
  45.     /* 
  46.     //载入菜单资源 
  47.     hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1)); 
  48.     //创建有菜单资源的窗口 
  49.     hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT, 
  50.         CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);*/  
  51.       
  52.     //载入菜单资源,并在窗口加载菜单资源  
  53.     hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));  
  54.     SetMenu(hwnd,hmenu);  
  55.   
  56.     //显示更新窗口  
  57.     ShowWindow(hwnd,nCmdShow);  
  58.     UpdateWindow(hwnd);  
  59.   
  60.     //消息循环  
  61.     while(GetMessage(&msg,NULL,0,0))  
  62.     {  
  63.         TranslateMessage(&msg);  
  64.         DispatchMessage(&msg);  
  65.     }  
  66.   
  67.     return msg.wParam;  
  68. }  
  69.   
  70. LRESULT CALLBACK textprom(  
  71.   HWND hwnd,      // handle to window  
  72.   UINT uMsg,      // message identifier  
  73.   WPARAM wParam,  // first message parameter  
  74.   LPARAM lParam   // second message parameter  
  75. )  
  76. {  
  77.     switch(uMsg)  
  78.     {  
  79.     case WM_DESTROY:  
  80.         PostQuitMessage(0);  
  81.         break;  
  82.     }  
  83.     return DefWindowProc(hwnd,uMsg,wParam,lParam);  
  84. }  

运行结果:

技术分享

卸载菜单

步骤:

技术分享

代码示例:

  1. HMENU hmenu;  
  2. switch(uMsg)  
  3. {  
  4. //添加鼠标右键单击事件响应处理,即卸载菜单  
  5. case WM_LBUTTONDOWN:  
  6.     hmenu = GetMenu(hwnd);  
  7.     if(hmenu != NULL)  
  8.     {  
  9.         SetMenu(hwnd,NULL);  
  10.         DestroyMenu(hmenu);  
  11.     }  
  12.     break;  
  13.   
  14. .....  

运行结果:

技术分享 

单击鼠标右键后

技术分享

菜单常用的操作


菜单项常用操作 

  • 设置默认菜单项  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操作
    1. //在程序源码的显示更新窗口  
    2. ShowWindow(hwnd,nCmdShow);  
    3. UpdateWindow(hwnd);  
    4.   
    5. //在程序源码的显示更新窗口后插入  
    6. //插入菜单项  
    7. AppendMenu(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),MF_ENABLED,40012,"菜单项1.1.3");  
    程序运行结果:
      技术分享
  • 让上图菜单项1.1.1禁用:
    1. //在程序源码的显示更新窗口  
    2. ShowWindow(hwnd,nCmdShow);  
    3. UpdateWindow(hwnd);  
    4.   
    5. //在程序源码的显示更新窗口后插入  
    6. //插入菜单项  
    7. AppendMenu(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),MF_ENABLED,40012,"菜单项1.1.3");  
    8. //禁用菜单项  
    9. EnableMenuItem(GetSubMenu(GetSubMenu(GetMenu(hwnd),0),0),0,MF_BYPOSITION | MF_GRAYED);  
    运行结果:
    技术分享

创建弹出菜单


步骤:

  • 载入菜单资源
    (用 GetSubMenu,非 LoadMenu 或 getMenu,因为后两种获得的菜单句柄都是主菜单的句柄,而主菜单句柄不适合用 TrackPopupMenu 显示弹出菜单,若用的是主菜单句柄作为弹出菜单句柄时候效果如下图所示)
    技术分享
  • 调用 TrackPopupMenu 显示弹出菜单

流程图如下:

技术分享

代码示例:

.rc 资源内容

  1. /***********************************************/  
  2. //主菜单  
  3. IDR_MENU1 MENU   
  4. BEGIN  
  5.     POPUP "菜单1"  
  6.     BEGIN  
  7.         POPUP "子菜单1.1"  
  8.         BEGIN  
  9.             MENUITEM "菜单项1.1.1",                    ID_40001  
  10.             MENUITEM "菜单项1.2.1",                    ID_40002  
  11.         END  
  12.         MENUITEM "菜单项1.2",                      ID_40003  
  13.         MENUITEM SEPARATOR  
  14.         MENUITEM "菜单项1.3",                      ID_40004  
  15.         MENUITEM "菜单项1.4",                      ID_40005  
  16.     END  
  17.     POPUP "菜单2"  
  18.     BEGIN  
  19.         MENUITEM "菜单项2.1",                      ID_40006  
  20.         MENUITEM "菜单项2.2",                      ID_40007  
  21.     END  
  22. END  
  23. /***********************************************/  
  24. //弹出菜单  
  25. IDR_MENU2 MENU   
  26. BEGIN  
  27.     POPUP "弹出菜单"  
  28.     BEGIN  
  29.         MENUITEM "弹出菜单项1.1",                    ID_A_40012  
  30.         MENUITEM "弹出菜单项1.2",                    ID_A_40013  
  31.     END  
  32. END  

加载弹出菜单

  1. hmenuPop=GetSubMenu(LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2)),0);  

显示弹出菜单

  1. POINT p;  
  2. switch(uMsg)  
  3. {  
  4. //添加鼠标左键单击事件响应处理,即显示弹出菜单  
  5. case WM_RBUTTONDOWN:  
  6.     p.x=LOWORD(lParam);   
  7.     p.y=HIWORD(lParam);  
  8.     //将窗口坐标转换成屏幕坐标  
  9.     ClientToScreen(hwnd,&p);   
  10.     TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);  
  11.     break;  
  12.   
  13. .....  

 程序运行结果(在鼠标右键单击后):

技术分享

菜单加速键


 .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资源:

  1. IDR_ACCELERATOR1 ACCELERATORS   
  2. BEGIN  
  3.     "K",            ID_40009,               VIRTKEY, CONTROL, ALT, NOINVERT  
  4. END  

 加载菜单加速资源:

  1. HACCEL haccel;  
  2. haccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));  

 更改消息循环:

  1. while(GetMessage(&msg,NULL,0,0))  
  2. {  
  3.     if(!TranslateAccelerator(hwnd,haccel,&msg))  
  4.     {  
  5.     TranslateMessage(&msg);  
  6.     DispatchMessage(&msg);  
  7.     }  
  8. }  

 添加菜单项1.4 事件响应:

  1. switch(uMsg)  
  2. {  
  3. //添加鼠标左键单击事件响应处理,即卸载对话框  
  4. case WM_RBUTTONDOWN:  
  5.     p.x=LOWORD(lParam);   
  6.     p.y=HIWORD(lParam);  
  7.     //将窗口坐标转换成屏幕坐标  
  8.     ClientToScreen(hwnd,&p);   
  9.     TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);  
  10.     break;  
  11. //添加菜单响应事件  
  12. case WM_COMMAND:  
  13.     switch(LOWORD(wParam))  
  14.     {  
  15.         //添加菜单项1.4响应事件  
  16.     case ID_40009:  
  17.         MessageBox(hwnd,"success!","test",MB_OK);  
  18.         break;  
  19.     }  
  20.     break;  
  21.   
  22. .....  

程序源码:

  1. #include<windows.h>  
  2. #include"resource.h"  
  3.   
  4. HMENU hmenuPop;//弹出菜单句柄  
  5.   
  6. LRESULT CALLBACK textprom(  
  7.     HWND hwnd,      // handle to window  
  8.     UINT uMsg,      // message identifier  
  9.     WPARAM wParam,  // first message parameter  
  10.     LPARAM lParam   // second message parameter  
  11. );  
  12.   
  13. int WINAPI WinMain(  
  14.     HINSTANCE hInstance,  // handle to current instance  
  15.     HINSTANCE hPrevInstance,  // handle to previous instance  
  16.     LPSTR lpCmdLine,      // pointer to command line  
  17.     int nCmdShow          // show state of window  
  18. )  
  19. {  
  20.     WNDCLASS wndclass;  
  21.     HWND hwnd;  
  22.     HMENU hmenu;  
  23.     MSG msg;  
  24.     HACCEL haccel;  
  25.   
  26.     //设计窗口类  
  27.     wndclass.cbClsExtra=0;  
  28.     wndclass.cbWndExtra=0;  
  29.     wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  
  30.     wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);  
  31.     wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);  
  32.     wndclass.hInstance=hInstance;  
  33.     wndclass.lpfnWndProc=textprom;  
  34.     wndclass.lpszClassName="text";  
  35.     wndclass.lpszMenuName=NULL;  
  36.     //wndclass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);  
  37.     wndclass.style=CS_HREDRAW | CS_VREDRAW;  
  38.   
  39.     //注册窗口类  
  40.     if(!RegisterClass(&wndclass))  
  41.         MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);  
  42.   
  43.     //创建无菜单资源的窗口窗口  
  44.     hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,0,0,500,300,NULL,NULL,hInstance,NULL);  
  45.   
  46.     /* 
  47.     //载入菜单资源 
  48.     hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1)); 
  49.     //创建有菜单资源的窗口 
  50.     hwnd=CreateWindow("text","hellow world",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT, 
  51.     CW_USEDEFAULT,CW_USEDEFAULT,NULL,hmenu,hInstance,NULL);*/  
  52.   
  53.     //载入菜单资源,并在窗口加载菜单资源  
  54.     hmenu=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1));  
  55.     SetMenu(hwnd,hmenu);  
  56.   
  57.     //载入弹出菜单资源  
  58.     hmenuPop=GetSubMenu(LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2)),0);  
  59.     //hmenuPop=LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU2));  
  60.   
  61.     //显示更新窗口  
  62.     ShowWindow(hwnd,nCmdShow);  
  63.     UpdateWindow(hwnd);  
  64.   
  65.     //加载菜单加速资源  
  66.     haccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDR_ACCELERATOR1));  
  67.   
  68.     //消息循环  
  69.     while(GetMessage(&msg,NULL,0,0))  
  70.     {  
  71.         if(!TranslateAccelerator(hwnd,haccel,&msg))  
  72.         {  
  73.         TranslateMessage(&msg);  
  74.         DispatchMessage(&msg);  
  75.         }  
  76.     }  
  77.   
  78.     return msg.wParam;  
  79. }  
  80.   
  81. LRESULT CALLBACK textprom(  
  82.     HWND hwnd,      // handle to window  
  83.     UINT uMsg,      // message identifier  
  84.     WPARAM wParam,  // first message parameter  
  85.     LPARAM lParam   // second message parameter  
  86. )  
  87. {  
  88.     POINT p;  
  89.     switch(uMsg)  
  90.     {  
  91.     //添加鼠标左键单击事件响应处理,即卸载对话框  
  92.     case WM_RBUTTONDOWN:  
  93.         p.x=LOWORD(lParam);   
  94.         p.y=HIWORD(lParam);  
  95.         //将窗口坐标转换成屏幕坐标  
  96.         ClientToScreen(hwnd,&p);   
  97.         TrackPopupMenu(hmenuPop,TPM_LEFTALIGN | TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);  
  98.         break;  
  99.     //添加菜响应事件  
  100.     case WM_COMMAND:  
  101.         switch(LOWORD(wParam))  
  102.         {  
  103.         //添加菜单项1.4响应事件  
  104.         case ID_40009:  
  105.             MessageBox(hwnd,"success!","test",MB_OK);  
  106.             break;  
  107.         }  
  108.         break;  
  109.     case WM_DESTROY:  
  110.         PostQuitMessage(0);  
  111.         break;  
  112.     }  
  113.     return DefWindowProc(hwnd,uMsg,wParam,lParam);  
  114. }  

运行结果(在单击菜单项1.4或者按下Ctrl+Alt+K的组合键时):

技术分享


菜单编写(VC_Win32)