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

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

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

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

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

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

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

  1. // 在这里把菜单附加上,成为类级别
  2. wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN);//整个菜单资源的ID,不是菜单项
  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参数。

  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);

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

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

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

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

  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中。

  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声明变量一样。所以,我们上面的代码可以改为:

  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. ........

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

  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;

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

    1. #include <Windows.h>
    2. #include "resource.h"
    3. // 声明消息处理函数
    4. LRESULT CALLBACK WindowMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    5. //入口点
    6. int WINAPI WinMain(
    7. HINSTANCE hthisInstance,//当前实例句柄
    8. HINSTANCE hPrevInstance,//钱一个实例句柄,一般不使用
    9. LPSTR cmdline,//命令行参数
    10. int nShow)//窗口的显示方式
    11. {
    12. // 设计窗口类
    13. WNDCLASS wc = { };
    14. wc.lpszClassName = L"MainWd";
    15. wc.hInstance  = hthisInstance;
    16. wc.lpfnWndProc = WindowMainProc;
    17. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    18. // 在这里把菜单附加上,成为类级别
    19. //wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN);//整个菜单资源的ID,不是菜单项
    20. // 让窗口自动重绘
    21. wc.style = CS_HREDRAW | CS_VREDRAW;
    22. // 注册窗口类
    23. RegisterClass(&wc);
    24. // 创建窗口
    25. HWND hm = CreateWindow(
    26. L"MainWd",
    27. L"我的应用程序",
    28. WS_OVERLAPPEDWINDOW,
    29. 25,
    30. 18,
    31. 380,
    32. 280,
    33. NULL,
    34. LoadMenu(hthisInstance,MAKEINTRESOURCE(IDR_MAIN)),
    35. hthisInstance,
    36. NULL);
    37. if(hm == NULL)
    38. return 0;
    39. // 显示窗口
    40. ShowWindow(hm, SW_SHOW);
    41. // 消息循环
    42. MSG msg;
    43. while(GetMessage(&msg, NULL, 0, 0))
    44. {
    45. TranslateMessage(&msg);
    46. DispatchMessage(&msg);
    47. }
    48. return 0;
    49. }
    50. LRESULT CALLBACK WindowMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    51. {
    52. // 获取窗口上的整个菜单栏的句柄
    53. auto hmm = GetMenu(hwnd);
    54. // 获取第一个弹出菜单,即[水果]菜单
    55. auto hfmn = GetSubMenu(hmm, 0);
    56. switch(msg)
    57. {
    58. case WM_COMMAND:
    59. {
    60. //判断用户选了哪个菜单
    61. switch(LOWORD(wParam))
    62. {
    63. case IDM_APPLE:
    64. CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_APPLE, MF_BYCOMMAND);
    65. MessageBox(hwnd,L"你选择了苹果。",L"提示",MB_OK);
    66. break;
    67. case IDM_PEAR:
    68. CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_PEAR, MF_BYCOMMAND);
    69. MessageBox(hwnd,L"你选择了梨子。", L"提示", MB_OK);
    70. break;
    71. case IDM_BANANA:
    72. CheckMenuRadioItem(hfmn, IDM_APPLE, IDM_BANANA, IDM_BANANA, MF_BYCOMMAND);
    73. MessageBox(hwnd, L"你选择了香蕉。", L"提示", MB_OK);
    74. break;
    75. }
    76. }
    77. return 0;
    78. case WM_DESTROY:
    79. PostQuitMessage(0);
    80. return 0;
    81. default:
    82. return DefWindowProc(hwnd, msg, wParam, lParam);
    83. }
    84. }

