上一篇我们学习了利用windows API创建工具栏和菜单栏,与上一篇紧密联系的就是菜单栏,菜单栏是一个大多数复杂一些的Windows应用程序不可或缺的部分。比如下图就是Windows自带的记事本的菜单栏:

菜单一般都是在标题栏下,工具栏以上,常常叫主菜单或顶级菜单(top-level menu),顶级菜单可能还会有弹出菜单(popup menu)或子菜单(submenu)。弹出菜单还有被“选中”(checked)状态,各菜单还有启用、禁用状态。

每一个菜单都有一个ID与之对应,当某个菜单被点击是,程序在WM_COMMAND消息中把菜单ID传给应该消息处理函数,就能知道哪个菜单被按下。

菜单栏的创建最常见是利用VS的菜单资源编辑器,然后加载该资源。比如下面的代码片段在创建主窗体时使用了LoadMenu函数加载菜单资源编辑的菜单:

hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(ID_MENU));
hWnd = CreateWindow(TEXT(“myclass”), TEXT(“mytitle”), WS_OVERLAPPENDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, hMenu, hInstance, NULL);

另外一种办法是在处理WM_CREATE消息是调用SetMenu函数来设置菜单:

SetMenu(hWnd, hMenu);

本文一直秉承一个原则,就是采用API的方式来创建而不是资源。因为用API来创建虽然麻烦一点,但是更加独立,比如如果不是用VS环境,那就可能没有资源编辑了,要是把我们的源程序在非VS的环境下编译就能显示出通用性和可移植性了。

  • 菜单消息

当用户选择一个菜单时,会产生WM_INITMENU消息和WM_MENUSELECT,WM_INITMENU消息使得我们有机会在菜单的选中之前做一些事情,而WM_MENUSELECT消息是在菜单被选中或者光标移到该菜单时被发送,我们可以利用这个消息进行菜单选中时的处理。

WM_INITMENUPOPUP消息在一个弹出菜单显示前发送,可以用来修改一些菜单显示。

最重要、最常用的就是上面我们提到的WM_COMMAND消息,当菜单被点击时就会产生这个消息。上面的消息对应的参数意义请参考MSDN。

  • 菜单创建

菜单相关的API有好几十个,我们这里只用一些常用的API函数,这几个函数基本可以完成菜单的基本功能,更多的菜单函数和功能的请参考MSDN。

函数CreateMenu可以创建一个菜单,CreatePopupMenu创建一个下拉式或弹出是菜单。函数AppendMenu可以追加一个菜单项,函数InsertMenu可以插入一个菜单项,TrackPopupMenu函数将在指定的位置显示一个弹出菜单。这几个菜单原型如下:

HMENU CreateMenu(VOID);
HMENU CreatePopupMenu(VOID);
BOOL AppendMenu(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, LPCTSTR lpNewItem);
BOOL InsertMenu(HMENU hMenu, UINT uPosition, UINT uFlags, PTR uIDNewItem, LPCTSTR lpNewItem);
BOOL TrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, int nReserved, HWND hWnd, HWND prcRect);

其实菜单的常用部分大都是用这几个函数完成的,并不复杂。不说了,直接一边上代码一边解释更直接,我们通过以下demo演示运用这几个常见的函数来创建和使用菜单:

#include <windows.h>

