Как обнаружить событие OnClose (OnPopDown) TPopupMenu в приложениях Delphi

Я могу написать некоторый код внутри события OnPopUp TPopupMenu. Но мне также нужно другое событие для OnPopDown. Есть ли способ сделать это с помощью Delphi 10.3.3?


person Andrzej    schedule 18.07.2020    source источник
comment
Вы видели delphi.cjcsoft.net/viewthread.php?tid=45678? ?   -  person Andreas Rejbrand    schedule 18.07.2020
comment
@AndreasRejbrand Да, у меня это не работает.   -  person Andrzej    schedule 21.07.2020
comment
@Andrzej Каким именно образом? Пожалуйста, будьте более конкретными. Этот метод (переопределяющий TPopupListEx) действительно работает. Я использовал его раньше.   -  person Remy Lebeau    schedule 21.07.2020
comment
@RemyLebeau Я выполнил точные шаги, успешно скомпилировал, но подписи не изменились, используя Delphi 10.3.3.   -  person Andrzej    schedule 21.07.2020
comment
@Andrzej Какое отношение подписи имеют к обнаружению закрытия меню? Вы сейчас путаете вопрос.   -  person Remy Lebeau    schedule 21.07.2020
comment
@RemyLebeau Извините, я имею в виду код на веб-сайте cjcsoft, он должен изменить заголовок при обнаружении закрытия меню, но это не так. Ответ Андреаса Рейбранда работает нормально.   -  person Andrzej    schedule 21.07.2020
comment
@Andrzej отлично работал у меня, когда я использовал его раньше. Мы не видим код, который вы используете в своем проекте, возможно, вы применяете его неправильно.   -  person Remy Lebeau    schedule 21.07.2020


Ответы (1)


Есть разные варианты, которые вы можете попробовать.

Подход 1

В более простом случае, когда у вас есть конкретный элемент управления, контекстное меню которого нужно отслеживать, вы можете обработать его сообщение WM_CONTEXTMENU вручную:

  protected
    procedure WMContextMenu(var Message: TWMContextMenu);
      message WM_CONTEXTMENU;

где (например)

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;

Тест ClientRect.Contains(ScreenToClient(Message.Pos)) необходим, чтобы вы не перезаписали собственное контекстное меню полосы прокрутки. Также нужно учитывать случай, когда контекстное меню открывается с помощью клавиатуры (например, клавиши меню или Shift+F10).

Подход 2

Если вам этого недостаточно, вы можете создать свой собственный TPopupMenu дочерний класс и переопределить его метод Popup, который является виртуальным. Добавьте метод DoPopdown и вызовите его в конце (в соответствии с дизайном метода DoPopup).

Чтобы быстро протестировать этот подход, вы можете использовать класс интерпозера:

type
  TPopupMenu = class(Vcl.Menus.TPopupMenu)
    procedure Popup(X, Y: Integer); override;
  end;

реализовано как

{ TPopupMenu }

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

Но, конечно, лучше создать настоящий класс-потомок (возможно, TPopupMenuEx?), который вы регистрируете в IDE. Добавьте FOnPopdown: TNotifyEvent закрытое поле, DoPopdown защищенную функцию и OnPopdown опубликованное свойство. Это точно имитирует механизм OnPopup.

Излишне говорить, что этот подход также работает с меню TTrayIcon.

person Andreas Rejbrand    schedule 18.07.2020
comment
Извините, но это не работает для меня. Можете ли вы изменить код только для PopupMenu1? - person Andrzej; 18.07.2020
comment
@Andrzej: Что не работает? В первом подходе вам нужно изменить элемент управления, а не всплывающее меню. - person Andreas Rejbrand; 18.07.2020
comment
Код перед всплывающим окном и код после всплывающего окна не срабатывают, я полагаю - person Andrzej; 18.07.2020
comment
Это один конкретный элемент управления, контекстное меню которого вам нужно отслеживать? - person Andreas Rejbrand; 18.07.2020
comment
У меня есть приложение VCL. Я сбросил PopupMenu1. Я могу добавить событие OnPopup для PopupMenu1, но мне также нужно добавить событие, когда PopupMenu1 закрывается. - person Andrzej; 18.07.2020
comment
Да, я знаю. Но вы также должны прикрепить всплывающее меню к какому-либо элементу управления или добавить код для его ручного отображения. Например, возможно, вы установили для свойства PopupMenu формы значение PopupMenu1 или у вас есть элемент управления TRichEdit с именем RichEdit1, PopupMenu которого вы установили для PopupMenu1. Или, может быть, у вас есть код, который вызывает PopupMenu1.Popup вручную. Что он? - person Andreas Rejbrand; 18.07.2020
comment
Вы можете прикрепить одно всплывающее меню к нескольким элементам управления (например, к пяти элементам расширенного редактирования, двум кнопкам и одному полю редактирования). Если да, то мой первый подход не сработает. Вот почему я написал В более простом случае, когда у вас есть конкретный элемент управления, контекстное меню которого вам нужно отслеживать [...]. - person Andreas Rejbrand; 18.07.2020
comment
Ой, извини. Он прикреплен к Form1. PopupMenu Form1 установлен PopupMenu1. - person Andrzej; 18.07.2020
comment
Если это так, мой подход будет работать просто отлично. Вам нужно добавить мой первый фрагмент кода в объявление TForm1 в разделе interface. Второй фрагмент должен находиться в разделе implementation. Это будет работать. Обещаю. Если щелкнуть форму правой кнопкой мыши, вы услышите низкочастотный звуковой сигнал перед отображением всплывающего меню и высокочастотный звуковой сигнал, когда оно будет закрыто. Вы должны заменить эти звуковые сигналы кодом, который вы хотите выполнить до и после отображения меню. - person Andreas Rejbrand; 18.07.2020
comment
Без проблем! :-) - person Andreas Rejbrand; 18.07.2020
comment
Если бы VCL был достаточно любезен, чтобы передать дескриптор исходного окна обработчику, можно было бы использовать OnContextPopup вместо обработчика сообщений. В настоящее время было бы довольно неуклюже узнать, что запрос контекста был получен из дочернего элемента управления без связанного всплывающего меню или самой формы - если это вообще возможно. OTOH, если это нормально, что элементы управления без всплывающих окон запускают всплывающее окно формы, можно использовать обработчик событий. - person Sertac Akyuz; 19.07.2020
comment
@SertacAkyuz: Вы не можете использовать Sender? - person Andreas Rejbrand; 19.07.2020
comment
@Andreas - Нет, после того, как элемент управления выполняет сообщение в форму, отправителем является форма. - person Sertac Akyuz; 19.07.2020