跟我一起玩Win32开发(5):具有单选标记的菜单的更多相关文章

  1. 跟我一起玩Win32开发(4):创建菜单

    也不知道发生什么事情,CSDN把我的文章弄到首页,结果有不少说我在误人子弟,是啊,我去年就说过了,如果你要成为砖家级人物,请远离我的博客,我这个人没什么特长,唯一厉害的一点就是不相信权威,鄙视砖家,所 ...

  2. 跟我一起玩Win32开发(转自CSDN-东邪独孤)

    跟我一起玩Win32开发(1):关于C++的几个要点 跟我一起玩Win32开发(2):完整的开发流程 跟我一起玩Win32开发(3):窗口的重绘 跟我一起玩Win32开发(4):创建菜单 跟我一起玩W ...

  3. 跟我一起玩Win32开发(17):启动和结束进程

    这里我再次说明一下,我不知道为什么,现在的人那么喜欢走极端,估计是价值观都“升级”了的缘故吧. 我撰写这一系列Win32相关的文章,并不是叫大家一定要用Win32去开发项目,仅仅是给大家了解一下,Wi ...

  4. 跟我一起玩Win32开发(18):使用对话框的两个技巧

    相信大家知道对话框怎么用了,就是先用“资源编辑器”设计一个对话框,然后在代码中加载处理.今天,我向大家分享两个使用对话框的技巧,还是比较实用的.不用担心,先喝杯茶,很简单的,一点也不复杂,总之,看俺写 ...

  5. 跟我一起玩Win32开发(19):浏览和打开文件

    在应用程序中,我们很经常要实现的功能,是Open文件或保存文件对话框,让用户来选择一个或N个文件.本文我将介绍两种思路,第一种方法较为复杂,第二种方法较为简单. 方法一:老规矩 这是一种传统方法,使用 ...

  6. 跟我一起玩Win32开发(12):使用控件——单选按钮

    今天,咱们还是接着玩“控件斗地主”,这是我原创的超级游戏,有益身心健康,玩一朝,十年少. 哦,对,脑细胞极速运动了一下,想起了一个问题,这个破问题虽然网上有很多种解决方案,但是,并没有让所有人都解决问 ...

  7. 跟我一起玩Win32开发(21):复制&粘贴&剪贴板操作

    我要提醒一下大家,看了我的博文学到的知识,千万不要用于实际开发,不然你会被你的上司骂:“妈的,这些东西哪来的,从来没有人这样做过.”不信你试试,脑细胞被冻结的经理或者技术总监们肯定会这样说的. 如果是 ...

  8. 跟我一起玩Win32开发(2):完整的开发流程

    上一篇中我给各位说了一般人认为C++中较为难的东西——指针.其实对于C++,难点当然不局限在指针这玩意儿上,还有一些有趣的概念,如模板类.虚基类.纯虚函数等,这些都是概念性的东西,几乎每一本C++书上 ...

  9. 跟我一起玩Win32开发(25):监视剪贴板

    自从郭大侠和蓉儿离开桃花岛后,最近岛比较寂静,有一种“门前冷落鞍马稀”的感觉.于是,老邪就拿出<九阴真经>认真阅读,同时用迅雷下载经典大剧<汉武大帝>晚上睡觉前看上几集,老邪一 ...

  10. 跟我一起玩Win32开发(23):渐变颜色填充

    GradientFill函数可以对特定的矩形区域或者三角形区域进行渐变颜色的填充.我们先来看看GradientFill函数到底长得什么样子,帅不帅. BOOL GradientFill( _In_   ...

随机推荐

  1. 读写锁(pthread)

    读写锁: 用于对于某个给定资源的共享访问,而不是像互斥锁那样,将所有试图进入临界区的线程都阻塞住 相关内容: 线程互斥锁 分配规则:(写独占,读共享) 1.只要没有线程持有某个给定的读写锁用于写,那么 ...

  2. Linux fcntl函数详解

    功能描述:根据文件描述词来操作文件的特性. 文件控制函数          fcntl -- file control 头文件: #include <unistd.h> #include ...

  3. (转) Universal-Image-Loader使用大全(史上最屌)

    转载自http://blog.csdn.net/zenjj11/article/details/38728481 项目介绍: Android上最让人头疼的莫过于从网络获取图片.显示.回收,不论什么一个 ...

  4. Codeforces Round #422 (Div. 2) D. My pretty girl Noora 数学

    D. My pretty girl Noora     In Pavlopolis University where Noora studies it was decided to hold beau ...

  5. springCloud和docker笔记(1)——微服务架构概述

    1.微服务设计原则 1)单一职责原则:只关注整个系统中单独.有界限的一部分(SOLID原则之一) 2)服务自治原则:具备独立的业务能力和运行环境,可独立开发.测试.构建.部署 3)轻量级通信机制:体量 ...

  6. JavaScript算法题(二) && 数组filter使用

    1.Let's implement the reject() function... 例: var odds = reject([1, 2, 3, 4, 5, 6], function(num){ r ...

  7. mysql优化----大数据下的分页,延迟关联,索引与排序的关系,重复索引与冗余索引,索引碎片与维护

    理想的索引,高效的索引建立考虑: :查询频繁度(哪几个字段经常查询就加上索引) :区分度要高 :索引长度要小 : 索引尽量能覆盖常用查询字段(如果把所有的列都加上索引,那么索引就会变得很大) : 索引 ...

  8. Hive两种访问方式:HiveServer2 和 Hive Client

        老版HiveClient:  要求比较多,需要Hive和Hadoop的jar包,各配置环境.       HiveServer2:   使得与YARN和HDFS的连接从Client中独立出来, ...

  9. POJ1426 Find The Multiple —— BFS

    题目链接:http://poj.org/problem?id=1426 Find The Multiple Time Limit: 1000MS   Memory Limit: 10000K Tota ...

  10. Centos6.8更好yum源

    第一步:备份你的原镜像文件,以免出错后可以恢复. mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.back ...