Author: Tomas Rutkauskas
Is it possible to know when a popup menu closes? I really need to differentiate
between when a user selects an item or when the menu disappears because the user
clicks somewhere else and it loses focus. Is there some message sent to the app
window once TrackPopupMenu() returns?
Answer:
Solve 1:
There are messages that are send to the window specified as the menus owner in the
call to TrackPopupMenu. If you are using Delphi 5, add the following unit to your
project and your form will get the three custom messages defined in the units
interface:
1 unit ExPopupList;
2 3 interface4 5 uses Controls;
6 7 const8 CM_MENUCLOSED = CM_BASE - 1;
9 CM_ENTERMENULOOP = CM_BASE - 2;
10 CM_EXITMENULOOP = CM_BASE - 3;
11 12 implementation13 14 uses Messages, Forms, Menus;
15 16 type17 TExPopupList = class(TPopupList)
18 protected19 procedure WndProc(varmessage: TMessage); override;
20 end;
21 22 { TExPopupList }23 24 procedure TExPopupList.WndProc(varmessage: TMessage);
25 26 procedure Send(msg: Integer);
27 begin28 if Assigned(Screen.Activeform) then29 Screen.ActiveForm.Perform(msg, message.wparam, message.lparam);
30 end;
31 32 begin33 casemessage.Msg of34 WM_ENTERMENULOOP:
35 Send(CM_ENTERMENULOOP);
36 WM_EXITMENULOOP:
37 Send(CM_EXITMENULOOP);
38 WM_MENUSELECT:
39 with TWMMenuSelect(message) do40 if (Menuflag = $FFFF) and (Menu = 0) then41 Send(CM_MENUCLOSED);
42 end;
43 inherited;
44 end;
45 46 initialization47 Popuplist.Free;
48 PopupList := TExPopupList.Create;
49 {Note: will be freed by Finalization section of Menus unit}50 51 end.
Solve 2
The TPopupMenu.Popup method (which is used to display such a menu even when
presented "automatically" by the VCL) has it's own message pump whilst being
displayed. i.e. the Popup procedure only returns to the caller when the menu has
been dismissed.
I used this feature to implement a minor extension to TPopupMenu that not only
raises an event when the menu has been dismissed, but also peeks in the relevant
message queue for the presence of a WM_COMMAND message - i.e. was the menu
dismissed because an item was selected or because the menu was cancelled with no
item selected. This can then be reflected in the event.
52 { ... }53 type54 TIXPopupMenuEvent = procedure(Sender: TObject; Cancelled: Boolean) ofobject;
55 56 TIXPopupMenu = class(TPopupMenu)
57 private58 eOnDismissed: TIXPopupMenuEvent;
59 public60 procedure Popup(X, Y: Integer); override;
61 published62 property OnDismissed: TIXPopupMenuEvent read eOnDismissed write eOnDismissed;
63 end;
64 65 implementation66 67 {TIXPopupMenu}68 69 procedure TIXPopupMenu.Popup(X, Y: Integer);
70 var71 msg: tagMSG;
72 begin73 inherited;
74 if Assigned(OnDismissed) then75 OnDismissed(Self, PeekMessage(msg, PopupList.Window, WM_COMMAND,
76 WM_COMMAND, PM_NOREMOVE) = FALSE);
77 end;