菜单和按钮

例子:菜单1

  本小节仅仅向你展示如果向你的窗口中加入一个基本的菜单,通常你会用到一个提前制作好的菜单资源,这会是一份.rc文件并且会被编译链接进你的.exe可执行程序中。这是具体的流程做法,而商业编译器将会有一个资源编辑器,你可以通过这个编辑器来创建菜单,但是在这个例子中我会向你展示如何用.rc文件的手动写法。通常我会配合使用一个头文件,在资源文件和源文件中我们需要引入这个头文件,这个头文件中包含了控制和菜单选项等的标识符。

  在本小节的栗子中,你可以按照指示在simple_window代码的基础上做改造。

  首先头文件名通常会叫“resource.h”

 #define IDR_MYMENU 101
#define IDI_MYICON 201 #define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002

  没有更多的东西,我们的菜单很简单,标识符名和ID号由你自己选择,现在我们来写.rc文件的代码。

 #include "resource.h"

 IDR_MYMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END POPUP "&Stuff"
BEGIN
MENUITEM "&Go", ID_STUFF_GO
MENUITEM "G&o somewhere else", , GRAYED
END
END IDI_MYICON ICON "menu_one.ico"

  根据你使用的工具,将.rc文件加入你的项目或生成文件中。

  你也要在你的源文件中使用#include “resource.h”引入资源头文件。

  在你的窗口中附加菜单和图标最简单的方式就是在注册窗口类是指定好,就像下面:

     wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MYMENU);

     wc.hIcon  = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON));
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, , , );

  在原来的代码上面改看看会发生什么,你的窗口现在应该有一个File和Stuff的菜单,这种是你的资源文件被成功编译并链接至程序中的情况,如果发生错误请检查。

  窗口右上角的图标和任务栏上的指定的小图标现在应该能够显示出来了,如果你使用Alt-Tab的组合键你会看到应用列表的大图标。

  我曾经用loadIcon()来加载小图标因为它更简单,但是这个方法加载的图标的默认大小是32x32,为了加载小图标我们需要使用LoadImage()方法。请注意图标文件和资源可以包含多个图像,在这种情况下我提供了加载资源图标的两个尺寸。

例子:菜单2

  另一种使用菜单资源的选择是动态创建(即你的程序运行的时候),这是一种更加聪明的工作方式,有时为了增加程序的灵活性是有必要的。

  你也可以使用没有存储在资源中的图标,而选择在运行的时候单独加载存储图标的文件,这也给了你一种选择,即允许用户在通用的对话框中选择图标,在后面我们会讨论到,或者其他受动态创建影响的东西。

  让我们再一次从没有资源文件和头文件的simple_windows的代码开始,现在我们将会处理WM_CREATE消息然后向我们的窗口中增加菜单。

 #define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002

  现在把这两个id放在你的源文件的头部,就在#include的下面,现在我们把下面的代码加入到WM_CREATE消息的句柄中:

 case WM_CREATE:
{
HMENU hMenu, hSubMenu;
HICON hIcon, hIconSm; hMenu = CreateMenu(); hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff"); SetMenu(hwnd, hMenu); hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, , , LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR); hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, , , LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);
}
break;

  这个跟第一个例子的效果一样,创建了一个菜单附加到我们的窗口中,当程序终止时,指定附加到窗口的菜单会自动被移除。所以我们不要担心如何除掉它,如果我们想这样做,可以使用GetMenu()和DestroyMenu()来达到目的。

  加载图标的代码相当简单,这里我们调用了两次LoadImage(),分别加载16x16和32x32尺寸的图标,我们不能使用LoadIcon()因为它只能加载资源文件,而不是图标文件本身。我们为实例句柄参数指定了一个NULL的参数因为我们不是从一个模块中加载资源的,我们通过传递我们想要加载的图标文件名而不是资源ID。最后我们通过LR_LOADFROMFILE标识表明我们想要函数我们传递进入的字符串当做文件名而不是一个资源名。

  如果图标加载成功,我们会将图标通过WM_SETICON分配给窗口;如果失败了,会弹出一个告诉我们加载出错的对话框。

  注意:如果突变文件不在我们程序的当前工作目录下那么调用LoadImage()会失败。如果你使用VC++并且从IDE运行程序,当前工作目录会是项目文件所在的目录中的一个。如果你使用浏览器的调试或发布目录或者命令行运行程序,那么你需要将图标文件复制进相应的工作目录中以便程序可以找到它;如果都不行,尝试用绝对路径"C:\\Path\\To\\Icon.ico"

  OK,现在我们有自己的菜单了,我们需要让它做些东西,这相当简单,我们需要做的就是处理WM_COMMAND消息。同样我们需要检查得到了哪些命令然后根据它做处理,现在我们的WndProc()看起来影响像下面这样了。

 LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_CREATE:
{
HMENU hMenu, hSubMenu; hMenu = CreateMenu(); hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File"); hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff"); SetMenu(hwnd, hMenu); hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, , , LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR); hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, , , LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_EXIT: break;
case ID_STUFF_GO: break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage();
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return ;
}

  正如你看到了我们得到了所有设置的WM_COMMAND,甚至它有自己的switch,这个switch的值是根据wParam的低字来判断的,wParam对于WM_COMMAND来说包含了发送消息的控制或菜单id。

  很明显,我们想要退出菜单选项实现关闭程序的功能,所以在WM_COMMAND的ID_FILE_EXIT句柄下你可以使用以下的代码来达到目的。

 PostMessage(hwnd, WM_CLOSE, , );

  你的WM_COMMAND消息处理程序看起来应该像下面这样子:

     case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, , );