#define IDM_FILE_NEW   1001
#define IDM_FILE_OPEN 1002
#define IDM_FILE_SAVE 1003 #define IDM_EDIT_COPY 1004
#define IDM_EDIT_PASTE 1005
#define IDM_EDIT_HL 1006 #define IDM_VIEW_FULL 1007
#define IDM_VIEW_HALF 1008
#define IDM_VIEW_PART 1009 #define IDM_FILE_OPEN_SOLUTION 10021
#define IDM_FILE_OPEN_PROJECT 10022 static TCHAR szAppName[] = TEXT("Menubar");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass))
{
MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
} hWnd = CreateWindow(szAppName, // window class name
szAppName, // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
400, // initial x size
300, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd); while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
} HMENU CreateMenuBar(void)
{
//总菜单
HMENU hMenu = CreateMenu(); //文件菜单
HMENU hFileMenu = CreateMenu();
AppendMenu(hFileMenu, MF_STRING, IDM_FILE_NEW, TEXT("&New"));
AppendMenu(hFileMenu, MF_SEPARATOR, 0, NULL); //插入一条横条,请看运行效果
AppendMenu(hFileMenu, MF_STRING, IDM_FILE_SAVE, TEXT("&Save"));
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, TEXT("File(&F)")); //文件的二级子菜单
HMENU hSubMenu = CreateMenu();
AppendMenu(hSubMenu, MF_STRING, IDM_FILE_OPEN_SOLUTION, TEXT("So&lution"));
AppendMenu(hSubMenu, MF_STRING, IDM_FILE_OPEN_PROJECT, TEXT("Pro&ject"));
//将该二级菜单插入到第二条的位置
InsertMenu(hFileMenu, 1, MF_BYPOSITION|MF_POPUP, (UINT_PTR)hSubMenu, TEXT("Open")); //编辑菜单
hFileMenu = CreateMenu();
AppendMenu(hFileMenu, MF_STRING, IDM_EDIT_COPY, TEXT("&Copy"));
AppendMenu(hFileMenu, MF_STRING, IDM_EDIT_PASTE, TEXT("&Paste"));
AppendMenu(hFileMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hFileMenu, MF_STRING|MF_CHECKED, IDM_EDIT_HL, TEXT("&Update"));//增加一个check选项
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, TEXT("Edit(&E)")); //缩放菜单
hFileMenu = CreateMenu();
AppendMenu(hFileMenu, MF_STRING, IDM_VIEW_HALF, TEXT("&Half"));
AppendMenu(hFileMenu, MF_SEPARATOR, 0, NULL);
//设置一个灰色不可选的菜单,该菜单可以用EnableMenuItem函数修改可选状态
AppendMenu(hFileMenu, MF_STRING|MF_GRAYED, IDM_VIEW_PART, TEXT("P&art"));
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, TEXT("Zoom(&Z)"));
return hMenu;
} static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps; switch (message)
{
case WM_CREATE:
{
HMENU hMenu = CreateMenuBar();
SetMenu(hWnd, hMenu); //以上只是创建了菜单,需要设置
}
return 0; case WM_RBUTTONUP:
{
POINT point;
point.x = LOWORD(lParam);
point.y = HIWORD(lParam);
ClientToScreen(hWnd, &point); //这里的坐标是相对于屏幕的,需要转换为客户坐标
HMENU hSubMenu = GetSubMenu(GetMenu(hWnd), 0); //获取菜单的第0个子菜单,用这个菜单来演示弹出菜单
TrackPopupMenu(hSubMenu, TPM_RIGHTBUTTON, point.x, point.y, 0, hWnd, NULL);
}
return 0; case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_FILE_NEW:
MessageBox(hWnd, TEXT("you click new file button"), TEXT("hint"), MB_OK);
break;
default:
break;
}
return 0; case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
;
EndPaint(hWnd, &ps);
return 0; case WM_DESTROY:
PostQuitMessage(0);
return 0 ;
} return DefWindowProc (hWnd, message, wParam, lParam);
}

本demo运行后点击“文件”菜单如下:

点击“编辑”菜单如下:

鼠标右键弹出快捷菜单:

文篇只演示了常用的菜单,其他比如位图菜单、非客户区弹出菜单等更多内容有兴趣在讨论,也可以参考MSDN的相关函数自己进行测试。本文的菜单栏编程结合上一篇的工具栏和状态栏内容以及第二篇的创建常用控件部分,基本可以完成窗口应用程序的界面编程了。当然再次强调,我们这些都是基于Windows API函数完成的,可能很多人会说,我用MFC,资源编辑器,对话框下的控件面板、甚至VB、C#都可以很快编写出这些界面。没错,但是隐藏在这些的下面还是会回到我们这些基本的API上。

更多经验交流可以加入Windows编程讨论QQ群454398517

关注微信公众平台:程序员互动联盟(coder_online),你可以第一时间获取原创技术文章,和(java/C/C++/Android/Windows/Linux)技术大牛做朋友,在线交流编程经验,获取编程基础知识,解决编程问题。程序员互动联盟,开发人员自己的家。

转载请注明出处http://www.coderonline.net/,谢谢合作!

