菜单
菜单是 Windows 程序设计的基础部分,菜单的使用大大提高了用户体验,并带给用户比较相近的使用界面。菜单从菜单资源获取它们的名字,菜单资源中有一个可用项目的列表。Windows 菜单就是用户可用项目的列表,可以被单击打开一个子菜单或者使程序做某些事情。主菜单嵌入在菜单栏,子菜单从菜单栏下拉并且可以拥有其他子菜单。菜单栏有时被称作顶层菜单,子菜单有时被称作弹出菜单。菜单栏上的菜单按钮命名应该给出程序提供的命令的主要种类。Windows 还提供一种快捷菜单,它们与菜单栏无关,可以出现在屏幕的任意位置。有时这些菜单也叫做上下文菜单或者弹出菜单,它们可以通过右键点击出现。
列表框
列表框是包含用户可选择的项目列表的控件。列表框项目表现形式可以是文本字符串、位图或两者结合。如果列表框大小不够一次容纳所有列表项目,它会提供一个滚动条,用户在需要的时候可以滚动列表框遍历项目,选择自己想要的列表项。选择一个列表项会改变它的可视化外观,通常是改变文字和背景颜色为操作系统为选择项设定的颜色。当用户选择某一项或者从某一项不再选择,系统会向列表框父窗口发送一个通知消息。通知消息可以被处理来添加列表框的附加功能。Win32 API 提供两个列表框的常规风格:单选(默认风格)和多选。
菜单
菜单应该是一种最为公认与熟悉的计算机接口形式。给用户呈现一致的菜单界面是非常有用的。菜单栏(主菜单)在标题栏正下方显示,子菜单从主菜单下拉,其他子菜单再从这些下拉菜单呈现。菜单项可以调用一个命令或者打开一个子菜单。调用命令的菜单被称作命令项或者命令。主菜单的菜单项几乎总是打开一个子菜单,主菜单很少包含命令项,主菜单项打开子菜单是一个惯例。每个菜单都必须有一个 owner 窗口,这样当用户选择菜单或者从菜单选择一项的时候, Windows 可以发送消息给它的 owner 窗口。
创建菜单
创建菜单有两种方法:使用菜单创建函数或使用资源模板(使用 brcc32.exe 把 .RC 资源源文件编译成 .RES 文件 )。最常用(也最简单)的方法是使用资源模板,但是首先我们要演示如何使用菜单创建函数,这样你可以在运行期创建菜单。
一个最简单的 API 函数是 CreateMenu ,它没有参数,返回新菜单的句柄。
hMenu := CreateMenu;
新菜单完全是空的,直到你添加某些项目它是无法使用的。如果你使用空菜单句柄设置一个菜单或者在菜单创建函数中使用,不会发生任何事情。有几个向菜单添加菜单项的函数,InsertMenuItem( ) 是非常强大的,但是如果你不需要 InsertMenuItem 的额外的特征, AppendMenu( ) 和 InsertMenu( ) 也是很有用的。我将从 AppendMenu( ) 开始。
--------------------------------------------------------------------------------
AppendMenu( ) 函数
function AppendMenu(
hMenu: THandle; // 菜单的句柄
uFlags: Cardinal; // 菜单项标志
uIDNewItem: Cardinal; // 菜单项标识符或子菜单的句柄
lpNewItem: PChar // 菜单项文字
): Boolean; // 布尔型返回值
第一参数 hMenu 是你需要添加菜单项的菜单的句柄。第二个参数 uFlags 是这个菜单项的创建标志位,在下面会列出。它们有 MF_ 前缀表示菜单标志(Menu Flag),在很多菜单创建和修改函数中都会用到。
MF_BITMAP:使用位图作为菜单项,lpNewItem 参数包含作为菜单项显示的位图的句柄。
MF_CHECKED:在菜单项前面放置一个检查标志(勾选)。这个标志显示一个检查标志位图。
MF_UNCHECKED:菜单项不放置检查标志(默认)。这个标志将会在菜单项显示一个未勾选标志(如果指定则显示,默认是什么都不显示)。
MF_DISABLED:禁用菜单项使它不能被选择,但是该标志不使之变灰。
MF_ENABLED:使得菜单项可用,能被用户选择(默认),从变灰状态恢复。
MF_GRAYED:禁用菜单项使它不能被选择,使该项变灰呈现标准的不可用外观。
MF_MENUBREAK:放置该项到新一行(菜单栏)或新一列(下拉菜单、子菜单或快捷菜单)但不使用分栏标志。
MF_OWNERDRAW:指定该项为自定义绘制项。在菜单第一次显示前,它的 owner 窗口接收一个 WM_MEASUREITEM 消息来重新得到菜单项的宽度和高度。只要菜单项的外观需要更新,就会发送 WM_DRAWITEM 消息给它的 owner 窗口。
MF_POPUP:指定菜单项打开一个下拉菜单或子菜单。lpNewItem 参数指定下拉菜单或子菜单的句柄。这个标志用于添加菜单名字到菜单栏,或者添加菜单项用于打开下拉菜单、子菜单或快捷菜单。
MF_SEPARATOR:绘制水平分隔线。这个标志只用于下拉菜单、子菜单或快捷菜单。分隔线不能变灰、禁用或者高亮显示。lpNewItem 和 uIDNewItem 参数将被忽略。
MF_STRING:指定菜单项作为文本字符串, lpNewItem 参数指向 PChar。
由于菜单显示冲突的缘故,下列四组标志不能组合在一起使用:
1: MF_CHECKED 和 MF_UNCHECKED
2: MF_DISABLED、MF_ENABLED 和 MF_GRAYED
3: MF_BITMAP、MF_STRING 和 MF_OWNERDRAW
4: MF_MENUBARBREAK 和 MF_MENUBREAK
下一个参数为 uIDNewItem ,它指定新菜单项的 ID 编号,或者子菜单的句柄(如果参数 uFlags 被设置为 MF_POPUP 的话)。最后一个参数为 lpNewItem ,它在 windows.pas 单元被定义为 PChar 类型,通常包含菜单项的文字。不过,如果 uFlag 位被设置成为 MF_BITMAP ,那么这个参数就是需要显示的位图的句柄,你需要把 THandle 强制转化为 PChar 。如果 uFlag 位被设置成为 MF_OWNERDRAW ,那么这个参数被作为整型值,在菜单外观需要更新时发送 WM_MEASURE 或者 WM_DRAWITEM 消息,该值将在 lParam 的 itemData 传递,这次需要把整型强制转化为 PChar。
添加加速键(热键)
菜单的标准键盘接口可以通过向菜单项添加加速键(热键)来改善。加速键是菜单项文字中的下划线字符。当某个菜单被激活时,用户可以按下菜单项上对应的下划线字符来选择这个菜单项。用户可以通过按下 ALT 键来高亮显示菜单栏的第一项来激活菜单栏。子菜单在出现时处于激活状态。为了给菜单项创建一个加速键,只要在菜单项文本的任意字符前面加上“&”符号即可。例如,文本字符串“&Move”会使系统给字符“M”加上下划线并自动设置热键为“m”。当你使用 MF_STRING 标志时,你可以在你想设置热键的字符前面加上“&”字符。如果你想在文本中出现“&”符号,那么你需要把两个“&”符号放在一起使用,比如“&Bread && Butter”将显示“Bread & Butter”形式。例如:
为了添加文本菜单项,使用“101”作为 ID 编号,“New”作为文字,可以使用
AppendMenu(hMenu, MF_STRING, 101, '&New');
为了添加分隔线(最后两个参数没用),可以使用
AppendMenu(hMenu, MF_SEPARATOR, 0,nil);
为了添加子菜单项“Folders”,第三个参数为子菜单句柄,使用
AppendMenu(hMenu, MF_POPUP or MF_STRING, hSubMenu, 'Folders');
为了添加位图菜单项,使用“102”作为 ID 编号(必须将位图句柄强制转化为 PChar),可以使用
AppendMenu(hMenu, MF_BITMAP, 102, PChar(hBitmap));
--------------------------------------------------------------------------------
InsertMenu( ) 函数
InsertMenu( )函数与 AppendMenu( )函数很相近,用于在已经存在的菜单上的特定位置添加菜单项。InsertMenu( )函数有一个额外的从 0 起步的参数,用于插入菜单项的位置。它在 window.pas as 单元定义如下:
function InsertMenu(
hMenu: THandle; // 菜单的句柄
uPosition, Cardinal, // 菜单项位置
uFlags: Cardinal; // 菜单项标志
uIDNewItem: Cardinal; // 菜单项标志
lpNewItem: PChar // 菜单项文字
): Boolean; // 布尔型返回值
除了 uPosition 参数,它和 AppendMenu( )函数的参数是一致的。第二个参数 uPosition ,是基于 0 起步的你想放置菜单项的位置,如果你给出一个大于可用位置的数,它将在菜单的最后一行添加菜单项。其他参数和 AppendMenu( ) 函数一样,例如,为了在第四行添加文本菜单项,使用“203”作为 ID 编号,“Undo”作为文字,可以使用:
InsertMenu(hMenu, 3, MF_STRING, 203, '&Undo');
--------------------------------------------------------------------------------
InsertMenuItem( ) 函数
InsertMenuItem( ) 函数使用 TMenuItemInfo 记录来获取菜单项的创建信息。由于 TMenuItemInfo 记录有 11 个域,它比只有 5 个参数的 InsertMenu( ) 函数包含的信息更多,因此 InsertMenuItem( ) 比 AppendMenu 或 InsertMenu 函数拥有更多的创建选项。InsertMenuItem 在 windows.pas 单元定义如下:
function InsertMenuItem(hMenu: THandle; // 菜单的句柄
uItem: Cardinal; // ID 编号或菜单项位置
fByPosition: Boolean;