How to select a Menu Item without closing the Menu?

前端 未结 4 1430
野性不改
野性不改 2020-12-08 02:48

By default when you select an item from a TMainMenu or TPopupMenu etc, the menu closes after it was clicked. I would like to change this behavior so that when I select on a

4条回答
  •  有刺的猬
    2020-12-08 03:36

    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.
    

提交回复
热议问题