In the below code, when right clicked on the panel on the form, a popup menu with three items is launched. The first item behaves normally, the other two items also fires their click events but the popup menu is not closed.
The popup is launched with 'TrackPopupMenu', if instead you'd like to use 'OnPopup' events, or need to use sub menus having non-closing items, refer to the link in the comment I posted to your question. Adapting the code for a main menu would not be difficult as well..
I'm not commenting the code not to promote the usage of the approach since it makes use of an undocumented message, also I feel it is a bit convoluted..
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Menus, ExtCtrls;
type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
Item1Normal1: TMenuItem;
Item2NoClose1: TMenuItem;
Item3NoClose1: TMenuItem;
Panel1: TPanel;
procedure Panel1ContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
private
FGetPopupWindowHandle: Boolean;
FPopupWindowHandle: HWND;
OrgPopupWindowProc, HookedPopupWindowProc: Pointer;
FSelectedItemID: UINT;
procedure WmInitMenuPopup(var Msg: TWMInitMenuPopup); message WM_INITMENUPOPUP;
procedure WmEnterIdle(var Msg: TWMEnterIdle); message WM_ENTERIDLE;
procedure WmMenuSelect(var Msg: TWMMenuSelect); message WM_MENUSELECT;
procedure PopupWindowProc(var Msg: TMessage);
procedure MenuSelectPos(Menu: TMenu; ItemPos: UINT; out CanClose: Boolean);
procedure MenuSelectID(Menu: TMenu; ItemID: UINT; out CanClose: Boolean);
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Panel1ContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
var
Pt: TPoint;
begin
Pt := (Sender as TPanel).ClientToScreen(MousePos);
TrackPopupMenu(PopupMenu1.Handle, 0, Pt.X, Pt.Y, 0, Handle, nil);
end;
procedure TForm1.WmInitMenuPopup(var Msg: TWMInitMenuPopup);
begin
inherited;
if Msg.MenuPopup = PopupMenu1.Handle then
FGetPopupWindowHandle := True;
end;
procedure TForm1.WmEnterIdle(var Msg: TWMEnterIdle);
begin
inherited;
if FGetPopupWindowHandle then begin
FGetPopupWindowHandle := False;
FPopupWindowHandle := Msg.IdleWnd;
HookedPopupWindowProc := classes.MakeObjectInstance(PopupWindowProc);
OrgPopupWindowProc := Pointer(GetWindowLong(FPopupWindowHandle, GWL_WNDPROC));
SetWindowLong(FPopupWindowHandle, GWL_WNDPROC, Longint(HookedPopupWindowProc));
end;
end;
procedure TForm1.WmMenuSelect(var Msg: TWMMenuSelect);
begin
inherited;
if Msg.Menu = PopupMenu1.Handle then
FSelectedItemID := Msg.IDItem;
end;
const
MN_BUTTONDOWN = $01ED;
procedure TForm1.PopupWindowProc(var Msg: TMessage);
var
NormalItem: Boolean;
begin
case Msg.Msg of
MN_BUTTONDOWN:
begin
MenuSelectPos(PopupMenu1, UINT(Msg.WParamLo), NormalItem);
if not NormalItem then
Exit;
end;
WM_KEYDOWN:
if Msg.WParam = VK_RETURN then begin
MenuSelectID(PopupMenu1, FSelectedItemID, NormalItem);
if not NormalItem then
Exit;
end;
WM_DESTROY:
begin
SetWindowLong(FPopupWindowHandle, GWL_WNDPROC, Longint(OrgPopupWindowProc));
classes.FreeObjectInstance(HookedPopupWindowProc);
end;
end;
Msg.Result := CallWindowProc(OrgPopupWindowProc, FPopupWindowHandle,
Msg.Msg, Msg.WParam, Msg.LParam);
end;
procedure TForm1.MenuSelectID(Menu: TMenu; ItemID: UINT; out CanClose: Boolean);
var
Item: TMenuItem;
begin
CanClose := True;
Item := Menu.FindItem(ItemID, fkCommand);
if Assigned(Item) then begin
// Menu Item is clicked
Item.Click;
// Panel1.Caption := Item.Name;
CanClose := Item = Item1Normal1;
end;
end;
procedure TForm1.MenuSelectPos(Menu: TMenu; ItemPos: UINT; out CanClose: Boolean);
begin
MenuSelectID(Menu, GetMenuItemID(Menu.Handle, ItemPos), CanClose);
end;
end.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…