1. Computing

How to Detect a TPopupMenu's OnClose (OnPopDown) Event

By

How to Detect a TPopupMenu's OnClose (OnPopDown) Event

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:
  1. Drop a TPopupMenu on a Delphi form
  2. Add several menu items to the PopupMenu
  3. Include the "PopupListEx" in the uses clause
  4. 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; 

Delphi tips navigator:
» How to Move ListBox Items with the Mouse (Drag and Drop)
« How to Remove the "Today" Mark from the TDateTimePicker Delphi component

©2014 About.com. All rights reserved.