Search code examples
delphifiremonkeydelphi-10.2-tokyo

Dynamically create submenu


I have a TMainMenu with a menu item called mnuWindows. I wish to create submenu items dynamically. I thought this code might do it but it doesn't work:

var
  mnuitm: TMenuItem;

mnuitm:=TMenuItem.Create(nil);
mnuitm.Text:='some text';
mnuWindows.AddObject(mnuitm);

When I click on mnuWIndows, nothing happens. Where am i going wrong?

EDIT:

The submenu was not displaying on clicking because each time I did so, the program had been freshly started and I didn't realize that under these circumstances, two clicks are necessary. The first click doesn't visibly do anything and the second click drops down the submenu. So, I concede the code snippet above works.

But I still have a difficulty. I need to create several submenu items so I tried the following loop inside the mnuWindows OnClick event handler:

for I := 0 to TabSet1.Tabs.Count - 1 do
begin
  mnuitm := TMenuItem.Create(mnuWindows);
  mnuitm.Text := TabSet1.Tabs[I].Text;
  mnuitm.OnClick:=MenuItemClick;
  if not mnuWindows.ContainsObject(mnuitm) then
    mnuWindows.AddObject(mnuitm);
end;

The intent of the above code is that clicking the mnuWindows item displays a list of the tabs in a tabset. This code works up to a point. On first being clicked, it correctly lists the current tabs. But when I add a tab and click on mnuWindows again, the new tab is not shown in the list. The list is exactly as before. I wondered if the menu needed updating or refreshing somehow. I came across the following method

IFMXMenuService.UpdateMenuItem(IItemsContainer, TMenuItemChanges)

but it is poorly documented and I'm not sure how to use it or even whether it is relevant.

EDIT2: I thought the two down votes on my post were harsh. I have searched the web extensively for an example of how to dynamically create submenus in Firemonkey and there is very little. I did find a solution from 2012, but syntax changes since then mean that it does not work in Tokyo 10.2.


Solution

  • I have answered my own question.

    As a reminder, what I wanted to do was to dynamically create a submenu under my top level menu item "Windows" (component name "mnuWindows"). In the submenu, I wished to list the names of the tabs in a tabset.

    Attempting to create the submenu dynamically in the mnuWindows.OnClick event was a failure.

    My eventual solution was to rebuild the submenu with the following method and to call this method immediately after creating a new tab, removing a tab, or renaming a tab:

    procedure Form1.ReBuildWindowsMenu;
    var
      mnuitm: TMenuItem;
      I: Integer;
    begin
      mnuWindows.Clear; // removes submenu items
      for I := 0 to TabSet1.Tabs.Count - 1 do
      begin
        mnuitm := TMenuItem.Create(MainMenu1);
        mnuitm.Caption:= TabSet1.Tabs[I].Text; // submenu item displays same text as associated tab
        mnuitm.OnClick := MenuItemClick; // makes the associated tab active
        mnuWindows.AddObject(mnuitm);
      end;
    end;
    

    The OnClick handler contains the single statement

    TabSet1.ActiveTabIndex:=(Sender as TMenuItem).Index;
    

    This simple solution keeps my Windows list perfectly synched with the tabs in the tabset. I'm planning to use a similar approach to put a most recently used (MRU) file list into my File menu.