• 跟我一起玩Win32开发(5):具有单选标记的菜单


    帅哥们,美女们,下午好,我又来误人子弟,请做好准备。

    今天,我们的目的是,想要实现下图中的这种菜单效果。

    就是一种类似单选按钮的菜单,多个菜单项中,同时只有一个会被选中。

    首先,我们在资源编辑器中,设计一个菜单资源。这个资源编辑器在管理资源ID的时候,有些问题,有时候不同步更新,有时候会保存不到,反正就会混乱。如果遇到问题,你可以先把菜单设计好,接着打开resource.h,手动把这些ID和它的值改一下。为了使这三个菜单项能形成一个组,必须让它们的ID值是连续的,比如我这里让它们分别为501,502,503。

    101指的是整个菜单资源,后三个都是子菜单项。如果想更保险的话,可以在【解决方案资源管理器】中右击资源文件(.rc结尾),选择【查看代码】,然后检查一下是否正确就可以了。

    现在菜单弄好了,下面我们来了解一下把菜单添加到窗口的两个类型。

    第一种是类级别的,也就是我们在设计窗口类时,直接指定给WNDCLASS结构的lpszMenuName成员,这样做意味着,在调用CreateWindow函数创建窗口时,无论你是否为窗口指定菜单,最终显示的窗口上都会有菜单,因为它是基于整个窗口类的。

    [cpp] view plain copy
     
    1. // 在这里把菜单附加上,成为类级别  
    2. wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN);//整个菜单资源的ID,不是菜单项  
    [cpp] view plain copy
     
    1. HWND hm = CreateWindow(  
    2.     L"MainWd",  
    3.     L"我的应用程序",  
    4.     WS_OVERLAPPEDWINDOW,  
    5.     25,  
    6.     18,  
    7.     380,  
    8.     280,  
    9.     NULL,  
    10.     NULL,  
    11.     hthisInstance,  
    12.     NULL);  

    这样在我们创建窗口时,哪怕你把hMenu参数设为NULL,最后显示的窗口都会有菜单,因为菜单是属于窗口类本身的。

    另一种方式,就是不设置为类级别的菜单,而是在调用CreateWindow时指定给hMenu参数。

    [cpp] view plain copy
     
    1. HWND hm = CreateWindow(  
    2.     L"MainWd",  
    3.     L"我的应用程序",  
    4.     WS_OVERLAPPEDWINDOW,  
    5.     25,  
    6.     18,  
    7.     380,  
    8.     280,  
    9.     NULL,  
    10.     LoadMenu(hthisInstance,MAKEINTRESOURCE(IDR_MAIN)),  
    11.     hthisInstance,  
    12.     NULL);  

    同时我们把设计窗口类时设置菜单的代码注释掉。

    [cpp] view plain copy
     
    1. // 在这里把菜单附加上,成为类级别  
    2. //wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN);//整个菜单资源的ID,不是菜单项  

    然后,我们运行这个程序,它还是有菜单的。

    接着,我们把CreateWindow的hMenu参数设置为NULL,

    [cpp] view plain copy
     
    1. HWND hm = CreateWindow(  
    2.     L"MainWd",  
    3.     L"我的应用程序",  
    4.     WS_OVERLAPPEDWINDOW,  
    5.     25,  
    6.     18,  
    7.     380,  
    8.     280,  
    9.     NULL,  
    10.     /*LoadMenu(hthisInstance,MAKEINTRESOURCE(IDR_MAIN))*/  
    11.     NULL,  
    12.     hthisInstance,  
    13.     NULL);  

    看看这时候运行程序,还能不能看到菜单。



    现在就看不到菜单了,这两种加载菜单的方式,就区别在这里。

    要为菜单实现单选标记,调用CheckMenuRadioItem函数,第一个参数是要在其子项中设置的单选的菜单的句柄,第二个参数和第三个参数指定合并为一个组的ID范围,在这个范围内的菜单项被看人为同一组,这一组中,每一次只能有一项被checked,第四个参数就指定在这组项中哪一个被选中,最后一个参数决定是用ID来标识还用从0开始的索引。

    但是,我们在改变菜单项单选状态前,必须获得【水果】弹出菜单的句柄。

    我们先来看一下,一般菜单栏的层次结构。

    它就像一个树形结构,一层一层往下展开,上图中,红色矩形画的部分是菜单的根,即整个菜单栏,蓝色矩形标注的是菜单栏的下一级,弹出菜单,如【文件】、【编辑】、【视图】这些,它们一般只负责弹出子项列表,自身不响应用户选择命令,这也是我们在资源编辑器中设计菜单时,不需要给它们ID号的原因。

    在【文件】下面又有了项,如图中黄色矩形标注的地方,如【新建】、【打开】。

    知道这个后,我们的思路就有了。

    1、调用GetMenu( 窗口句柄 )获取窗口中菜单栏的句柄;

    2、调用GetSubMenu(  菜单栏句柄,0 )获得【水果】弹出菜单的句柄,0表示菜单栏中的第一个元素,如果第二个就是1,我们的弹出菜只有【水果】一项;

    3、调用CheckMenuRadioItem函来来Check菜单。

    因为我们是在响应WM_COMMAND消息时作出响应的,所以这些代码应写在WindowProc中。

    [cpp] view plain copy
     
    1. LRESULT CALLBACK WindowMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     // 获取窗口上的整个菜单栏的句柄  
    4.     HMENU hmm = GetMenu(hwnd);  
    5.     // 获取第一个弹出菜单,即[水果]菜单  
    6.     HMENU hfmn = GetSubMenu(hmm, 0);  
    7.     switch(msg)  
    8.     {  
    9.     case WM_COMMAND:  
    10.         {  
    11.         .......  

    菜单句柄是HMENU类型,所以GetMenu和GetSubMenu函数都返回HMENU类型的值。其实,这里我给大家推荐一个技巧,就是使用auto关键字,我们无需管它函数什么,统一用auto关键字,它会根据代码上下文推断数据类型,就像C#里面的var声明变量一样。所以,我们上面的代码可以改为:

    [cpp] view plain copy
     
    1. LRESULT CALLBACK WindowMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     // 获取窗口上的整个菜单栏的句柄  
    4.     auto hmm = GetMenu(hwnd);  
    5.     // 获取第一个弹出菜单,即[水果]菜单  
    6.     auto hfmn = GetSubMenu(hmm, 0);  
    7.     switch(msg)  
    8.     {  
    9.     case WM_COMMAND:  
    10.         {  
    11.          ........  


    然后,我们响应命令消息。

    [cpp] view plain copy
     
    1. switch(msg)  
    2. {  
    3. case WM_COMMAND:  
    4.     {  
    5.         //判断用户选了哪个菜单  
    6.         switch(LOWORD(wParam))  
    7.         {  
    8.         case IDM_APPLE:  
    9.             CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_APPLE, MF_BYCOMMAND);  
    10.             MessageBox(hwnd,L"你选择了苹果。",L"提示",MB_OK);  
    11.             break;  
    12.         case IDM_PEAR:  
    13.             CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_PEAR, MF_BYCOMMAND);  
    14.             MessageBox(hwnd,L"你选择了梨子。", L"提示", MB_OK);  
    15.             break;  
    16.         case IDM_BANANA:  
    17.             CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_BANANA, MF_BYCOMMAND);  
    18.             MessageBox(hwnd, L"你选择了香蕉。", L"提示", MB_OK);  
    19.             break;  
    20.         }  
    21.     }  
    22.     return 0;  


    这样就得到单选菜单的效果了。下面是完整的代码清单。

    [cpp] view plain copy
     
      1. #include <Windows.h>  
      2. #include "resource.h"  
      3.   
      4. // 声明消息处理函数  
      5. LRESULT CALLBACK WindowMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);  
      6.   
      7. //入口点  
      8. int WINAPI WinMain(  
      9.     HINSTANCE hthisInstance,//当前实例句柄  
      10.     HINSTANCE hPrevInstance,//钱一个实例句柄,一般不使用  
      11.     LPSTR cmdline,//命令行参数  
      12.     int nShow)//窗口的显示方式  
      13. {  
      14.     // 设计窗口类  
      15.     WNDCLASS wc = { };  
      16.     wc.lpszClassName = L"MainWd";  
      17.     wc.hInstance  = hthisInstance;  
      18.     wc.lpfnWndProc = WindowMainProc;  
      19.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);  
      20.     // 在这里把菜单附加上,成为类级别  
      21.     //wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN);//整个菜单资源的ID,不是菜单项  
      22.     // 让窗口自动重绘  
      23.     wc.style = CS_HREDRAW | CS_VREDRAW;  
      24.     // 注册窗口类  
      25.     RegisterClass(&wc);  
      26.     // 创建窗口  
      27.     HWND hm = CreateWindow(  
      28.         L"MainWd",  
      29.         L"我的应用程序",  
      30.         WS_OVERLAPPEDWINDOW,  
      31.         25,  
      32.         18,  
      33.         380,  
      34.         280,  
      35.         NULL,  
      36.         LoadMenu(hthisInstance,MAKEINTRESOURCE(IDR_MAIN)),  
      37.         hthisInstance,  
      38.         NULL);  
      39.     if(hm == NULL)  
      40.         return 0;  
      41.     // 显示窗口  
      42.     ShowWindow(hm, SW_SHOW);  
      43.     // 消息循环  
      44.     MSG msg;  
      45.     while(GetMessage(&msg, NULL, 0, 0))  
      46.     {  
      47.         TranslateMessage(&msg);  
      48.         DispatchMessage(&msg);  
      49.     }  
      50.     return 0;  
      51. }  
      52.   
      53. LRESULT CALLBACK WindowMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)  
      54. {  
      55.     // 获取窗口上的整个菜单栏的句柄  
      56.     auto hmm = GetMenu(hwnd);  
      57.     // 获取第一个弹出菜单,即[水果]菜单  
      58.     auto hfmn = GetSubMenu(hmm, 0);  
      59.     switch(msg)  
      60.     {  
      61.     case WM_COMMAND:  
      62.         {  
      63.             //判断用户选了哪个菜单  
      64.             switch(LOWORD(wParam))  
      65.             {  
      66.             case IDM_APPLE:  
      67.                 CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_APPLE, MF_BYCOMMAND);  
      68.                 MessageBox(hwnd,L"你选择了苹果。",L"提示",MB_OK);  
      69.                 break;  
      70.             case IDM_PEAR:  
      71.                 CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_PEAR, MF_BYCOMMAND);  
      72.                 MessageBox(hwnd,L"你选择了梨子。", L"提示", MB_OK);  
      73.                 break;  
      74.             case IDM_BANANA:  
      75.                 CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_BANANA, MF_BYCOMMAND);  
      76.                 MessageBox(hwnd, L"你选择了香蕉。", L"提示", MB_OK);  
      77.                 break;  
      78.             }  
      79.         }  
      80.         return 0;  
      81.     case WM_DESTROY:  
      82.         PostQuitMessage(0);  
      83.         return 0;  
      84.     default:  
      85.         return DefWindowProc(hwnd, msg, wParam, lParam);  
      86.     }  
      87. }  
  • 相关阅读:
    根据时间戳获取年月日时分秒
    除法函数,乘法函数,加法函数,减法函数
    禁止鼠标点右键
    获取cookie,设置cookie,删除cookie
    解决 堆栈 出现的父对象和子对象相关联的问题 (深拷贝)
    团队展示
    用户规格说明书——结对编程
    测试与优化——结对编程
    程序开发——结对编程
    程序开发初体验
  • 原文地址:https://www.cnblogs.com/weekbo/p/8681895.html
Copyright © 2020-2023  润新知