2011. január 29., szombat
How to check if a menu selection has been dispatched by the TPopupList
Problem/Question/Abstract:
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.
unit ExPopupList;
interface
uses
Controls;
const
CM_MENUCLOSED = CM_BASE - 1;
CM_ENTERMENULOOP = CM_BASE - 2;
CM_EXITMENULOOP = CM_BASE - 3;
implementation
uses Messages, Forms, Menus;
type
TExPopupList = class(TPopupList)
protected
procedure WndProc(var Message: TMessage); override;
end;
{ TExPopupList }
procedure TExPopupList.WndProc(var Message: TMessage);
procedure Send(msg: Integer);
begin
if Assigned(Screen.Activeform) then
Screen.ActiveForm.Perform(msg, Message.wparam, Message.lparam);
end;
begin
case message.Msg of
WM_ENTERMENULOOP:
Send(CM_ENTERMENULOOP);
WM_EXITMENULOOP:
Send(CM_EXITMENULOOP);
WM_MENUSELECT:
with TWMMenuSelect(Message) do
if (Menuflag = $FFFF) and (Menu = 0) then
Send(CM_MENUCLOSED);
end;
inherited;
end;
initialization
PopupList.Free;
PopupList := TExPopupList.Create;
{Note: will be freed by Finalization section of Menus unit}
end.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése