虽然存取菜单资源的最常用方法是在窗口类别中指定菜单,您也可以使用其它方法。Windows应用程序可以使用LoadMenu函数将菜单资源加载内存中,如同LoadIcon和LoadCursor函数一样。LoadMenu传回一个菜单句柄。如果您在资源描述档中为菜单使用了名称,叙述如下:
hMenu = LoadMenu (hInstance, TEXT ("MyMenu")) ;
如果使用了数值,那么LoadMenu呼叫采用如下的形式:
hMenu = LoadMenu (hInstance, MAKEINTRESOURCE (ID_MENU)) ;
您也可以在窗口类别中指定NULL菜单,并且在CreateWindow呼叫中也指定NULL菜单,然后在窗口被建立后再给窗口指定一个菜单:
SetMenu (hwnd, hMenu) ;
菜单和消息
当使用者选择一个菜单项时,Windows通常向窗口消息处理程序发送几个不同的消息。在大多数情况下, 您的程序可以忽略大部分消息,只需把它们传递给DefWindowProc即可。WM_INITMENU就是这一类的消息,它具有下列参数:
wParam: 主菜单句柄
lParam: 0
wParam值是您的主菜单句柄,即使使用者选择的是系统菜单中的项目。Windows程序通常忽略WM_INITMENU消息。尽管在选中该项之前的消息已经给程序提供了修改菜单的机会,但是我们觉得此刻改变顶层菜单是会扰乱使用者的。
程序也会接收到WM_MENUSELECT消息。随着使用者在菜单项中移动光标或者鼠标,程序会收到许多WM_MENUSELECT消息。这对实作那些包含对菜单项的文字描述的状态列是很有帮助的。WM_MENUSELECT的参数如下所示:
LOWORD (wParam):被选中项目:菜单ID或者弹出式菜单句柄
HIWORD (wParam):选择旗标
lParam: 包含被选中项目的菜单句柄
WM_MENUSELECT是一个菜单追踪消息,wParam的值告诉您目前选择的是菜单中的哪一项(加高亮度显示的那个),wParam的高字组中的「选择旗标」可以是下列这些旗标的组合:MF_GRAYED、MF_DISABLED、MF_CHECKED、MF_BITMAP、MF_POPUP、MF_HELP、MF_SYSMENU和MF_MOUSESELECT。如果您需要根据对菜单项的选择来改变窗口显示区域的内容,那么您可以使用WM_MENUSELECT消息。许多程序把该消息发送给DefWindowProc。
当Windows准备显示一个弹出式菜单时,它给窗口消息处理程序发送一个WM_INITMENUPOPUP消息,参数如下:
wParam: 弹出式菜单句柄
LOWORD (lParam):弹出式菜单索引
HIWORD (lParam): 系统菜单为1,其它为0
为此,首先要得到菜单句柄:
hMenu = GetMenu (hwnd) ;
CheckMenuItem函数用来取消目前被选中的项目:
CheckMenuItem (hMenu, iSelection, MF_UNCHECKED) ;
iSelection的值被设定为wParam的值,新的背景颜色被选中:
iSelection = wParam ; CheckMenuItem (hMenu, iSelection, MF_CHECKED) ; MenuItem (hMenu, IDM_TIMER_START, MF_GRAYED) ;改变菜单
我们已经看到了如何使用AppendMenu函数为程序定义菜单以及将菜单项加入到系统菜单中。在Windows 3.0之前,您不得不被迫使用ChangeMenu函数来完成这种工作。ChangeMenu函数有很多功能,至少在当时,整个Windows中它是最复杂的函数之一。现在,许多函数都比ChangeMenu函数还要复杂,并且ChangeMenu的功能被分解为五个新的函数:
- AppendMenu在菜单尾部添加一个新的菜单项目
- DeleteMenu删除菜单中一个现有的菜单项并清除该项目
- InsertMenu在菜单中插入一个新项目
- ModifyMenu修改一个现有的菜单项目
- RemoveMenu从菜单中移走某一项目
您可以使用下列命令来获得弹出式菜单的句柄:
hMenuPopup = GetSubMenu (hMenu, iPosition) ;
其中iPosition是hMenu指示的顶层菜单中弹出式菜单项的索引(开始为0)。然后您可以在其它函数中使用弹出式菜单句柄(例如在AppendMenu函数中)。
您可以使用下列命令获得顶层菜单或者弹出式菜单中目前的项数:
iCount = GetMenuItemCount (hMenu) ;
您可以取得弹出式菜单项的菜单ID:
id = GetMenuItemID (hMenuPopup, iPosition) ;
其中iPosition是菜单项在弹出式菜单中的位置(以0开始)。
在MENUDEMO中您已经看到如何选中、或者取消选中弹出式菜单中的某一项:
CheckMenuItem (hMenu, id, iCheck) ;
在MENUDEMO中,hMenu是顶层菜单的句柄,id是菜单ID,而iCheck的值是MF_CHECKED或MF_UNCHECKED。如果hMenu是弹出式菜单句柄,那么参数id是位置索引而不是菜单ID。如果使用索引会更方便的话,那么您可以在第三个参数中包含MF_BYPOSITION,例如:
CheckMenuItem (hMenu, iPosition, MF_CHECKED | MF_BYPOSITION) ;
除了第三个参数是MF_ENABLED、MF_DISABLED或MF_GRAYED外,EnableMenuItem函数与CheckMenuItem函数所完成的工作类似。如果您在具有弹出式菜单的顶层菜单项上使用EnableMenuItem,那么必须在第三个参数中使用MF_BYPOSITION标识符,因为菜单项没有菜单ID。我们将在本章后面所示的POPPAD2程序中看到EnableMenuItem的一个例子。 HiliteMenuItem也类似于CheckMenuItem和EnableMenuItem,但是它使用的是MF_HILITE和MF_UNHILITE。当您在菜单项之间移动时,Windows使用反白显示方式加亮显示菜单项。您通常不需要使用HiliteMenuItem。
您还需要对您的菜单做些什么呢?还记得我们在菜单中使用了哪些字符串吗?您可以透过下面的呼叫来回顾一下:
iCharCount = GetMenuString (hMenu, id, pString, iMaxCount, iFlag) ;
iFlag可以是MF_BYCOMMAND(其中id是菜单ID),也可以是MF_BYPOSITION(其中的id是位置索引)。函数将字符串的iMaxCount个字节复制到pString中,并传回复制的字节数。
或许您也想知道菜单项目前的属性是什么:
iFlags = GetMenuState (hMenu, id, iFlag) ;
同样地,iFlag可以是MF_BYCOMMAND或MF_BYPOSITION。传回值iFlags是目前所有属性的组合,您可以通过对MF_DISABLED、MF_GRAYED、MF_CHECKED、MF_MENUBREAK、MF_MENUBARBREAK和MF_SEPARATOR标识符的检测来决定目前的属性。
也许现在您对菜单有了一些了解。这时您可能想知道,如果您不再需要菜单时又应该如何处理。您可以使用下面的命令来清除菜单:
DestroyMenu (hMenu) ;