关于菜单

菜单是一个项列表,这些项指定 (应用程序的子菜单) 的选项或选项组。 单击菜单项会打开子菜单或使应用程序执行命令。 本部分提供有关以下主题的信息:

菜单按层次结构排列。 层次结构的顶层是 菜单栏;它包含 菜单列表,而菜单又可以包含 子菜单。 菜单栏有时称为 顶级菜单,菜单和子菜单也称为 弹出菜单

菜单项可以执行命令或打开子菜单。 执行命令的项称为 命令项命令

菜单栏上的项几乎总是打开一个菜单。 菜单栏很少包含命令项。 从菜单栏打开的菜单从菜单栏下拉,有时称为 下拉菜单。 显示下拉菜单时,它将附加到菜单栏。 菜单栏上打开下拉菜单的菜单项也称为 菜单名称

菜单栏上的菜单名称表示应用程序提供的命令main类别。 从菜单栏中选择菜单名称通常会打开一个菜单,其菜单项对应于类别中的命令。 例如,菜单栏可能包含“文件”菜单名称,当用户单击该名称时,该名称会激活包含菜单项(如“新建”、“打开”和“保存”)的菜单。 若要获取有关菜单栏的信息,请调用 GetMenuBarInfo

只有重叠或弹出窗口才能包含菜单栏:子窗口不能包含子窗口。 如果窗口具有标题栏,系统会将菜单栏放在它正下方。 菜单栏始终可见。 但是,在用户选择激活子菜单项之前,子菜单不可见。 有关重叠窗口和弹出窗口的详细信息,请参阅 窗口类型

每个菜单都必须有一个所有者窗口。 当用户选择菜单或从菜单中选择项时,系统会将消息发送到菜单的所有者窗口。

本部分讨论以下主题。

快捷菜单

系统还提供 快捷菜单。 快捷菜单未附加到菜单栏;它可以出现在屏幕上的任意位置。 应用程序通常将快捷菜单与窗口的一部分(例如工作区)或特定对象(如图标)相关联。 因此,这些菜单也称为 上下文菜单

快捷菜单一直处于隐藏状态,直到用户激活它,通常通过右键单击所选内容、工具栏或任务栏按钮。 菜单通常显示在插入点或鼠标光标的位置。

窗口菜单

“窗口”菜单 (也称为“系统”菜单或“控制”菜单) 是几乎完全由操作系统定义和管理的弹出菜单。 用户可以通过单击标题栏上的应用程序图标或右键单击标题栏上的任意位置来打开窗口菜单。

窗口 ”菜单提供一组标准菜单项,用户可以选择这些菜单项来更改窗口的大小或位置,或者关闭应用程序。 可以添加、删除和修改窗口菜单上的项,但大多数应用程序只使用标准的菜单项集。 重叠窗口、弹出窗口或子窗口可以有一个窗口菜单。 重叠或弹出窗口不包含窗口菜单的情况并不常见。

当用户从“ 窗口 ”菜单中选择命令时,系统会将 WM_SYSCOMMAND 消息发送到菜单的所有者窗口。 在大多数应用程序中,窗口过程不会处理窗口菜单中的消息。 相反,它只是将消息传递到 DefWindowProc 函数,以便系统默认处理消息。 如果应用程序将命令添加到窗口菜单,则窗口过程必须处理该命令。

应用程序可以使用 GetSystemMenu 函数创建要修改的默认窗口菜单的副本。 任何不使用 GetSystemMenu 函数创建其自己的窗口菜单副本的窗口都会收到标准窗口菜单。

帮助标识符

与每个菜单栏、菜单、子菜单和快捷菜单关联的是帮助标识符。 如果用户在菜单处于活动状态时按 F1 键,则此值将作为 WM_HELP 消息的一部分发送到所有者窗口。

键盘访问菜单

系统为菜单提供标准键盘界面。 可以通过为菜单项提供助记访问键和快捷方式 (快捷键) 键来增强此界面。

以下主题介绍了标准键盘接口、访问键和快捷键:

标准键盘接口

系统设计为使用或不使用鼠标或其他指针设备。 由于系统提供标准键盘界面,因此用户可以使用键盘选择菜单项。 此键盘接口不需要特殊代码。 无论用户是通过键盘还是鼠标选择菜单项,应用程序都会收到命令消息。 标准键盘接口处理以下击键。