break;
case ID_STUFF_GO: break;
}
  break;

  我留给你实现另一个ID_STUFF_GO菜单命令做点什么。

程序文件图标

  你可能注意到了menu_one.exe文件现在像我们用资源文件引入图标的方式显示了图标,然而menu_two.exe并没有这样做因为我们引入了一个外部文件。窗口浏览器在第一个程序中通过资源文件非常简单的显示了一个图标,因为只需要一个图标;如果我们想通过程序显示一个确切地图标,那么就可以采取第一种方式,即简单地使用资源ID;如果你想要根据你的选择显示图标那么就采取第二种方式吧。

  PS.由于本人英文水平所限,只能翻译到这个程度了,有纰漏还望多多指出,附上本篇翻译的英文原版教程地址:http://www.winprog.org/tutorial/menus.html

Win32编程API 基础篇 -- 6.菜单和图标的更多相关文章

  1. Win32编程API 基础篇 -- 1.入门指南 根据英文教程翻译

    入门指南 本教程是关于什么的 本教程的目的是向你介绍使用win32 API编写程序的基础知识(和通用的写法).使用的语言是C,但大多数C++编译器也能成功编译,事实上,教程中的绝大多数内容都适用于任何 ...

  2. Win32编程API 基础篇 -- 4.消息循环

    消息循环 理解消息循环 为了编写任何即使是最简单的程序,了解windows程序的消息循环和整个消息发送结构是非常有必要的.既然我们已经尝试了一点消息处理的东西,我们应该对整个程序有更深入的理解,如果你 ...

  3. Win32编程API 基础篇 -- 5.使用资源

    使用资源 你可能想参考教程结尾的附近,为了获得跟VC++和BC++资源相关的信息. 在我们讲得更加深入之前,我将大致讲解一下资源的主题,这样在每个小节中我就不必再去重讲一遍了.在这一小节中,你不需要编 ...

  4. Win32编程API 基础篇 -- 3.消息处理 根据英文教程翻译

    消息处理 例子:窗口点击 好的,现在我们已经得到一个窗口了,但我们什么也做不了除了DefWindowProc()允许窗口大小被调整,最大最小化等...这不是很激动人心啊 在接下来的一小节中我将向你展示 ...

  5. Win32编程API 基础篇 -- 2.一个简单的窗口 根据英文教程翻译

    一个简单的窗口 例子:简单的窗口 有时人们在IRC提问,”我应该怎样制作一个窗口”...嗯,这恐怕不是完全这么简单好回答!其实这并不难一旦你明白你在做什么,但在你得到一个可展示的窗口之前还有一些事情需 ...

  6. 我拖拖拖--H5拖放API基础篇

    不要搞错,本文不是讲如何拖地的.看过<javascript精粹>朋友应该知道,他实现拖放的过程比较复杂,现在时代不同了,我们用H5的新的拖放API就能非常方便的实现拖放效果了.最近在园子见 ...

  7. ASP.NET Web API 基础篇1

    ASP.NET Web API 直到我膝盖中了一箭[1]基础篇 无题 蓦然回首,那些年,我竟然一直很二. 小时候,读武侠小说的时候,看到那些猪脚,常常会产生一种代入感,幻想自己也会遭遇某种奇遇,遇到悬 ...

  8. (转)Android高性能编程(1)--基础篇

    关于专题     本专题将深入研究Android的高性能编程方面,其中涉及到的内容会有Android内存优化,算法优化,Android的界面优化,Android指令级优化,以及Android应用内存占 ...

  9. 【TCP/IP】之Java socket编程API基础

    Socket是Java网络编程的基础,深入学习socket对于了解tcp/ip网络通信协议很有帮助, 此文讲解Socket的基础编程.Socket用法:①.主要用在进程间,网络间通信. 文章目录如下: ...

