【问题标题】:How Do I Tell Which Menu Item is Open in Delphi?如何判断 Delphi 中打开了哪个菜单项?
【发布时间】:2011-12-13 00:40:47
【问题描述】:

我正在我的 Delphi 2009 应用程序中实现上下文相关帮助。除了在一种情况下,它工作正常。我无法确定我在主菜单中,以及打开了哪个菜单项。

我想要做的是,如果用户打开了文件菜单并且在打开时按 F1,那么我将在文件菜单上显示我的帮助。如果他们打开“编辑”菜单并按 F1,那么我将在“编辑”菜单等上显示我的帮助。

我正在使用 ApplicationEventsHelp 来处理用户按下 F1 如下:

function MainForm.ApplicationEvents1Help(Command: Word; Data: Integer;
  var CallHelp: Boolean): Boolean;
begin
  if Command = HELP_COMMAND then begin
    Application.HelpSystem.ShowTopicHelp(PChar(Data), Application.CurrentHelpFile);
    CallHelp := false;
  end;
  Result := true;
end;

正如我所提到的,这适用于除主菜单之外的所有内容。我试过使用

FindVCLWindow(Mouse.CursorPos)

和其他识别活动控件的方法,看看他们是否会识别菜单,但他们似乎没有。

有没有办法判断按下 F1 键时打开了哪个菜单项(如果有)?


感谢大家的帮助和好主意。

为了记录我的最终解决方案,我发现系统并不是特别擅长找出它所在的控件,有时会出错并将不正确的数据传递给 ApplicationEventsHelp,这会打开一个不适当的帮助页面。

在尝试并使用解决方案来处理已接受答案中的菜单后,我发现最好确定我所在的控件以显示正确的帮助项。我最终甚至没有使用 HelpKeyword 属性,而是对其进行了硬编码。代码很清晰,并且可以正常工作。我的 RVEdit 窗口也有我的帮助,它会根据您所在的文档部分显示不同的帮助页面(我的 CurCursorID 告诉我)。

对于任何想像我一样这样做的人,方法如下:

function TLogoAppForm.ApplicationEvents1Help(Command: Word; Data: Integer;
  var CallHelp: Boolean): Boolean;
var
  HelpKeyword: string;
  SType: string;

begin
  if Command = HELP_COMMAND then begin
    if PtInRect(RVEdit.ClientRect, RVEdit.ScreenToClient(Mouse.CursorPos)) then begin
      if CurCursorID = 'H' then HelpKeyword := 'RefTopReport'
      else if CurCursorID = 'T' then HelpKeyword := 'RefTableContents'
      else if CurCursorID = '~HNAME' then HelpKeyword := 'RefIndexNames'
      else if copy(CurCursorID, 1, 2) = 'N+' then HelpKeyword := 'RefIndexNames'
      else if CurCursorID = 'B' then HelpKeyword := 'RefBottomReport'
      else if CurCursorID <> '' then HelpKeyword := 'RefInformationArea'
      else HelpKeyword := 'RefEverythingReport';
      Application.HelpSystem.ShowTopicHelp(HelpKeyword, Application.CurrentHelpFile);
    end
    else if PtInRect(ElTree.ClientRect, ElTree.ScreenToClient(Mouse.CursorPos)) then
      Application.HelpSystem.ShowTopicHelp('RefTreeView', Application.CurrentHelpFile)
    else if PtInRect(TopToolbar.ClientRect, TopToolbar.ScreenToClient(Mouse.CursorPos)) then
      Application.HelpSystem.ShowTopicHelp('RefTopToolbar', Application.CurrentHelpFile)
    else if PtInRect(BottomToolbar.ClientRect, BottomToolbar.ScreenToClient(Mouse.CursorPos)) then
      Application.HelpSystem.ShowTopicHelp('RefBottomToolbar', Application.CurrentHelpFile)
    else
      Application.HelpSystem.ShowTopicHelp('RefMainWindow', Application.CurrentHelpFile);
    CallHelp := false;
  end
  else if Command = HELP_CONTEXTPOPUP then begin
    case Data of
      0: HelpKeyword := 'RefMenuBar';
      11: HelpKeyword := 'RefFileMenu';
      12: HelpKeyword := 'RefEditMenu';
      13: HelpKeyword := 'RefSearchMenu';
      14: HelpKeyword := 'RefNavigateMenu';
      15: HelpKeyword := 'RefViewMenu';
      16: HelpKeyword := 'RefOrganizeMenu';
      17: HelpKeyword := 'RefHelpMenu';
      else HelpKeyword := '';
    end;
    if HelpKeyword <> '' then begin
      Application.HelpSystem.ShowTopicHelp(HelpKeyword, Application.CurrentHelpFile);
      CallHelp := false;
    end;
  end;
  Result := true;