击键 操作
字母字符 选择具有指定字符作为其访问键的第一个菜单项。 如果所选项调用菜单,则显示菜单并突出显示第一项。 否则,将选择菜单项。
ALT 切换菜单栏模式和菜单栏模式。
ALT+空格键 显示“窗口”菜单。
Enter 激活菜单,如果某个项具有与之关联的菜单,则选择第一个菜单项。 否则,此击键选择项目,就像用户在选择该项时松开鼠标按钮一样。
ESC 退出菜单模式。
向左键 到上一个顶级菜单项的周期。 顶级菜单项包括菜单名称和窗口菜单。 如果所选项位于菜单中,则选择菜单中的上一列或上一个顶级菜单项。
向右键 工作方式类似于向左键,但方向相反。 在菜单中,此击键向前移动一列;当当前所选项位于最右侧列中时,将选择下一个菜单。
向上或向下键 在菜单名称中按下时激活菜单。 在菜单中按下时,向上键击将选择上一项;向下键击键选择下一项。

 

可以通过向菜单项添加访问键 (助记) 来增强菜单的标准键盘界面。 访问键是菜单项文本中的带下划线的字母。 当菜单处于活动状态时,用户可以通过按对应于该项的带下划线字母的键来选择菜单项。 用户通过按 Alt 键突出显示菜单栏上的第一项,使菜单栏处于活动状态。 菜单在显示时处于活动状态。

若要为菜单项创建访问键,请在项的文本字符串中的任何字符前面加上一个和号。 例如,文本字符串“Move”&会导致系统为字母“M”添加下划线。

除了具有访问键之外,菜单项还可以具有与之关联的快捷键。 快捷键不同于访问键,因为菜单不必处于活动状态,快捷键即可正常工作。 此外,访问键 始终 与菜单项相关联,而快捷键 通常 (,但不必) 与菜单项相关联。

标识快捷键的文本将添加到菜单项文本字符串中。 快捷文本显示在菜单项名称的右侧,在反斜杠和制表符 (\t) 后面。 例如,“&Close\tAlt+F4”表示一个 Close 命令,其中 Alt+F4 组合键作为其快捷键,字母“C”作为其访问键。 有关详细信息,请参阅 键盘加速器

可以使用菜单模板或菜单创建函数创建菜单。 菜单模板通常定义为资源。 菜单模板资源可以显式加载或分配为窗口类的默认菜单。 还可以在内存中动态创建菜单模板资源。

以下主题详细介绍了菜单创建:

大多数应用程序使用菜单模板资源创建菜单。 菜单模板定义菜单,包括菜单栏中的项和所有菜单。 有关创建菜单模板资源的信息,请参阅开发工具附带的文档。

创建菜单模板资源并将其添加到应用程序的可执行文件 (.exe) 文件后,可以使用 LoadMenu 函数将资源加载到内存中。 此函数返回菜单的句柄,然后可以使用 SetMenu 函数将其分配给窗口。 可以将菜单分配给任何不是子窗口的窗口。

将菜单作为资源实现使应用程序更易于本地化,以便在多个国家/地区使用。 只需要为每种语言本地化资源定义文件,而不需要针对应用程序的源代码进行本地化。

可以从运行时内置于内存中的菜单模板创建菜单。 例如,允许用户自定义其菜单的应用程序可能会根据用户的首选项在内存中创建菜单模板。 然后,应用程序可以将模板保存在文件或注册表中供将来使用。 若要从内存中的模板创建菜单,请使用 LoadMenuIndirect 函数。 有关菜单模板格式的说明,请参阅 菜单模板资源

标准菜单模板由 MENUITEMTEMPLATEHEADER 结构以及一个或多个 MENUITEMTEMPLATE 结构组成。

扩展菜单模板由 MENUEX_TEMPLATE_HEADER 结构以及一个或多个 MENUEX_TEMPLATE_ITEM 结构组成。

系统会为每个菜单生成唯一的句柄。 菜单句柄HMENU 类型的值。 应用程序必须在许多菜单函数中指定菜单句柄。 创建菜单或加载菜单资源时,会收到菜单栏的句柄。

若要检索已创建或加载的菜单的菜单栏的句柄,请使用 GetMenu 函数。 若要检索与菜单项关联的子菜单的句柄,请使用 GetSubMenuGetMenuItemInfo 函数。 若要检索窗口菜单的句柄,请使用 GetSystemMenu 函数。

使用菜单创建函数,可以在运行时创建菜单或向现有菜单添加菜单项。 可以使用 CreateMenu 函数创建空菜单栏,使用 CreatePopupMenu 函数创建空菜单。 可以使用 MENUINFO 结构保存菜单的某些设置信息。 若要获取或检索菜单的设置,请使用 GetMenuInfoSetMenuInfo。 若要向菜单添加项,请使用 InsertMenuItem 函数。 旧 版 AppendMenuInsertMenu 函数仍受支持,但 InsertMenuItem 应用于新应用程序。

