【Windows编程】系列第七篇:Menubar的创建和使用

上一篇我们学习了利用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上。

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

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

时间: 2024-10-11 12:17:27

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

Windows Azure系列 -《基础篇》- 如何创建虚拟机

首先,使用自己的windows azure账号登陆管理平台manage.windowsazure.cn,找到并点击"虚拟机"标签,即可看到目前云平台中你所拥有的虚机实例,在我的环境中现在没有任何的虚机,所以我们可以通过点击图示的新建按钮进行选择或直接点击"创建虚拟机"进行创建: 点击"创建虚拟机",在DNS名称位置填写所建虚机的主机名,并选择映像和虚机大小(可选单核至8核,内存从768M至56G),这里说明一下,处于用户名不可以设置为常用的adm

Windows Azure系列 -《基础篇》- 存储

打开windows azure管理门户,点击下方的"新建", 定义一个存储名称,并设定好地缘组和位置,冗余的方式可以是地域之间的冗余,也可以是本地区域内的冗余,可以根据自己的实际情况选择. 目前windows azure在中国有两个数据中心可供选择,以托管订阅的虚机,建议选择离自己所在地位置更近一点的,访问效率会比较高. 设置完后,点击创建存储账号,既可以完成存储账号的创建工作. 在列表中我们可以看到创建的过程及状态, 通过点击所创建的存储,可以查看它的状态和修改存储配置, 在配置标签

Windows Azure系列 -《基础篇》- 创建虚拟网络

如何在Windows Azure中创建虚拟网络,以构建云环境中的虚拟局域网: 1.登陆Windows Azure平台,点击侧边栏网络按钮,在中间点击"创建虚拟网络". 2.在接下来的配置页面,填写虚拟网络的名称.选择地理外置和地缘组(如果已有),没有则选择创建新的地缘组. 3.接下来填写DNS服务器地址(没有则留空) 4.规划和配置IP网络,选择适用的地址空间. 5.最后点击确认按钮完成. Windows Azure系列 -<基础篇>- 创建虚拟网络,布布扣,bubuko.

Windows Azure系列 -《基础篇》- 基本设置

打开Windows Azure管理门户,在左侧边栏点击设置,展开设置页面 在这个页面中,我们可以看到你所拥有的订阅的信息和管理信息等. 这里简单说一下各项内容: 1. 订阅 订阅部分显示你目前账号所拥有的订阅信息以及管理帐户,如果你有多个订阅并绑定在一起,那么它将显示你所有的订阅信息. 2. 管理证书 在管理证书页面,我们可以管理用于windows azure的证书,点上传以便将证书导入windows azure. 3. 管理员 在windows azure中,如果需要有多个管理人员,我们可以在

Windows Azure系列 -《基础篇》- 计划程序

打开Windows Azure管理门户,在左侧边栏点击设置,展开计划程序页面,我们可以看到目前的作业情况: 在计划程序中,我们可以做的就是定制特定管理作业,并查看以往作业的执行情况. 点击作业集合,然后点击创建计划程序作业,即可创建你自己的作业, 在接下来的页面选择快速创建,填写作业名称并选择区域,点击下一步 在作业页面,填写作业的相关信息,包括名称.类型.方法等 确认重复周期和启动时间即可. 接下来就可以看到作业集合的状态, 以及可以查看各作业的执行情况,通过筛选可以有选择的查看所要关注的作业

走进windows编程的世界-----窗口的注册及创建

1   窗口注册和创建 1.1WIN32 窗口程序创建步骤 1.WinMain入口函数的定义 2.WindowProc函数的定义 3.注册窗口类 RegisterClass.RegisterClassEX 4.创建窗口 CreateWindow.CreateWindowEx HWND CreateWindow( LPCTSTRlpClassName,//指向已注册的窗口类的名称的指针 LPCTSTRlpWindowName,//指向窗口名称的指针 DWORDdwStyle,//窗口的风格 int

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

在讲解聚合管道(Aggregation Pipeline)之前,我们先介绍一下 MongoDB 的聚合功能,聚合操作主要用于对数据的批量处理,往往将记录按条件分组以后,然后再进行一系列操作,例如,求最大值.最小值.平均值,求和等操作.聚合操作还能够对记录进行复杂的操作,主要用于数理统计和数据挖掘.在 MongoDB 中,聚合操作的输入是集合中的文档,输出可以是一个文档,也可以是多条文档. MongoDB 提供了非常强大的聚合操作,有三种方式: 聚合管道(Aggregation Pipeline)

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

1   Windows编程基础 1.1Win32应用程序基本类型 1)  控制台程序 不须要完好的windows窗体,能够使用DOS窗体方式显示 2)  Win32窗体程序 包括窗体的程序,能够通过窗体与程序进行交互 3)  Win32库程序 提供已有的代码,供其它程序使用 动态库(DLL):是在运行的时候能够载入的. 静态库(LIB):是在编译链接是使用的程序.成为当前程序的一部分. 1.2头文件和库 1.2.1头文件 主要的头文件windows.h包括了windows经常使用的定义等,其它,

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

上次因为时间的关系,所以把上一个专题遗留下的一个问题在本专题中和大家分享下,本专题主要介绍下如何实现UDP广播的程序,下面就直接介绍实现过程和代码以及运行的结果. 一.程序实现 UDP广播程序的实现代码: [csharp] view plain copy print? using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.W