end;

我确实必须将 11 到 17 放入 7 个主菜单中 MenuItems 的 HelpContext 属性中,以便根据您所在的菜单显示正确的帮助。菜单项的检测就是帮助答案这个问题提供给我。

好消息是这段代码很容易理解(使用 HelpKeywords 而不是 HelpContext 数字)并且即使在转换为 Delphi XE 和 FireMonkey 之后可能仍然可以工作。

【问题讨论】:

  • 你的整个方法都是错误的。不要使用 FindVCLControl。为您的控件和操作设置 HelpContext 即可。
  • @David - FindVCLControl 是我认为可以作为一个组合工作的一个想法。我对这种方法不满意,而且它也不管用——这就是我问这个问题的原因。

标签: delphi menu help-system


【解决方案1】:

查看Command = HELP_COMMANDDataPChar 的演员表,您似乎使用基于关键字而不是上下文标识符(HelpType = htKeyword)的帮助系统。

(这里在 Delphi 7 中)菜单项没有 HelpTypeHelpKeyword 属性,因此您必须使用 HelpContext 属性:

function TForm1.ApplicationEvents1Help(Command: Word; Data: Integer;
  var CallHelp: Boolean): Boolean;
begin
  if Command = HELP_COMMAND then
  begin
    //Application.HelpSystem.ShowTopicHelp(PChar(Data), Application.CurrentHelpFile);
    //Doesn't this do the same?
    Application.HelpKeyword(PChar(Data));
    CallHelp := False;
  end
  else if Command = HELP_CONTEXT then
  begin
    // Convert the context identifier to your keyword, or:
    Application.HelpContext(Data);
    CallHelp := False;
  end;
  Result := True;
end;

【讨论】:

  • +1,在 2009 年,TMenuItem 仍然没有HelpType 也没有HelpKeyword,所以HelpContext 是正确的方法。
  • 我什至不确定是否需要这个事件处理程序。 CallHelp 默认为 True,因此应该会弹出帮助主题。此事件处理程序仅用于特殊干预。
  • @NGLN - 是的,我进行了特殊处理,但我将示例简化为基础。
  • @TLama - 如果您是说自 D2009 以来添加了 HelpType 和 HelpKeyword,那么当我升级到 XE2 时,我的问题将得到解决。
  • @NGLN - TY。在我升级之前,您已经给了我临时解决方法。但是,我必须检查 Command = HELP_CONTEXTPOPUP 而不是 HELP_CONTEXT 才能在菜单中捕获 HelpContext 值。
【解决方案2】:

通过捕获 Windows 消息“WM_MENUSELECT”,可以跟踪选定的菜单项。

更多信息请参见menuitemhints

例子:

 type
    TForm1 = class(TForm)
    ...
    private
      fMyCurrentSelectedMenuItem : TMenuItem;
      procedure WMMenuSelect(var Msg: TWMMenuSelect) ; message WM_MENUSELECT;
    end

procedure TForm1.WMMenuSelect(var Msg: TWMMenuSelect) ;
 var
    menuItem : TMenuItem;
    hSubMenu : HMENU;
 begin
    inherited; // from TCustomForm (so that Application.Hint is assigned)

    menuItem := nil;
    if (Msg.MenuFlag <> $FFFF) or (Msg.IDItem <> 0) then
    begin
      if Msg.MenuFlag and MF_POPUP = MF_POPUP then
      begin
        hSubMenu := GetSubMenu(Msg.Menu, Msg.IDItem) ;
        menuItem := Self.Menu.FindItem(hSubMenu, fkHandle) ;
      end
      else
      begin
        menuItem := Self.Menu.FindItem(Msg.IDItem, fkCommand) ;
      end;
    end;

    //miHint.DoActivateHint(menuItem) ;
    fMyCurrentSelectedMenuItem := menuItem;
 end; (*WMMenuSelect*)

因此,当按下 F1 按钮时,您可以使用 fMyCurrentSelectedMenuItem 来激活正确的帮助。

【讨论】:

  • 感谢您的回答。我一直在寻找这样的解决方案,但 NLGN 的方式更简洁,因为它将代码全部保存在一个地方。
【解决方案3】:

您可以使用 GetMenuItemRect 函数:
1. 浏览主菜单中的所有项目并调用 GetMenuItemRect 以获取项目位置。仅当显示项目时功能才会起作用。
2. 使用 GetCursorPos 和 PtInRect 检查鼠标是否在菜单项上并调用相应的帮助主题。

【讨论】:

  • 谢谢。这是另一种我不知道但很有用的可能性。
猜你喜欢
  • 1970-01-01
  • 2013-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多