加载或创建菜单后,必须先将其分配给窗口,系统才能显示它。 可以通过定义类菜单来分配菜单。 有关详细信息,请参阅 窗口类菜单。 还可以通过将菜单的句柄指定为 CreateWindowCreateWindowEx 函数的 hMenu 参数,或通过调用 SetMenu 函数将菜单分配给窗口。

若要显示快捷菜单,请使用 TrackPopupMenuEx 函数。 快捷菜单(也称为浮动弹出菜单或上下文菜单)通常在处理 WM_CONTEXTMENU 消息时显示。

可以将菜单分配给任何不是子窗口的窗口。

仍支持较旧的 TrackPopupMenu 函数,但新应用程序应使用 TrackPopupMenuEx 函数。

窗口类菜单

注册窗口类时,可以指定默认菜单(称为 类菜单 )。 为此,请将菜单模板资源的名称分配给用于注册类的 WNDCLASS 结构的 lpszMenuName 成员。

默认情况下,每个窗口都为其窗口类分配类菜单,因此无需显式加载菜单并将其分配给每个窗口。 可以通过在对 CreateWindowEx 函数的调用中指定不同的菜单句柄来替代类菜单。 还可以在使用 SetMenu 函数创建窗口后更改该窗口的菜单。 有关详细信息,请参阅 窗口类

以下主题讨论用户选择菜单项时系统执行的操作,以及应用程序控制项的外观和功能的方式:

打开子菜单的命令项和项

当用户选择命令项时,系统会向拥有菜单的窗口发送命令消息。 如果命令项位于窗口菜单上,系统会发送 WM_SYSCOMMAND 消息。 否则,它会发送 WM_COMMAND 消息。

与打开子菜单的每个菜单项关联的是对应子菜单的句柄。 当用户指向此类项时,系统会打开子菜单。 不会向所有者窗口发送命令消息。 但是,在显示子菜单之前,系统会向所有者窗口发送 WM_INITMENUPOPUP 消息。 可以使用 GetSubMenuGetMenuItemInfo 函数获取与项关联的子菜单的句柄。

菜单栏通常包含菜单名称,但它也可以包含命令项。 子菜单通常包含命令项,但它也可以包含打开嵌套子菜单的项。 通过将此类项添加到子菜单,可以将菜单嵌套到任何深度。 为了为用户提供视觉提示,系统会自动在打开子菜单的菜单项文本右侧显示一个小箭头。

与每个菜单项关联的是应用程序定义的唯一整数,称为 菜单项标识符。 当用户从菜单中选择命令项时,系统会将该项的标识符作为 WM_COMMAND 消息的一部分发送到所有者窗口。 窗口过程检查标识符以确定消息的源,并相应地处理消息。 此外,在调用菜单函数时,可以使用菜单项的标识符指定菜单项;例如,启用或禁用菜单项。

打开子菜单的菜单项具有标识符,就像命令项一样。 但是,当从菜单中选择此类项时,系统不会发送命令消息。 相反,系统会打开与菜单项关联的子菜单。

若要检索指定位置的菜单项的标识符,请使用 GetMenuItemIDGetMenuItemInfo 函数。

除了具有唯一标识符外,菜单栏中或菜单中的每个菜单项都具有唯一的位置值。 菜单栏中最左侧的项或菜单中的顶部项的位置为零。 后续菜单项的位置值递增。 系统将位置值分配给菜单中的所有项,包括分隔符。 下图显示了菜单栏和菜单中项的位置值。

菜单栏和菜单

调用修改或检索特定菜单项相关信息的菜单函数时,可以使用项的标识符或位置来指定该项。 有关更多信息,请参见下一节。

以编程方式访问菜单项

大多数菜单函数允许按位置或命令指定菜单项。 某些函数使用 MF_BYPOSITIONMF_BYCOMMAND 标志来指示搜索算法;其他参数具有显式 的 fByPosition 参数。 如果按位置指定菜单项,则项编号是菜单中的从零开始的索引。 如果通过 命令指定菜单项,则菜单及其子菜单将搜索其菜单标识符等于提供的项编号的项。 如果菜单层次结构中的多个项与项编号匹配,则未指定使用哪一项。 如果菜单包含重复的菜单标识符,则应使用基于位置的菜单操作来避免这种歧义。

默认菜单项