【Windows编程】系列第七篇:Menubar的创建和使用的更多相关文章

  1. MongoDB基础教程系列--第七篇 MongoDB 聚合管道

    在讲解聚合管道(Aggregation Pipeline)之前,我们先介绍一下 MongoDB 的聚合功能,聚合操作主要用于对数据的批量处理,往往将记录按条件分组以后,然后再进行一系列操作,例如,求最 ...

  2. 走进windows编程的世界-----入门篇

    1   Windows编程基础 1.1Win32应用程序基本类型 1)  控制台程序 不须要完好的windows窗体,能够使用DOS窗体方式显示 2)  Win32窗体程序 包括窗体的程序,能够通过窗 ...

  3. 《windows核心编程系列》七谈谈用户模式下的线程同步

    用户模式下的线程同步 系统中的线程必须访问系统资源,如堆.串口.文件.窗口以及其他资源.如果一个线程独占了对某个资源的访问,其他线程就无法完成工作.我们也必须限制线程在任何时刻都能访问任何资源.比如在 ...

  4. [C# 网络编程系列]专题七:UDP编程补充——UDP广播程序的实现

    转自:http://www.cnblogs.com/zhili/archive/2012/09/03/2666974.html 上次因为时间的关系,所以把上一个专题遗留下的一个问题在本专题中和大家分享 ...

  5. Python高级网络编程系列之第二篇

    在上一篇中,我们深入探讨了TCP/IP协议的11种状态,理解这些状态对我们编写服务器的时候有很大的帮助,但一般写服务器都是使用C/Java语言,因为这些语言对高并发的支持特别好.我们写的这些简单的服务 ...

  6. Python高级网络编程系列之第一篇

    在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...

  7. 深入理解ajax系列第七篇——传递JSON

    前面的话 虽然ajax全称是asynchronous javascript and XML.但目前使用ajax技术时,传递JSON已经成为事实上的标准.因为相较于XML而言,JSON简单且方便.本文将 ...

  8. 深入理解ajax系列第七篇

    前面的话 虽然ajax全称是asynchronous javascript and XML.但目前使用ajax技术时,传递JSON已经成为事实上的标准.因为相较于XML而言,JSON简单且方便.本文将 ...

  9. Java系列--第七篇 基于Maven的Android开发实战项目

    本篇是基于<Android应用案例开发大全,吴亚峰等著>的项目开发实例源码,其中有些图片,我做了一些修改,用于个人学习,请勿用于商业. 1, 日程管理专家 mvn archetype:ge ...

随机推荐

  1. Visual Studio 2013 Unit Test Project App.config文件设置方法

    开放中经常会要做单元测试,新的项目又没有单元测试项目,怎么才能搭建一个单元测试项目呢? 下面跟我四步走,如有错误之处,还请指正! 1.添加项目 2.添加配置文件 新建app.config文件,注意不是 ...

  2. C# foreach 中获取索引index的方法

    方法一: int i = 0; foreach (var item in arr) { i++; } 方法二: foreach (var item in arr) { int index = arr. ...

  3. jquery实现tab切换完整代码

    代码如下,保存到html文件打开: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " ...

  4. WCF入门教程(五)配置文件

    WCF入门教程(五)配置文件 服务协定以及实现写好后,需要将相关服务公布出去,就需要HOST来承载,供客户端来调用. 承载服务有两种方式,一种通过配置文件,一种通过代码进行配置.上一章已经介绍了代码方 ...

  5. [WCF编程]7.实例上下文模式

    一.实例上下文模式概述 实例上下文(IntanceContext Mode)表示服务端的服务实例与客户端的服务代理的绑定方式. 在实例化服务器对象时,WCF采用了3种不同的模式:单调(Per-Call ...

  6. 【NopCommerce源码架构学习-二】单例模式实现代码分析

    单例模式是是常用经典十几种设计模式中最简单的..NET中单例模式的实现也有很多种方式.下面我来介绍一下NopCommerce中单例模式实现. 我之前的文章就分析了一下nop中EngineContext ...

  7. python tornado websocket 实时日志展示

    一.主题:实时展示服务器端动态生成的日志文件 二.流程: 1. 客户端浏览器与服务器建立websocket 链接,服务器挂起保存链接实例,等待新内容触发返回动作 2. 日志服务器脚本循环去发现新内容, ...

  8. [转] js实现html table 行,列锁定

    js实现html table 表头,指定列锁定 实现效果如下: 感兴趣的朋友可以直接复制出来运行看效果. <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTM ...

  9. 关于移动端swiper的2种样式重置

    手机查看效果地址:猛戳 ,PC端查看可以缩放浏览器窗口看效果~~ 思路:主要考虑选择器优先级大于默认就可以随意撸码 注意:该demo里用的mobile-adaptive.js是让页面以rem自适应,也 ...

  10. [deviceone开发]-do_ImageView实现正圆的示例

    一.简介 我们经常需要用一个正圆形状的图片来设置头像,在do平台这个比较容易,就是通过设置圆角来实现,但是有几个小技巧需要解释一下 主要组件:do_ImageView 二.效果图 三.相关下载 htt ...