Search code examples
delphimenutactionmanager

How to add Submenus to TActionMainMenuBar programatically?


I have been developing a VCL program and am adding code for placing a 'Recent Files' dropdown submenu listing a group of files. For this program I have populated a tActionManager and a tActionMainMenuBar. I have created similar menus before using tmenuitems in a standard tMainMenu component generating the list of recent file submenu items in the FormCreate event procedure. However, I am uncertain how to go about this dynamically using the Action components and have not found any examples to serve as a model.

Can anyone provide an example of how this is done


Solution

  • To illustrate this I created a simple application with an action manager as TActionMainToolBar and added a simple file menu using standard items Open, Save As and Exit (no separators) plus a speed button.

    The speed button adds two separators and a file action, and its OnClick event looks like this...

    procedure TForm1.SpeedButton1Click(Sender: TObject);
    var
      iActionClientItem : TActionClientItem;
      iFileAction : TCustomAction;
    begin
      //  Edit2.Text := BaseToBase( Edit1.Text, SpinEdit1.Value , SpinEdit2.Value );
      ActionManager1.AddSeparator( ActionManager1.FindItemByAction(FileExit1), FALSE );
      iActionClientItem := ActionManager1.AddSeparator( ActionManager1.FindItemByAction(FileExit1) );
      iFileAction :=  TCustomAction.Create( self ); // we want to put in same collection
      iFileAction.Caption := 'Fred';
      // etc.. to build what is wanted
      iActionClientItem := ActionManager1.AddAction( iFileAction, iActionClientItem )  ;
    end;
    

    Running the program and opening the file menu shows the three expected entries. After clicking the button (don't do it twice - it is justy to demonstrate how it works) you get the two separators and a new menu sub item with the caption 'Fred' Note that you may need to give it a unique name if you need to do any handling of it.

    Edit

    Building sub menus can be done basically the same way but with a little trick of adding an unused and not visible item not through the action manager. It doesn't have to be done this way - it is just a simple way to get what we want.

    This code shows how to achieve this:

    procedure TForm1.SpeedButton1Click(Sender: TObject);
    var
      iActionClientItem : TActionClientItem;
      iSubMenuItem : TActionClientItem;
      iFileAction : TCustomAction;
      iChildMenu : TActionBarItem;
    begin
      ActionManager1.AddSeparator( ActionManager1.FindItemByAction(FileExit1), FALSE );
      iActionClientItem := ActionManager1.AddSeparator( ActionManager1.FindItemByAction(FileExit1) );
      iFileAction :=  TCustomAction.Create( self ); // we want to put in same collection
      iFileAction.Caption := 'Fred';
      // etc.. to build what is wanted
      iActionClientItem := ActionManager1.AddAction( iFileAction, iActionClientItem )  ;
    
      //*********************************
      // Build sub menu (from Fred) - stage 1 add the visual element - a new action bar
      iChildMenu :=  ActionManager1.ActionBars.Add;
      iActionClientItem.ChildActionBar := iChildMenu.ActionBar;
    
      // we add a dummy entry that we can build from.
      // We could set the properties manually, but when we use the action manager
      // it does that automatically, so it is easier just to set this item to not visible
      // then use action manager to do the rest.
      iSubMenuItem := iActionClientItem.Items.Add;
      iSubMenuItem.Visible := FALSE;
    
      // Now the real build...
    
      iFileAction :=  TCustomAction.Create( self ); // we want to put in same collection
      iSubmenuItem := ActionManager1.AddAction( iFileAction, iSubMenuItem );
      iSubmenuItem.Caption := 'Fred 1';
      // etc
    
      iFileAction :=  TCustomAction.Create( self ); // we want to put in same collection
      iSubmenuItem := ActionManager1.AddAction( iFileAction, iSubMenuItem );
      iSubmenuItem.Caption := 'Fred 2';
      // etc
    end;