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

How to detect right-click on a TMenuItem with WM_MENURBUTTONUP?

在 Windows 10 上的 32 位 Delphi 11 VCL 应用程序中,我使用 TApplicationEvents 组件来捕获 Windows 消息。不幸的是,当我右键单击 TPopupMenu MenuItem:

时,TApplicationEvents 似乎对 WM_MENURBUTTONUP 消息没有反应
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;

Microsoft documentation 说:

WM_MENURBUTTONUP message
Sent when the user releases the right mouse button while the cursor is on a menu item.

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

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

Sent when the user [...]

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

TMainMenu

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

  protected
    procedure WndProc(var Message: TMessage); override;

到你的表单class,实现如下:

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

尝试,例如:

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;

TPopupMenu

对于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;

并确保将 TPopupMenuTrackButton 设置为 tbLeftButton

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

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;