Author: Jonas Bilinkevicius
I am creating a dynamic popup menu, fill it and call Popup to show the menu. After
that method returns I am done with the menu, so I free it (see comment of Peter
below). The problems is the OnClick events never get called. I found the problem
but don't see how to get around it. When the menu is created it is added to the
PopupList which has created a hidden window whose responsibility it is to dispatch
the wm_Command message sent by all popup menus. The problem is that the wm_Command
is happening after the Popup menu method returns and after I have freed the menu
and it has been removed from the PopupList.
How do I get around this? I don't see a mechanism to check if the menu selection
has been dispatched by the PopupList before freeing the menu item. I guess I could
make the Popup menu a field of my class and free it in the OnClick Events but I
won't be able to free the menu if no menu item is selected. I don't like this
solution since the only place I need the popup is in one method of my class so I
want to keep it a local variable.
Answer:
You go badly wrong when you think you're done. When Popup returns you are not done
with the menu, you have just shown it and the user can now make a menu selection or
close the menu by clicking elsewhere or hittin ESC. Only after that has happended
are you truely "done" with the menu, if you destroy the VCl wrapper earlier the
windows menu may visually persist but you have destroyed the link between it and
your code.
In D5 there is a solution to your problem. Add this unit to your project (no
further code is needed) and the active form will get the custom messages declared
in the units interface. You could destroy the popup menu instance when you see
CM_EXITMENULOOP. This solution does not work in earlier versions of Delphi which
did not expose the Popuplist to the outside world. In these versions the only
solution would be to install a WH_CALLWNDPROC hook (thread specific) when the menu
is popped up and remove it again when it gets the WM_EXITMENULOOP message.
1 unit ExPopupList;
2 3 interface4 5 uses6 Controls;
7 8 const9 CM_MENUCLOSED = CM_BASE - 1;
10 CM_ENTERMENULOOP = CM_BASE - 2;
11 CM_EXITMENULOOP = CM_BASE - 3;
12 13 implementation14 15 uses Messages, Forms, Menus;
16 17 type18 TExPopupList = class(TPopupList)
19 protected20 procedure WndProc(varmessage: TMessage); override;
21 end;
22 23 { TExPopupList }24 25 procedure TExPopupList.WndProc(varmessage: TMessage);
26 procedure Send(msg: Integer);
27 begin28 if Assigned(Screen.Activeform) then29 Screen.ActiveForm.Perform(msg, message.wparam, message.lparam);
30 end;
31 begin32 casemessage.Msg of33 WM_ENTERMENULOOP:
34 Send(CM_ENTERMENULOOP);
35 WM_EXITMENULOOP:
36 Send(CM_EXITMENULOOP);
37 WM_MENUSELECT:
38 with TWMMenuSelect(message) do39 if (Menuflag = $FFFF) and (Menu = 0) then40 Send(CM_MENUCLOSED);
41 end;
42 inherited;
43 end;
44 45 initialization46 PopupList.Free;
47 PopupList := TExPopupList.Create;
48 {Note: will be freed by Finalization section of Menus unit}49 end.