Title: How to Detect a TPopupMenu's OnClose (OnPopDown) Event in Delphi applications
The TPopupMenu component encapsulates the properties, methods, and events of a pop-up menu. A pop-up menu appears (for a component or a form) when the user clicks on a control with the right mouse button.
TPopupMenu provides the OnPopup event that gets fired just before the pop-up menu appears.
Unfortunately, the TPopupMenu does not expose an event you can handle that will fire when the menu gets closed - either after a user has selected an item from the menu or has activated some other UI element.
What happens when a user selects an item from a popup menu is that Windows sends a WM_MENUSELECT message (with some specific parameter values) and a WM_EXITMENULOOP message to the window owning the menu. However, the window owning the menu is *not* your Delphi form - it is a tool window created and managed by the TPopupList class defined in the menus.pas unit.
To get notified when the popup menu closes, you have to extend the PopupList class by adding several new message handling options.
TPopupListEx
Here's the source of the extended PopupList class you need to add to your projects in order to be able to respond when the popup menu is closed:
unit PopupListEx;
interface
uses Controls;
const
CM_MENU_CLOSED = CM_BASE + 1001;
CM_ENTER_MENU_LOOP = CM_BASE + 1002;
CM_EXIT_MENU_LOOP = CM_BASE + 1003;
implementation
uses Messages, Forms, Menus;
type
TPopupListEx = class(TPopupList)
protected
procedure WndProc(var Message: TMessage) ; override;
private
procedure PerformMessage(cm_msg : integer; msg : TMessage) ;
end;
{ TPopupListEx }
procedure TPopupListEx.PerformMessage(cm_msg: integer; msg : TMessage) ;
begin
if Screen.Activeform nil then
Screen.ActiveForm.Perform(cm_msg, msg.WParam, msg.LParam) ;
end;
procedure TPopupListEx.WndProc(var Message: TMessage) ;
begin
case message.Msg of
WM_ENTERMENULOOP: PerformMessage(CM_ENTER_MENU_LOOP, Message) ;
WM_EXITMENULOOP : PerformMessage(CM_EXIT_MENU_LOOP, Message) ;
WM_MENUSELECT :
with TWMMenuSelect(Message) do
begin
if (Menu = 0) and (Menuflag = $FFFF) then
begin
PerformMessage(CM_MENU_CLOSED, Message) ;
end;
end;
end;
inherited;
end;
initialization
Popuplist.Free; //free the "default", "old" list
PopupList:= TPopupListEx.Create; //create the new one
// The new PopupList will be freed by
// finalization section of Menus unit.
end.
Here's how to use the PopupListEx unit:
Drop a TPopupMenu on a Delphi form
Add several menu items to the PopupMenu
Include the "PopupListEx" in the uses clause
Write a procedure to handle PopupListEx's messages: CM_MENU_CLOSED, CM_ENTER_MENU_LOOP and CM_EXIT_MENU_LOOP
An example implementation (download):
uses PopupListEx, ...
TForm1 = class(TForm)
...
private
procedure CM_MenuClosed(var msg: TMessage) ; message CM_MENU_CLOSED;
procedure CM_EnterMenuLoop(var msg: TMessage) ; message CM_ENTER_MENU_LOOP;
procedure CM_ExitMenuLoop(var msg: TMessage) ; message CM_EXIT_MENU_LOOP;
...
implementation
procedure TForm1.CM_EnterMenuLoop(var msg: TMessage) ;
begin
Caption := 'PopMenu entered';
end;
procedure TForm1.CM_ExitMenuLoop(var msg: TMessage) ;
begin
Caption := 'PopMenu exited';
end;
procedure TForm1.CM_MenuClosed(var msg: TMessage) ;
begin
Caption := 'PopMenu closed';
end;