如何使用 WM_MENURBUTTONUP 检测 TMenuItem 上的右键单击?

use*_*348 1 delphi winapi menuitem delphi-11-alexandria

在 Windows 10 上的 32 位 Delphi 11 VCL 应用程序中,我使用一个TApplicationEvents组件来捕获 Windows 消息。不幸的是,当我右键单击菜单项时,TApplicationEvents似乎没有对消息做出反应:WM_MENURBUTTONUPTPopupMenu

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
  case Msg.message of
      Winapi.Messages.WM_MENURBUTTONUP: CodeSite.Send('TForm1.ApplicationEvents1Message: WM_MENURBUTTONUP');
  end;
end;
Run Code Online (Sandbox Code Playgroud)

微软文档说:

WM_MENURBUTTONUP 消息
当用户在光标位于菜单项上时释放鼠标右键时发送。

作为替代方案,WM_COMMAND通过左键单击和右键单击发送。但是,出于特定目的,我只需要在右键单击菜单项时做出反应。

And*_*and 5

文档引用的部分解释了为什么您没有看到此消息:

用户 [...]

TApplicationEvents.OnMessage事件只能检测已发布的消息,而不能检测已发送的消息。

主菜单

所以如果你想检测这个消息,你可以添加

  protected
    procedure WndProc(var Message: TMessage); override;
Run Code Online (Sandbox Code Playgroud)

到您的表单类,实现如下:

procedure TForm1.WndProc(var Message: TMessage);
begin
  if Message.Msg = WM_MENURBUTTONUP then
    ShowMessage('rbu')
  else
    inherited
end;
Run Code Online (Sandbox Code Playgroud)

尝试一下,例如:

procedure TForm1.WndProc(var Message: TMessage);
begin
  if Message.Msg = WM_MENURBUTTONUP then
  begin
    var MI := Menu.FindItem(Message.LParam, fkHandle);
    if Assigned(MI) and InRange(Message.WParam, 0, MI.Count - 1) then
      ShowMessageFmt('Menu item "%s" right clicked.', [MI.Items[Message.WParam].Caption]);
  end
  else
    inherited
end;
Run Code Online (Sandbox Code Playgroud)

弹出式菜单

对于 a TPopupMenu,您需要编写自己的TPopupList后代:

type
  TPopupListEx = class(TPopupList)
  protected
    procedure WndProc(var Message: TMessage); override;
  end;

{ TPopupListEx }

procedure TPopupListEx.WndProc(var Message: TMessage);
begin
  if Message.Msg = WM_MENURBUTTONUP then
    ShowMessage('rbu')
  else
    inherited
end;

initialization
  FreeAndNil(PopupList);
  PopupList := TPopupListEx.Create;
Run Code Online (Sandbox Code Playgroud)

并确保将TPopupMenu的设置TrackButtontbLeftButton

如果您有多个弹出菜单,您可以尝试这样的操作(未完全测试):

procedure TPopupListEx.WndProc(var Message: TMessage);
begin
  if Message.Msg = WM_MENURBUTTONUP then
  begin
    for var X in PopupList do
      if TObject(X) is TPopupMenu then
      begin
        OutputDebugString(PChar(TPopupMenu(X).Name));
        var MI: TMenuItem;
        if TPopupMenu(X).Handle = HMENU(Message.LParam) then
          MI := TPopupMenu(X).Items
        else
          MI := TPopupMenu(X).FindItem(HMENU(Message.LParam), fkHandle);
        if Assigned(MI) and InRange(Message.WParam, 0, MI.Count - 1) then
        begin
          ShowMessageFmt('Menu item "%s" right clicked.', [MI.Items[Message.WParam].Caption]);
          Break;
        end;
      end;
  end
  else
    inherited
end;
Run Code Online (Sandbox Code Playgroud)

示例应用程序的屏幕录制,显示主菜单和两个不同的上下文菜单中的人民币单击。

  • @user1580348:我在我的答案中添加了这种方法,所以现在它可以与弹出菜单一起使用。是的,当然有可能。使用“GetKeyState”。 (2认同)