菜单快捷键未显示(Delphi 2009)

8
我已经尽力了,但仍然无法弄清楚这里发生了什么。在Delphi 4中它运行得很好。升级到Delphi 2009后,我不知道这是否是它应该工作的方式,或者这是一个问题:

这是我的程序在Delphi 2009下设计模式下菜单的样子:

enter image description here

请注意,主菜单和文件子菜单中的每个单词都有一个字母被加下划线。它应该是这样的。这个带下划线的字母称为加速键,在Windows应用程序中是标准的,以便您可以使用Alt键和那个字母快速选择菜单项,然后使用键盘而不是鼠标选择子菜单项。

您可以通过在项目标题中使用“&”字符来获得它们,例如:保存&另存为...

当我运行我的应用程序并使用鼠标打开文件菜单时,它看起来像这样:

enter image description here

主菜单中的字符被加下划线,但文件菜单中的字符没有加下划线。

如果我使用Alt-F键打开文件子菜单,则它看起来像这样:

enter image description here

所有加速键字母都正确地加下划线了。

我已经尝试过AutoHotKeys选项,但那不是问题所在。

有人遇到过这个问题吗?第二张图片中的示例是否是我不知道的正确行为?或者是否有一些选项或编码错误我可能错过了?


2009年11月(一年后):mghie似乎已经找到了问题的根源并解决了问题。请参见他下面的已接受答案。

4个回答

7
有一个标准的Windows设置(在显示属性下)可以通常隐藏这些加速键,除非按住Alt键。这可能解释了为什么使用Alt + F10打开菜单会显示它们。也许这就是原因?
[编辑]:不是这样的。我刚试了一下,一个简单的TForm与一个菜单项显示了加速器,但只要我添加一个TImageList并设置单个菜单项的ImageIndex,或者只是将OwnerDraw设置为true,那么加速器下划线就消失了。我想这确实是VCL中的一个bug。
顺便说一下,这是在Windows XP上。
解决方法:
我已经在Windows XP 64上使用Delphi 2009进行了调试,缺少加速器的根本原因似乎是Windows发送带有ODS_NOACCEL标志的WM_DRAWITEM消息,如果系统设置为始终显示加速器,则不应该这样。因此,您可以说这不是VCL的错误,而是Windows的问题,VCL无法解决。
但是,在您自己的代码中可以解决它,您只需要在将消息传递给VCL之前重置该标志即可。覆盖窗口过程。
protected
  procedure WndProc(var Message: TMessage); override;

像这样:

procedure TYourForm.WndProc(var Message: TMessage);
const
  ODS_NOACCEL = $100;
var
  pDIS: PDrawItemStruct;
  ShowAccel: BOOL;
begin
  if (Message.Msg = WM_DRAWITEM) then begin
    pDIS := PDrawItemStruct(Message.LParam);
    if (pDIS^.CtlType = ODT_MENU)
      and SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, @ShowAccel, 0)
    then begin
      if ShowAccel then
        pDIS^.itemState := pDIS^.itemState and not ODS_NOACCEL;
    end;
  end;
  inherited;
end;

这只是演示代码,你不应该每次收到 WM_DRAWITEM 消息时都调用 SystemParametersInfo() ,而是在程序启动时调用一次,并在每次收到 WM_SETTINGCHANGE 消息时再调用。


我也在使用Windows XP。在看到这些答案并经过我的思考以及你的帮助后,我同意这可能是VCL中的一个错误。我会报告它。 - lkessler
顺便提一下,我在 Delphi 2007 中也遇到了相同的行为。 - mghie
Mghie:感谢您对此事的进一步研究(整整一年后!!)并找到了这个。我已经在Embarcadero的错误报告中更新了您的信息。真希望我能给你++++++++++1。 - lkessler
在程序启动时调用SystemParametersInfo()可能在99%的情况下都足够好了。 - lkessler
受你的代码启发,我想强制TPopupMenu始终显示下划线,所以在 WndProc 中我总是调用 pDIS^.itemState := pDIS^.itemState and not ODS_NOACCEL; 确保 ODS_NOACCEL 标志被排除,但不幸的是它没有生效。有什么提示吗?谢谢。 - Edwin Yip
显示剩余3条评论

6

这是Windows 2000引入的“功能”:

The Old New Thing: 为什么Windows默认隐藏键盘加速器和焦点矩形?

看起来Delphi 4不支持这个Windows功能。

要在2000和XP菜单中显示加速键,请右键单击桌面上的空白处,选择属性,点击外观选项卡,在效果下取消勾选在按下Alt键之前隐藏带下划线的字母以进行键盘导航。 点击两次OK。

不确定如何在Vista中实现。


我以为你有答案。但不幸的是,当我看到那个“隐藏下划线字母”的选项时(我以前从未知道),我发现它没有被选中。 - lkessler
什么操作系统?你的屏幕截图看起来像XP。我刚在XP上尝试了一下,它可以工作(取消选项并加速键下划线显示出来)。也许你需要重新启动。 - Jim McKeeth

1

我不认为这是Delphi生成的错误,因为在Vista上使用记事本也有相同的行为。而且同样出现在Delphi中...
在你提问之前,我必须承认我没有留意到这一点,感谢你指出。


不,在Delphi 2009中,所有的子菜单项都显示加速键,即使我用鼠标选择菜单也是如此。这就是为什么我认为这是我的某个设置问题,而不是Delphi或操作系统的问题。这绝对是一个奇怪的问题,我已经花了大约4个小时来尝试解决它。 - lkessler

0
正如Jim McKeeth所指出的(正确地),这是“设计”行为。如果通过键盘操作触发菜单,则应显示加速器,但如果由鼠标触发,则故意不显示加速器。
我将我的XP配置为始终显示加速器,但更改该选项后进行快速测试确认,当使用鼠标时,菜单也不应显示下划线(Visual Studio按照我预期的响应,没有使用鼠标时的下划线)。然而,Microsoft Office忽略了此设置并始终显示下划线。因此,看起来这是Delphi中绘制菜单的错误(我自己没有任何Delphi经验)。
我还找到了Vista的选项:http://www.vistax64.com/vista-general/42125-always-show-menu-underline-keyboard-accelerators.html 您可以在新的易于访问中心中打开此功能(转到控制面板,单击“易于访问”,然后单击“易于访问中心”)。在易于访问中心中,单击“使键盘更易于使用”,然后在底部选择下划线键盘快捷方式和访问键复选框。
在进一步研究中,我在Delphi论坛上发现了这个相关的错误:http://qc.codegear.com/wc/qcmain.aspx?d=37403 看起来在您的情况下,子窗口(绘制的菜单)没有获取或处理其父窗口发送的WM_UIUPDATESTATE消息,这就是导致加速键重新绘制的原因。

看起来在你的情况下,子窗口(绘制的菜单)没有从它们的父窗口获取或处理WM_UIUPDATESTATE消息,这就是导致加速键重绘的原因。 - 没错!那就是Bug! - lkessler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接