菜单和按钮

例子:菜单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. PostgreSQL逻辑复制之pglogical篇

    PostgreSQL逻辑复制之slony篇 一.pglogical介绍 pglogical 是 PostgreSQL 的拓展模块, 为 PostgreSQL 数据库提供了逻辑流复制发布和订阅的功能. ...

  2. 一张图带你了解-常见面试之JUC包详解

    面试时经常问到JUC包下的类及特性,现在用一张图总结下

  3. 1.2Hello, World!的大小

    描述 还记得在上一章里,我们曾经输出过的“Hello, World!”吗? 它虽然不是本章所涉及的基本数据类型的数据,但我们同样可以用sizeof函数获得它所占用的空间大小. 请编程求出它的大小,看看 ...

  4. 微信公众号 sign类

    微信公众号 Sign import java.util.UUID; import java.util.Map; import java.util.HashMap; import java.util.F ...

  5. [NOI2003]Editor

    Description 很久很久以前,DOS3.x的程序员们开始对 EDLINEDLIN 感到厌倦. 于是,人们开始纷纷改用自己写的文本编辑器?? 多年之后,出于偶然的机会,小明找到了当时的一个编辑软 ...

  6. java项目怎么打jar包(项目中包含第三方jar包)

    1,右键选中项目,点击Export导出,找到java,选择第三个Runnable JAR file, 2,在Launch configuration中,填入程序的入口(有main方法的类),   然后 ...

  7. 提取header头进行模块化处理

    在进行爬取网上东西的时候一般网站都做了UA的过滤,解决办法就是在代码中加入. 所以才有了本篇提取header头信息单独写成一个模块或者说是函数/类的想法,直接上示例 1.把UA头信息在浏览器中复制出来 ...

  8. taskctl命令行类(sh、exe、python新增scp)插件升级扩展

    转载自: http://www.taskctl.com/forum/detail_129.html 上次写了一个帖子 TASKCTL中不使用代理,通过ssh免密连接执行远程脚本配置(SSH插件扩展)h ...

  9. 在iframe内页面完全加载完后,关闭父页面生成的div遮罩层

    遮罩层div为iframe父页面生成,需在iframe内页面完全加载完后,关闭遮罩层 alertMsgClose() :函数为关闭遮罩层函数 此段代码在iframe页面内: <script> ...

  10. solr 6.5.1 linux 环境安装

    前言 最近在研究搜索引擎,准备搭建一套属于自己的搜索APP,用于搜索的数据我已通过scrapy抓到本地了,现在需要一个搜索引擎来跑这些数据.于是选择了基于Lucene的solr来为我做搜索引擎的工作. ...