子菜单可以包含一个默认菜单项。 当用户通过双击打开子菜单时,系统会向菜单的所有者窗口发送命令消息,并关闭菜单,就像选择了默认命令项一样。 如果没有默认命令项,子菜单将保持打开状态。 若要检索和设置子菜单的默认项,请使用 GetMenuDefaultItemSetMenuDefaultItem 函数。

“选定”和“清除”菜单项

菜单项可以是选中的,也可以是清除的。 系统会在所选菜单项旁边显示一个位图,以指示其选定状态。 系统不会在清除项旁边显示位图,除非指定了应用程序定义的“清除”位图。 只能选择菜单中的菜单项;无法选择菜单栏中的项。

应用程序通常检查或清除菜单项以指示某个选项是否有效。 例如,假设应用程序有一个工具栏,用户可以使用菜单上的 工具栏 命令显示或隐藏该工具栏。 当工具栏处于隐藏状态时, “工具栏” 菜单项将清除。 当用户选择命令时,应用程序会检查菜单项并显示工具栏。

检查标记属性控制是否选择了菜单项。 可以使用 CheckMenuItem 函数设置菜单项的 检查 mark 属性。 可以使用 GetMenuState 函数来确定当前是选择还是清除菜单项。

可以使用 GetMenuItemInfo 和 SetMenuItemInfo 函数来检索和设置菜单项的检查状态,而不是 CheckMenuItemGetMenuState

有时,一组菜单项对应于一组互斥选项。 在这种情况下,可以使用所选单选菜单项 (类似于单选按钮控件) 来指示所选选项。 所选单选项使用项目符号位图而不是检查标记位图显示。 若要检查菜单项并使其成为单选项,请使用 CheckMenuRadioItem 函数。

默认情况下,系统会在所选菜单项旁边显示检查标记或项目符号位图,在清除的菜单项旁边不显示位图。 但是,可以使用 SetMenuItemBitmaps 函数将应用程序定义的选定位图和清除的位图与菜单项相关联。 然后,系统使用指定的位图来指示菜单项的选定或清除状态。

与菜单项关联的应用程序定义的位图的大小必须与默认检查标记位图相同,其尺寸可能因屏幕分辨率而异。 若要检索正确的维度,请使用 GetSystemMetrics 函数。 可以为不同的屏幕分辨率创建多个位图资源;根据需要创建一个位图资源并缩放它;或在运行时创建位图并在其中绘制图像。 位图可以是单色或彩色。 但是,由于菜单项在突出显示时会反转,因此某些倒排颜色位图的外观可能不可取。 有关详细信息,请参阅 位图

已启用、灰显和禁用的菜单项

菜单项可以启用、灰显或禁用。 默认情况下,菜单项处于启用状态。 当用户选择已启用的菜单项时,系统会向所有者窗口发送命令消息或显示相应的子菜单,具体取决于菜单项的类型。

当用户无法使用菜单项时,应将其灰显或禁用。 无法选择灰显和禁用的菜单项。 禁用的项看起来就像已启用的项。 当用户单击禁用的项时,不会选择该项,并且不会发生任何反应。 例如,在显示一个看起来处于活动状态但不处于活动状态的菜单的教程中,禁用的项可能很有用。

应用程序将不可用的菜单项灰显,以向用户提供命令不可用的视觉提示。 当某个操作不适用时,可以使用灰色项 (例如,当系统未安装打印机时,可以在“文件”菜单中的“打印”命令) 灰显。

EnableMenuItem 函数启用、灰显或禁用菜单项。 若要确定菜单项是启用、灰显还是禁用,请使用 GetMenuItemInfo 函数。

还可以使用 GetMenuState 函数来确定菜单项是启用、灰显还是禁用,而不是 GetMenuItemInfo

突出显示的菜单项

当用户选择菜单项时,系统会自动突出显示菜单上的菜单项。 但是,可以使用 HiliteMenuItem 函数在菜单栏上的菜单名称中显式添加或删除突出显示。 此函数对菜单上的菜单项没有影响。 不过,使用 HiliteMenuItem 突出显示菜单名称时,该名称仅显示为选中状态。 如果用户按 ENTER 键,则不会选择突出显示的项目。 例如,在演示菜单用法的训练应用程序中,此功能可能很有用。

Owner-Drawn菜单项

应用程序可以使用 所有者绘制的项完全控制菜单项的外观。 所有者绘制的项要求应用程序对绘制所选 (突出显示) 状态、已选择状态和已清除状态承担全部责任。 例如,如果应用程序提供了字体菜单,它可以使用相应的字体绘制每个菜单项;用于 Roman 的项将用 roman 绘制,Italic 项将绘制为斜体,依序。 有关详细信息,请参阅 创建Owner-Drawn菜单项