随机推荐

  1. vue-router+webpack线上部署时单页项目路由,刷新页面出现404问题

    使用vue项目,线上部署的时候,访问首页以及通过路由打开二级页面没有问题,但是一刷新就出现404现象 因为刷新页面时访问的资源在服务端找不到,因为vue-router设置的路由不是真实存在的路径. 解 ...

  2. restful api 错误

    简介 随着移动开发和前端开发的崛起,越来越多的 Web 后端应用都倾向于实现 Restful API.Restful API 是一个简单易用的前后端分离方案,它只需要对客户端请求进行处理,然后返回结果 ...

  3. video 能播放声音不能播放视频,黑屏

    与视频编码格式有关,mp4的视频编码有三种:MPEG4(DivX),MPEG4(Xvid),AVC(H264). 浏览器播放视频的支持有限,MP4格式的视频只支持h.264的视频: 视频编码: AVC ...

  4. 常用点击事件(鼠标、光标、键盘、body)

    常用事件: 鼠标: onclick(单击) ondblclick(双击) oncontextmenu(右击) onmouseover onmouseout 光标: onfocus onblur 键盘: ...

  5. Xml学习笔记(3)利用递归解析Xml文档添加到TreeView中

    利用递归解析Xml文档添加到TreeView中 private void Form1_Load(object sender, EventArgs e) { XmlDocument doc = new ...

  6. H5活动的一些事

    ISUX团队镇楼:https://isux.tencent.com/nine-question-of-swipe-html5-page.html IE6.7.8支持html5新元素 : http:// ...

  7. elasticsearch 查询优化

    首先对不必要的字段不做分词也就是不做索引,禁止内存交换 1.shard 一个Shard就是一个Lucene实例,是一个完整的搜索引擎. 分片数过多会导致检索时打开比较多的文件,多台服务器之间通讯成本加 ...

  8. Position属性四个值:static、fixed、absolute和relative的区别

    1.static(静态定位):默认值.没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明). 2.relative(相对定位):生成相对 ...

  9. CentOS6.6从头到尾部署nginx与tomcat多实例

    前提条件: 1.需要一个全新的centos系统(本文中用到是centos6.6) 2.vmware虚拟机 3.vmware下安装centos系统,以NAT方式与宿主机相连 4.在centos系统中pi ...

  10. TCP/IP和UDP的比较

    TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间,无法了解数据未达终点的状态.因此有必要增强网络层提供服务的服务质量. 2.引入传输层的原因 面向 ...