MenuItem Arrays purely in code? (API 1)

Desktop API 1

Is there a way to create a menuItem array completely in code or can it only be done from the menubar editor where one needs to at least create the first element?

Thanks

  • Karen

You can create menus all in code:

Public Function MyMenuHandler(m as DesktopMenuItem) As Boolean
  MessageBox m.Text
End Function

This is the handler we’ll use and now we can create 10 menu items:

Sub Opening() Handles Opening
  Dim Menu As DesktopMenuItem = FileMenu
  
  For i As Integer = 1 To 10
    
    Dim m As New DesktopMenuItem
    
    m.Text = "Test "+i.ToString
    m.Enabled = true
    
    AddHandler m.MenuItemSelected, AddressOf MyMenuHandler
    
    menu.AddMenu m
  Next
End Sub

Can you try something like this?

1 Like

I found a solution that lets me use Xojo MenuHandlers in the IDE.

I created a simple MenuItem subclass

The MenuItem Subclass keeps a weakRef to the menubar and in it’s action event writes a reference to itself in the Menubar tag property and returns false. The menuitem selected can then be retrieved in the MenuHandler.

I also wrote an extends method on Menubar that assigns it’s weakRef to all menuitems of the subclass.
And another one to retrieve it and NIL the MenuItem reference in the Menubar Tag.

It would be nice if menu handlers in the IDE just passed in the item selected! (It would be analogous to MenuItem.popup).

I think I put in such a feature request years ago but it never got any traction.

You shouldn’t need to maintain a reference to the MenuBar itself.

Your subclass, when selected, will raise the Action event on the instance that was selected. Using an object oriented design, the item passed you are looking for is me in the Action event. If you use an AddHandler approach like Christian suggested, it is the first parameter of the handler function (which I usually call oSender).

There is an example project for an API 1.0 IDE at Example Projects/Desktop/Menus/OpenRecentMenu.xojo_binary_project

I knew I could use add handler but I did not want to go that way as I wanted to use IDE Menuhandlers as that keeps the Navigator more organized (All meuhandlers are in one place) instead of some scattered throughout the methods.

For me Navigator organization is more important than OO purity.

BTW way back when (RB 5 or earlier) before Addhandler was added to the language I came up with a way of using interfaces to accomplish the same thing as addhandler for timers

Thanks
-Karen

I agree with you, but just wanted to say you can organise methods by using prefixes; Xojo even supports unicode characters in method names, so you could, for example, have this:

AMethod
AnotherMethod
:v:FileOpenMenuHandler
:v:FileSaveMenuHandler
:v:ViewScaleMenuHandler

(dummy example, of course)

Thanks.

Sometimes I do that.
In this case I am not doing this for all menuitems, just related groups, so they would still be split between methods and the menuhhandler sections of the IDE.

I also think there is an advantage to staying closer to the normal Xojo MenuHandler mechanism.

-Karen

1 Like

In case anyone is not familiar with MenuItem Arrays or not clear on what I did and i curious, I will step though it in a bit more detail.

The Xojo Menu handlers you create in the IDE are called by the MenuItem name ( the name of handler executed is the same as the name of the MenuItem selected)

The thing is MenuItem names do not have to be unique. But when you have duplicate names you need some way to know which MenuItem was actually selected, as all you you know int handler is te MenuItem name.

Xojo uses the ability to have duplicate MenuItem names in the MenuItem Array feature (which I’m not sure is still documented but does work). IMO it is a very useful and convenient feature when you have bunch of related items that really only need one menu handler for all of them - it’s what the MenuItem.Index property is used for.

The down side is that you HAVE to create the MenuItems and add the indexes (or at least the first MenuItem) in the IDE. If you do that, when the Menuhandler is called at runtime, it passes in the index of the item so you can know which MenuItem was actually selected.

Unfortunately if you create some named MenuItems in code and set their indexes in code, and create a MenuHandler with that name in the IDE, the index is NOT passed into the handler when it is called. That of course is because of how the feature is implemented under the hood…

I suspect it was not part of the original menu API of CrossBasic and was added afterwards… If had been there originally, it would have made sense to just always pass in the index (or the selected MenuItem instance itself) into the handler all the time to fully support the feature…

I know some feel that MenuItem Arrays should not exist (Even some in Xojo inc) …

While I agree they way they are implemented is a bit on the hacky side… the feature itself is very much something useful in a RAD environment…

Which is why years ago i put in a feature request to always just passing selected MenuItem into the hander… which is more consistent with how MenuItem.popup works.

The suggestion mentions earlier of using Addhandler is the more “pure” Object Oriented way to do it… and I do use it sometimes, but IMO it is less RADish (particularly for things you could use MenuArrays for) and is less intuitive because Xojo does not use that approach for ALL MenuHandlers…

So the problem I was trying to solve is how to use Xojo MenuHanders defined in the IDE for same named MenuItems defined in code in a similar way one would with a MenuItem Array…

And since making the selected MenuItem instance available in the menu handers gives not only the item index , but also it’s tag and displayed text, (all you would get with AddHandler as well as MenuItem.Popup), that was my goal.

To that end I made this simple MenuItem subclass.
(code is all API 1 but should be easily translated to API 2)

Class CustomMenuItem
Inherits Menuitem
	Event
		Function Action() As Boolean
		  If NOT (ParentBar Is NIL) then MenuBar(ParentBar.Value).Tag = me
		End Function
	EndEvent

	#tag Property, Flags = &h0
		ParentBar As WeakRef
	#tag EndProperty

All this does is store a weak reference to the MenuItem selected in the MenuBar tag property

I could assign the Menubar weak reference to the MenuItem when i create it code, but there might be times when I clone an add to another menubar or I want to create the item in the IDE and change it’s super to CustomMenuItem because I want the instance passed in for some reason…

So I created an extends method in a module to walk the menubar and assign the menubar reference to all it’s children the are CustomMenuItems:

Sub RegisterWithChildren(Extends Bar as Menubar)

   Dim BarRef as New WeakRef(Bar), Item as MenuItem
   Dim ub as Integer =Bar.Count -1

   For i as Integer = 0 to ub
       Item = Bar.Item(i)
       If Item.Count > 0 then
           Register(Item, BarRef)
       ElseIf Item IsA CustomMenuItem Then
		        CustomMenuItem(item).ParentBar = BarRef
       End if
   Next
End Sub
//----------------------
Private Sub Register(Menu as menuitem, Bar as WeakRef)

   Dim ub as Integer = Menu.Count -1, Item as MenuItem
   For i as Integer = 0 to ub
	  Item = Menu.Item(i)
	  If Item.Count > 0 then
		 Register(Item, Bar)
	  ElseIf Item isA CustomMenuItem Then
		 CustomMenuItem(item).ParentBar = Bar
      End If
    Next		  
	End Sub
	//---------------------

Then to make make it easier to retrieve the MenuItem and clean up in the MenuHandler I created another extension method:

Function SelectedItem(Extends Bar as MenuBar) As MenuItem
   If Bar.Tag isA MenuItem Then 
       Dim Item as MenuItem = bar.Tag
       Bar.Tag = NIL
       Return Item
   End if
End Function

Yes a bit hacky… but IMO it is more RADish …

As i said I wish Xojo Inc would just always pass in the selected MenuItem into MenuHandlers defined in the IDE, but I know it’s unlikely to happen!

Probably a lot info that no one really needs or will use, but that is what i did.

-Karen