菜单及其他资源
见钱眼开 于
2005-5-21
图标、光标、菜单和对话框等都是Windows的资源类型。资源就是数据,它们被存储在程序的.EXE文件中,但并非驻留在程序的数据区域。因此资源无法通过程序源代码中定义的变量直接访问,必须通过Windows提供的API函数(如LoadCursor和LoadIcon等)直接或间接加载到内存使用。
将一个图标文件最终包含到.EXE文件中,经过两个步骤:1。创建一个相应的资源描述文件(.RC),通过资源编译器(比如RC.EXE)编译成资源文件(.RES);2。资源文件(.RES)在链接过程中和.OBJ、.LIB等文件一起包含到.EXE文件。通常我们只需将图标文件和资源描述文件(.RC)添加到项目工程中即可,开发环境将自动完成后续工作。
资源描述文件是文本文件,包含对引用资源的文本形式描述。在该文件中,每一个资源都唯一对应一个ID。
下面是IconDemo.RC文件中图标资源的引用描述:
#include “Resource.h”
IDI_ICON ICON DISCARDABLE “IconDemo.ico”
IDI_ICON是图标标志ID;ICON表示资源类型为图标;DISCARDABLE是一个关键字,表示必要时Windows可以从内存中丢弃图标,以获得额外空间。之后无需程序特定操作,Windows能够重新加载该图标。
程序可通过以下方式获取图标句柄:
hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON));
第一个参数指出资源所在模块的实例句柄。如果为NULL,调用Windows预定义的图标。
MAKEINTRESOURCE是个宏,具体定义如下:
#define MAKEINTRESOURCE(i) (LPTSTR)((DWORD)((WORD)(i)))
它的作用是把一个数字转换为一个高16位为0的字符串指针。LoadIcon函数通过判断高16位是否为0,知道该参数赋予的是标识ID还是字符串。因此,图标资源标识符必须是16位数值。
如果Resource.h文件中并未定义IDI_ICON,那么还可以把它作为字符串来调用:
hIcon = LoadIcon(hInstance, TEXT(“IDI_ICON”));
动态设置窗口图标:
SetClassLong(hWnd,GCL_HICON,LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON)));
返回窗口图标:
hIcon = (HICON)GetClassLong(hWnd,GCL_HICON);
LoadIcon返回句柄后不需要负责销毁。比起LoadIcon,Windows文档更推荐使用LoadImage。
光标和图标的使用方法很相似。自定义光标一般为单色,大小为32*32象素。
动态设置窗口光标:
SetClassLong(hWnd,GCL_HCURSOR,LoadCursor(hInstance,”IDC_CURSOR”));
SetCursor(hCursor);
SetCursor必须在每次接收WM_MOUSEMOVE消息被调用,否则Windows将替换为窗口类预定义的光标。
使用字符串资源是为了更好的支持多语言。
在资源描述中,字符串显示在一个多行的语句中,如:
STRINGTABLE DISCARDABLE
BEGIN
IDS_STRING1,”character string 1”
IDS_STRING2,”character string 2”
END
每个字符串占一行,最多4097格字符。以/t作为制表符,/n用于行尾。
将字符串载入到数据缓冲区中:
LoadString(hInstance,IDS_STRING1,szBuffer,iMaxLen);
我们还可以将自定义的资源链接到目标.EXE中。例如一个BinData.bin文件,可以在资源描述文件中这样定义:
IDR_BINTYPE BINTYPE BinData.bin
“BINTYPE”是自定义的资源类型。IDR_BINTYPE是资源标识符。
成功编译后,我们首先调用FindResource函数返回程序模块中该类型资源的方位:
HRSRC hrBin = FindResource(hInstance,
TEXT(“BINTYPE”),
MAKEINTRESOURCE(IDR_BINTYPE));
再调用LoadResource函数载入资源(
注意:该函数返回值为
HGLOBAL
类型是出于兼容性考虑,该值并非是一个全局内存句柄,不要将它传给
GlobalLock
或
GlobalFree
函数):
HGLOBAL hgBin = LoadResource(hInstance,
hrBin);
最后调用LockResource函数锁定该资源返回数据指针:
pBinData = LocakResource(hgBin);
调用FreeResouce函数释放该资源,不过不调用也没有关系,程序终止时会自动释放。
菜单是一系列可用的选项,每个可选的菜单项被赋予唯一的ID。用户选择一个菜单项时,Windows会给程序发送包含该ID的WM_COMMAND消息。
在窗口上紧接在标题栏下的菜单条,被称为“主菜单”或“顶层菜单”。经过顶层菜单项弹出的菜单是“下拉菜单”或“子菜单”。
子菜单的各项可以被选中;顶层菜单的各项不可被选中。
顶层菜单和子菜单可以被“启用”/“禁用”、“灰化”。
顶层菜单和子菜单都拥有各自的菜单句柄。
一个菜单项具有三个基本特性:标题/图标、ID、属性。
菜单项标题中键入“&“字符,随后的字符在显示菜单时加下划线。按下”Alt”键加该字符可选中该菜单项。
除了在定义窗口类和调用CreateWindow函数时可以指定菜单,还可以动态修改菜单:
hMenu = LoadMenu(hInstance,MAKEINTRESOURCE(ID_MENU));
SetMenu(hWnd,hMenu);
销毁窗口时,所有未与窗口关联的菜单应该调用DestroyMenu显示清除。
WM_INITMENU
消息在菜单即将被激活而且首次被激活时触发。wParam参数包含被激活菜单句柄。
WM_MENUSELECT
消息在一个菜单项被选中时触发。如果该菜单项是一个命令项,wParam参数低16位包含该菜单项ID,lParam参数包含属主菜单句柄;如果是弹出项,则包含弹出或子菜单在主菜单中的索引,而lParam参数包含主菜单句柄(调用GetSubMenu返回弹出或子菜单句柄)。wParam参数高16位包含该菜单项标志,由以下标志构成:
MF_BITMAP
显示位图
MF_CHECKED
被选中
MF_DISABLED
禁用
MF_GRAYED
灰化
MF_HILITE
高亮
MF_MOUSESELECT
鼠标选中
MF_OWNERDRAW
自绘菜单项
MF_POPUP
打开弹出或子菜单
MF_SYSMENU
系统菜单项
WM_INITMENUPOPUP
消息在弹出或子菜单即将被激活时触发。wParam参数包含被激活菜单句柄。lParam参数低16位包含被激活菜单索引,高16位在系统菜单时为1,否则为0。
WM_COMMAND消息在选中一个菜单项时触发,该消息也可以由子窗口控件产生。不过有一点不同,菜单项产生的消息
lParam值为0,而子窗口控件为其句柄。
WM_SYSCOMMAND消息在选中一个系统菜单的菜单项时触发。该消息
wParam
参数低4位系统内部使用,要想获得正确ID,应先和0xfff0进行与运算。系统菜单中自定义菜单项ID为避免和预定义值冲突,务必小于0xf000。
最后一个与菜单相关的消息是WM_MENUCHAR。当一个下拉菜单被激活时,按下一个与菜单项热键不匹配的键,则该消息被发送给菜单属主窗口。
菜单可通过定义资源描述文件实现;也可以通过调用API函数实现。
HMENU hMainMenu = CreateMenu(); //
创建一个主菜单
HMENU hPopupMenu = CreateMenu(); //
创建一个弹出菜单
AppendMenu(hPopupMenu,MF_STRING,ID_MENUITEM,”&New”); //
在弹出菜单插入一个 标题为“New”的命令菜单项
AppendMenu(hMainMenu,MF_POPUP,hPopupMenu,”&File”); //在主菜单插入一个标题为“
File”的弹出菜单项
TrackPopupMenu(hPopupMenu,TPM_RIGHTBUTTON,x,y,0,hWnd,NULL); //在鼠标右键按下的屏幕坐标方位弹出一个菜单
使用WS_SYSMENU风格创建的主窗口在其标题栏左侧有一个系统菜单。
HMENU hMenu = GetSystemMenu(hWnd,FALSE); //
返回主窗口系统菜单
AppendMenu(hMenu,MF_SEPARATOR,0,NULL); //
插入一个分隔符
AppendMenu(hMenu,MF_STRING,IDM_SYS_ABOUT,”About”); //
插入一个菜单项
另外,其他编辑菜单的API函数还有:
DeleteMenu
删除菜单中一个菜单项
InsertMenu
在菜单中某个位置插入一个新项
ModifyMenu
修改现有菜单项
RemoveMenu
从菜单中删除某一项
DeleteMenu
和RemoveMenu的区别是,当菜单项是一个弹出项时,DeleteMenu删除该菜单项和对应弹出菜单;而RemoveMenu仅仅删除该菜单项,不处理弹出菜单。
DrawMenu(hWnd); //
重新绘制主窗口顶层菜单
hPopupMenu = GetSubMenu(hMainMenu,iIndex); //
返回一个弹出菜单
iCount = GetMenuItemCount(hMenu); //
返回菜单项数量
…
快捷键
(Accelerator Key)是产生WM_COMMAND或WM_SYSCOMMAND消息的键组合。
快捷键通常被用于加快菜单项操作。不过一个快捷键并非必须对应一个菜单命令。
通常Windows将键盘消息发送给目前活动窗口的窗口过程,而按下快捷键后的键盘消息经
TranslateAccelerator函数转换后可以直接发送给指定窗口过程。这样的话,即使当前输入焦点在子窗口,主窗口菜单仍旧可以及时响应。
任何虚拟键或者字符键连同
Shift键、Ctrl键或Alt键都可以定义快捷键。不过应该避免包含Tab、Enter、Esc和Spacebar键,防止和系统快捷键冲突。
以下是资源描述文件中包含的快捷键定义描述:
IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE
BEGIN
"N", IDA_NEW, VIRTKEY, ALT, NOINVERT
"O", IDA_OPEN, ASCII, ALT, NOINVERT
VK_ESCAPE, IDA_EXIT, VIRTKEY, CONTROL, NOINVERT
END
和其他资源的载入方法很相似,使用
LoadAccelerators载入“快捷键”表:
HANDLE hAccel = LoadAccelerators(hInstance,TEXT ("MyAccelerators"));
使用TranslateAccelerator实现
键盘消息转换。如果键盘消息被成功转换为
WM_COMMAND或WM_SYSCOMMAND消息,那么TranslateAccelerator返回真,TranslateMessage无需再处理该键盘消息。
while(GetMessage(&msg,NULL,0,0))
{
if(!TranslateAccelerator(hwnd,hAccel,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
如果快捷键与一个菜单项对应,那么窗口过程还会收到
WM_INITMENU、WM_INITMENUPOPUP和WM_MENUSELECT消息,就好像选中了菜单选项一样。
如果快捷键与一个禁用或者无效化的菜单项相对应,那么,
TranslateAccelerator函数就不会向窗口过程发送WM_COMMAND或WM_SYSCOMMAND消息。
如果活动窗口已经被最小化,那么
TranslateAccelerator将向窗口过程发送WM_SYSCOMMAND消息,而不是WM_COMMAND消息。TranslateAccelerator也会为没有对应任何菜单项的快捷键,向窗口过程发送WM_COMMAND消息。