如何在 Delphi 应用程序中检测 TPopupMenu 的 OnClose (OnPopDown) 事件

And*_*zej 1 delphi delphi-10.3-rio

我可以在 TPopupMenu 的 OnPopUp 事件中编写一些代码。但我还需要另一个 OnPopDown 事件。有没有办法使用 Delphi 10.3.3 做到这一点?

And*_*and 6

您可以尝试多种选择。

方法一

在更简单的情况下,当您有一个需要跟踪其上下文菜单的特定控件时,您可以WM_CONTEXTMENU手动处理其消息:

  protected
    procedure WMContextMenu(var Message: TWMContextMenu);
      message WM_CONTEXTMENU;
Run Code Online (Sandbox Code Playgroud)

哪里(例如)

procedure TForm1.WMContextMenu(var Message: TWMContextMenu);
begin
  if
    Assigned(PopupMenu)
      and
    (ClientRect.Contains(ScreenToClient(Message.Pos)) or (Message.Pos = Point(-1, -1)))
  then
  begin
    Windows.Beep(200, 500); // pre-popup code
    if (Message.XPos = -1) and (Message.YPos = -1) then // Menu key or Shift+F10
      with ClientToScreen(Point(0, 0)) do
        PopupMenu.Popup(X, Y)
    else
      PopupMenu.Popup(Message.XPos, Message.YPos);
    Windows.Beep(400, 500); // post-popup code
  end
  else
    inherited;
end;
Run Code Online (Sandbox Code Playgroud)

考试 ClientRect.Contains(ScreenToClient(Message.Pos))是必要的,这样您就不会“覆盖”滚动条自己的上下文菜单。此外,您需要考虑使用键盘(例如菜单键或 Shift+F10)打开上下文菜单的情况。

方法二

如果这对您来说还不够,您可以创建自己的TPopupMenu子类并覆盖其Popup虚拟方法。添加一个DoPopdown方法并在最后调用它(按照DoPopup方法的设计)。

要快速测试此方法,您可以使用中介层类:

type
  TPopupMenu = class(Vcl.Menus.TPopupMenu)
    procedure Popup(X, Y: Integer); override;
  end;
Run Code Online (Sandbox Code Playgroud)

实施为

{ TPopupMenu }

procedure TPopupMenu.Popup(X, Y: Integer);
begin
  inherited;
  Windows.Beep(400, 500); // post-popup code
end;
Run Code Online (Sandbox Code Playgroud)

但当然,创建一个真正的后代类(TPopupMenuEx,也许?)并在 IDE 中注册会更好。添加FOnPopdown: TNotifyEvent私有字段、DoPopdown受保护的函数和已OnPopdown发布的属性。这精确地模仿了该OnPopup机制。

不用说,这种方法也适用于 aTTrayIcon的菜单。