系统提供特殊类型的菜单项,称为 分隔符,显示为水平线。 可以使用分隔符将菜单划分为一组相关项。 不能在菜单栏中使用分隔符,并且用户不能选择分隔符。

当菜单栏包含的菜单名称多于一行时,系统会通过将菜单栏自动拆分为两行或更多行来包装菜单栏。 通过将 MFT_MENUBREAK 类型标志分配给菜单栏上的特定项,可以导致在菜单栏上的特定项处出现换行符。 系统将该项和所有后续项放在新行上。

当菜单包含的项数超过一列中容纳的项数时,该菜单将被截断。 可以通过将 MFT_MENUBREAK 类型标志分配给该项或使用 MENUITEM 语句中的 MENUBREAK 选项,在菜单中的特定项处发生列分隔。 系统将该项和所有后续项置于新列中。 MFT_MENUBARBREAK类型标志具有相同的效果,只不过新列和旧列之间会出现一条垂直线。

如果使用 AppendMenuInsertMenuModifyMenu 函数来分配换行符,则应 MF_MENUBREAKMF_MENUBARBREAK分配类型标志。

与菜单一起使用的消息

系统通过将消息发送到拥有菜单的窗口的窗口过程来报告与菜单相关的活动。 当用户选择菜单栏上的项目或单击鼠标右键以显示快捷菜单时,系统会发送一系列消息。

当用户激活菜单栏上的项时,所有者窗口首先会收到 WM_SYSCOMMAND 消息。 此消息包含一个标志,指示用户是使用键盘 (SC_KEYMENU) 还是鼠标 (SC_MOUSEMENU) 激活菜单。 有关详细信息,请参阅 对菜单的键盘访问

接下来,在显示任何菜单之前,系统会将 WM_INITMENU 消息发送到窗口过程,以便应用程序可以在用户看到菜单之前修改菜单。 系统每次激活菜单仅发送一次 WM_INITMENU 消息。

当用户指向打开子菜单的菜单项时,系统会在显示子菜单之前向所有者窗口发送 WM_INITMENUPOPUP 消息。 此消息使应用程序有机会在显示子菜单之前对其进行修改。

每当用户将突出显示从一项移动到另一项时,系统会向菜单所有者窗口的窗口过程发送 WM_MENUSELECT 消息。 此消息标识当前选定的菜单项。 许多应用程序在其main窗口底部提供一个信息区域,并使用此消息显示有关所选菜单项的其他信息。

当用户从菜单中选择命令项时,系统会向窗口过程发送 WM_COMMAND 消息。 WM_COMMAND消息的 wParam 参数的低位字包含所选项目的标识符。 窗口过程应检查标识符并相应地处理消息。

可以使用 MENUINFO 结构保存菜单的信息。 如果菜单是使用 MENUINFO 定义的。dwStyle 值 MNS_NOTIFYBYPOS,系统发送 WM_MENUCOMMAND 而不是 WM_COMMAND 时选择项目。 这允许访问 MENUINFO 结构中的信息,并直接提供所选项的索引。

并非所有菜单都可以通过窗口的菜单栏访问。 当用户单击特定位置的鼠标右键时,许多应用程序会显示快捷菜单。 此类应用程序应处理 WM_CONTEXTMENU 消息并显示快捷菜单(如果适用)。 如果应用程序不显示快捷菜单,则应将 WM_CONTEXTMENU 消息传递给 DefWindowProc 函数进行默认处理。

当用户在光标位于菜单项上时释放鼠标右键时,将发送 WM_MENURBUTTONUP 消息。 提供此消息是为了应用程序可以显示菜单项的上下文相关菜单或快捷菜单。

有一些消息仅涉及拖放菜单。 当鼠标光标进入菜单项或从项的中心移动到项的顶部或底部时,会将 WM_MENUGETOBJECT 发送给拖放菜单的所有者。 当用户实际拖动菜单项时,将发送 WM_MENUDRAG 消息。

当下拉菜单或子菜单被销毁时,系统会发送 WM_UNINITMENUPOPUP 消息。

如果将菜单分配给窗口并且该窗口被销毁,则系统会自动销毁菜单及其子菜单,从而释放菜单的句柄和菜单占用的内存。 系统不会自动销毁未分配给窗口的菜单。 应用程序必须通过调用 DestroyMenu 函数来销毁未分配的菜单。 否则,即使应用程序关闭,该菜单仍存在于内存中。 若要结束调用线程的活动菜单,请使用 EndMenu。 如果平台不支持 EndMenu,请向活动菜单的所有者发送 WM_CANCELMODE 消息。