菜单与图标
范例:menu_one
这是一个展示如何向你的窗口添加基本的菜单的小段落.一般你使用一个準好的菜单资源.它们在.rc文件中,编译后链接到你的.exe文件中.这部分对于不同的编译器很多差異,商业化的编译器拥有资源编辑器,你可用它来创建菜单,不过在这里我将向你展示.rc的內容这样你就可以手工修攺.我通常用一个.h文件同时被我的.rc文件和.c文件包含.这个件包含控件和菜单项目的标识等內容.
比如你可以从我们的那个simple_window的例子开始,再按照我们所说的把下面的代码加入进去.
首先是.h文件.一般称为"resource.h"
#define IDR_MYMENU 101
#define IDI_MYICON 201
#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002
不是很多代码,但我们的菜单将非常简单.这里的名字和值由你选择.现在我们来写.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", 0, GRAYED
END
END
IDI_MYICON ICON "menu_one.ico"
你可能需要把这个.rc文件添到你的项目或makefile中去(依你使用的工具而定).
你也要在你的源文件(.c)中#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, 16, 16, 0);
攺变一下,看有什么效果.
你的窗口将有一个File和一个Stuff菜单项目和它们下面的项目.假设你的.rc文件被正确编译并链接至你的程序中去.(又要参考编译器的说明了.)
窗口的左上角和任务栏现在将显示我们定义的自定义小图标.如果你按下Alt+Tab,应用程序列表中将显示图标的大版本.
我用了LoadIcon()来装入大图标因为这样简单,但它只会装入默认的分辨率为32*32的图标,所以为了装入小图标,我们要用LoadImage().注意图标文件和资源可以含有个图像,
这个例子中我提供了我要装入的两种大小的图像.
范例:menu_two
另外一种使用菜单资源的方式是在运行的时候创建一个(on
the fly).这需要多一点的技巧,但增添了灵活度而且在某些时候是必需的.
你也可以不是以资源存储的图标,你可以把你的图标存为一个单独的文件并在运行的时候装入.这样可以给你使用戶自己来选择图标的选项,使用我们将在后面的章节中讲的对话框或別的什么类似的东西来选择.
再从沒有添加.h的.rc文件的simple_window例子开始.现在我们要处理WM_CREATE消息并向窗口添加一个菜单.
#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002
这次把这两个标识放在你的.c文件的首部,就在你的#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, 32, 32, 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, 16, 16, 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()两次,装入了16*16和32*32两种大小的图标.我们这里不能用LoadIcon()因为它只能装入资源,而不能装入文件.我们把实例句柄参数写成了NULL因为我们不是从我们的模块装入资源,而且我们不用资源的标识而使用了我们要装入的图标文件名.最后我们传入了LR_LOADFROMFILE这个标志来指示我们需要这个函数将我们传入的字符串当作文件名而不是资源名字.
如果所有的调用都成功了我们就我在WM_SETICON的消息处理中把图标的句柄赋给我们的窗口,如果失败了则弹出一个对话框告诉我们出错了.
注意:如果我们要装入的图标文件不在程序现在的当前目錄则LoadImage()调用失败.如果你用VC++从IDE来运行程序的话,当前目錄就是你工程文件所在的目錄.如果你从Debug或Release目錄下用文件管理器或命令行运行的话,你就需要把你的图标文件拷进去样以使你的程序找到它.如果怎样还是出错,就在你的调用中指明图标的全路径, "c:\\path\\to\\icon.ico".
好的,现在我们有菜单了,我们让它做点事情.这很简单,我们就需要响应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, 32, 32, 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, 16, 16, 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(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
你可以看到我们把WM_COMMAND消息响应了,并且它还有自己的一个switch()在其中.这个switch()由wParm的低字来決定,因为可能WM_COMMAND消息包含了发送消息的菜单或控件的标识.
我们显然想要Exit菜单来关闭程序.所以在WM_COMMAND,ID_FILE_EXIT消息的处理程序中你可以使用下面的代码来做到这一点.
PostMessage(hwnd, WM_CLOSE, 0, 0);
你的WM_COMMAND处理代码现在看起来应该像样:
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_STUFF_GO:
break;
}
break;
至于ID_STUFF_GO要干什么就留给你来定义了.
程序文件的图标
你可能注意到了menu_one.exe看起来是我们给的那个当作资源的那个图标,而menu_two.exe则不是的,因为我们是装入的外部的文件.Windows资源管理器只是显示程序资源中的第一个图标(由ID排序),所以我们只有一个图标的时候,就显示那个了.如果我们要显示某个特定的图标,就把它加到资源中去并给定义个很小的标识...比如1.你根本就需要使用这个图标,你完全可以给你的窗口装入完全不同的图标.
|