Hide TMainMenu via TMenuItem OnClick event, with Custom VCL Style active

258 views Asked by At

Put TMainMenu on the form and attach this handler to some TMenuItem. It will hide the main menu, but not if Custom Vcl Style is active.

procedure TForm1.HideMenuClick(Sender: TObject);
begin
  Menu := nil;
  { raised exception class $C0000005 with message 'access violation at 0x0038f397: read of address 0x00000008'. }
end;

This will work, but only if the delay is large enough:

procedure TForm1.HideMenuClick(Sender: TObject);
begin
  TThread.ForceQueue(nil, procedure   
  begin
    Menu := nil
  end,
  20);
end;  

Is there a clean and reliable way to do this?

(Note: The question is only about TMainMenu and its OnClick events, not about other ways to remove the menu or other controls.)

Edit:

Use the mouse to reproduce the problem. If you use the keyboard to open the menu and activate the item, then single 'ForceQueue(nil, procedure begin Menu := nil; end)' will be enough to avoid access violation error. If the menu item is activated with a keyboard shortcut, then even simple 'Menu := nil' will work.

2

There are 2 answers

0
A. Bonic On

No arbitrary timeout, and it seems error free, at least with Delphi 11.3. But I doubt anyone would call this a clean solution.

procedure TForm1.HideMenuClick(Sender: TObject);
begin
  TThread.ForceQueue(nil, procedure
  begin
    TThread.ForceQueue(nil, procedure
    begin
      Menu := nil;
    end);
  end);
end;
3
HeartWare On

This one works for me without using timers or threads:

type
  TForm150 = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    HideMenu1: TMenuItem;
    procedure HideMenu1Click(Sender: TObject);
  private
    { Private declarations }
    PROCEDURE   HideMenu(VAR Msg : TMsg); MESSAGE WM_USER+1;
  public
    { Public declarations }
  end;

var
  Form150: TForm150;

implementation

{$R *.dfm}

procedure TForm150.HideMenu(var Msg: TMsg);
begin
  Menu:=NIL
end;

procedure TForm150.HideMenu1Click(Sender: TObject);
begin
  PostMessage(Handle,WM_USER+1,0,0)
end;

PostMessage allows the TMenuItem.OnClick event to complete before attempting to hide the TMainMenu.