MainMenu manipulation library

agraham

Expert
Licensed User
Longtime User
Form main menus are somewhat limited in Basic4ppc in that they may only be constructed in the IDE Designer and their structure is fixed at runtime.This library allows the manipulation of existing main menus and the creation of entirely new ones at runtime. As well as Click events it also provides Popup events for menu items and these events may be attached to existing or new menu items. Besides allowing the menu structure of a form to be altered this library also offers the ability to store complete menu structures and assign them to a form as required.

This library was inspired by a wishlist item posted by caesar (Michael).

As usual demo, help file, dll and source code for merging are provided. This library will work on .NET 1.0/1.1 or later on both desktop and device and in the IDE and both legacy or optimised compiled applications.

Edit :- Version 1.1 posted. See post #17 for details.
 

Attachments

  • MainMenu1.1.zip
    16.7 KB · Views: 152
Last edited:

derez

Expert
Licensed User
Longtime User
Menu

So you are going to write a new library for every wish ?

Looks useful, I'll start learning it.
In the help file, the first "GetMainMenu" is meant to be SetMainMenu...

edit: This post gave me another star, so thank you twice :)
 

agraham

Expert
Licensed User
Longtime User
In the help file, the first "GetMainMenu" is meant to be SetMainMenu...
I'm afraid that I can't see anything wrong with that, unless I am missing something obvious! Maybe its a languange nuance. That methods "gets", that is fetches, the MainMenu from the Form specified and "sets" the value in the MainMenu to what has been "got" (or "gotten" - US :)). Similarly NewMainMenu "sets" the present MainMenu to a new empty one.

EDIT :- I think I may now see what you mean. I originally thought that you meant there was an error in the help file, what you actually meant was that the method itself is misnamed - which I admit it probably is as the direction of the "get" is opposite to that which it usually indicates. Ah well ....!
 
Last edited:

mjcoon

Well-Known Member
Licensed User
I've posted an updated help in the first post that may explain this library a little more clearly than the original help does.

I expect so, but I am still struggling. (My wisdom is going along with my wisdom teeth, perhaps!)

My problem is understanding how the click or pop-up events (which must be mutually exclusive for any given item) are linked to the user's action. Presumably the event Subs are named after the object that is the subject of the New1() method. But that object can be used (if I follow right) to spawn any number of items that can be added to a main menu.

Can Sender.Text be used, as for other potentially ambiguous events, to discover which actual menu item was chosen? If so it would be useful to mention this in the Help. If not, how is it done?

(Of course Sender.Text would require that the leaf nodes in the menu tree all have distinct texts, even if they are theoretically distinguishable by higher levels in the tree. I appreciate that using Sender.Name or SenderFullName.Name would be better, but NewMenuItem() only offers a Text property, not a Name.)

BTW I stumbled upon this library thread after posting my wishlist item http://www.b4x.com/forum/basic4ppc-wishlist/6298-move-menus-form-form.html#post36868

Mike.
 

agraham

Expert
Licensed User
Longtime User
Sender will only give you the name of the Basic4ppc MenuItem object, not the .NET menu item that was clicked or popped up. When a MenuItem event occurs its Text property contains the text of the menu item in question.
From the demo
B4X:
Sub MenuItem1_Click
   TextBox1.Text = "Click : " & [B]MenuItem1.Text [/B]& " : " & MenuItem1.Checked
End Sub
In the Compact Framework there is no way of identifying individual menu items except by their Text property. They do have a Parent property, not exposed by the library, that is itself a menu item so you could look at the Text property of this with the Door library
B4X:
Sub MenuItem1_Popup
   Obj1.FromLibrary("NewMenu.MenuItem1", "mitem", B4PObject(2))   
   Obj1.Value = Obj1.GetProperty("Parent") ' a MainMenu or MenuItem object
   parent = obj1.RunMethod("ToString")
   If StrIndexOf(parent, "MenuItem", 0) < 0 Then
      parent = "Root" ' a MainMenu doesn't have a Text property
   Else
      parent = Obj1.GetProperty("Text")
   End If   
   TextBox1.Text = parent 
End Sub
 

mjcoon

Well-Known Member
Licensed User
From the demo
B4X:
Sub MenuItem1_Click
   TextBox1.Text = "Click : " & [B]MenuItem1.Text [/B]& " : " & MenuItem1.Checked
End Sub

Thanks for that Andrew. I hadn't even noticed that you provided a demo, let alone tried it out. But now I have I still have puzzlement. Originally I could not get the demo to breakpoint on that Sub you cited. Then I spotted the difference between "Click:" and "Click :"! So I added SenderFullName and discovered that there is a name associated with menu items but it is always that of the originating object menuitem1!

There is a lurking problem behind the use of text rather than name: localisation. Not that I'm going multi-lingual, but it would make identifying menu items that much more complicated.

In the Compact Framework there is no way of identifying individual menu items except by their Text property. They do have a Parent property, not exposed by the library, that is itself a menu item so you could look at the Text property of this with the Door library.

I still have not plucked up courage to try that Door. (Must be anticipating "a maze of twisty passages; all alike" - even though I never went into any Dungeons.)

On the other hand, given I originally wanted to propagate a menu built with the Designer to multiple forms, so I have started experimenting with that. Such menu items of course have names as well as text, and these can be disclosed with Sender. Furthermore SenderFullName shows the module too. But the module remains that in which the menu originated; it is not modified by the copying (not that that is very surprising; are they multiple references to the same structure?). Similarly the click event Sub remains that one that was established in the Designer, in the original module. However I should be able to get around both of those problems (which together would require some tricky use of globals to discover what the environment of the menu click was) by resetting the click event Sub of each cloned menu to a Sub in the corresponding module, and hence inherently aware of the environment. What fun!

Mike.
 

agraham

Expert
Licensed User
Longtime User
So I added SenderFullName and discovered that there is a name associated with menu items but it is always that of the originating object menuitem1!
That's what I told you in the first line of my post above!

There is a lurking problem behind the use of text rather than name: localisation. Not that I'm going multi-lingual, but it would make identifying menu items that much more complicated.
I can't disagree but it's a limitation of the Compact Framework. MenuItem objects in the full .NET Framework have a Tag property that can be used to uniquely label them. However the library is intended for use on both desktop and device and so is lower common denominator. You can always use the Door library to access the Tag property on the desktop if necessary.
 

agraham

Expert
Licensed User
Longtime User
by resetting the click event Sub of each cloned menu to a Sub in the corresponding module
Just noticed this. Not a good idea! Basic4ppc doesn't cope with removing events from controls so you will probably end up with multiple event sub invocations. You should do something like a CallSub from the event Sub to the code you want to run and get the Sub name from a global, or do a Select.
 

mjcoon

Well-Known Member
Licensed User
That's what I told you in the first line of my post above!

So you did!:signOops:

Just noticed this. Not a good idea! Basic4ppc doesn't cope with removing events from controls so you will probably end up with multiple event sub invocations. You should do something like a CallSub from the event Sub to the code you want to run and get the Sub name from a global, or do a Select.

Ah, I guess that's an unstated hidden limitation of AddEvent(). I had expected to get a slap for calling the menus "cloned" because there is no "Clone()" method in MainMenu and I suspect that each item/structure has to be built separately or else will merely be multiple references to the same structure.

Oh, well, I guess that the use of CallSub with (yet more) Public Globals is the only way to get what I want.

An alternative I have been toying with is to use panels on one form so that they can share the same menus. But the Designer is not good at dealing with that, and it makes dividing up code into modules inconvenient too.

Mike.
 

mjcoon

Well-Known Member
Licensed User
Oh, well, I guess that the use of CallSub with (yet more) Public Globals is the only way to get what I want.

And it was all going so well... :sign0161:

I was as usual working on the PC IDE until I got what I wanted and moved to my PDA since Andrew assured us that MainMenu.dll will work just as well, including under the two IDEs.

What I wanted, and got on PC, was a menu structure built in Designer on an arbitrary form amongst at least a half-dozen, read that menu into the MainMenu.dll cache, and then move it to each other form as they are invoked. The menu is a list of the forms to be switched to. Thus the menu always looks the same and if I want to introduce a new form I only have to do it in the single arbitrary version via Designer.

(Actually I got a bit ambitious, though I don't think that was my downfall. Some of the forms have additional menus specific to their function. I retain these using the cache mechanism in MainMenu.dll, and re-instate them during the menu switching process. This works on PC.)

Since there are a lot of "leaf" entries in this menu I use AddEvent() in a loop to connect all those leaf entries to a single event Sub. Strangely (on the PC) even when the menu is moved to other forms this Sub remains the capture route for menu activation in all forms. (Not an event for the MenuItem object; I haven't used AddClickEvent ().) But the Sub that does the menu switching knows which is the current form/module and can do the CallSub() as we discussed.

The code that works on the PC but fails at the last line (#86) cited is as follows:
B4X:
   MainMenuObj.GetMainMenu("Progress.ProgressForm")
   MenuItemObj.ControlRef = MainMenuObj.GetMenuItem(0)
   MainMenuObj.SaveMenuItem(MenuItemObj.ControlRef, 0)
   MainMenuObj.NewMainMenu
   MenuItemObj.ControlRef = MainMenuObj.SavedMenuItem(0)
   MainMenuObj.AddMenuItem(MenuItemObj.ControlRef)
I don't think that the last four lines are really necessary, so I tried commenting them out and can get past that failure point. However I then get a failure later, in the same way, the next time I attempt an AddMenuItem().

This is by way of putting down a marker; I can do more experiments such as trimming down Andrew's demo code and then bolting on my sequences.

Mike.
 

mjcoon

Well-Known Member
Licensed User
... I can do more experiments such as trimming down Andrew's demo code and then bolting on my sequences.

I've done all that, and had a frustrating time of it! Almost got to the point where the same code worked on PC and PDA, but...

Now I'm stumped because I seem prevented from using any version of "Sender" which is crucial to using a single event Sub for multiple menu entries in multiple forms. The "InvalidCastException" error message is attached. Unfortunately it also fails on the PC now with a similar and superficially more explicit but equally baffling "Unable to Cast" message (also attached). Under the PC IDE it runs fine, though without doing any more than showing the MsgBox outputs!

I have made a simple exhibition of the problem in the attached derivative of Andrew's MainMenu.dll demo code. It shows the menu OK but when you click the event Sub is called but fails at any variety of Sender as above.
 

Attachments

  • MainMenu.zip
    1.8 KB · Views: 13
  • InvalidCastException.jpg
    InvalidCastException.jpg
    28.9 KB · Views: 10
  • Unable to Cast.JPG
    Unable to Cast.JPG
    14.9 KB · Views: 8

agraham

Expert
Licensed User
Longtime User
OK got it.

This doesn't work when compiled for reasons that I won't try to explain why as it concerns how the compiler does things compared to the IDE.

senderText = Control(SenderFullName).Text




This is the intended way to get you the text of the MenuItem that was clicked.

senderText = MenuItem2.Text
 

agraham

Expert
Licensed User
Longtime User
For completeness I should also note that if you really do require runtime determination of a control (which you don't appear to) then the new v6.90 syntax for controls works when compiled so I would avoid using the old deprecated "Control(something)" syntax.

senderText = Menuitem(SenderFullName).Text

In fact the old syntax will still work when compiled if you provide the control type but I would avoid it and use the new way.

senderText = Control(SenderFullName, "MenuItem").Text
 

berndgoedecke

Active Member
Licensed User
Longtime User
MainMenu and FormExDesktop

Hello Andew,
I think you have a lot of work with Android Libaries but I have still another question concerning your B4P library MainMenu.
Is this library compatibel with Menu of a FormExDesktop Form and how to code it?

Best Regards

Bernd Goedecke
 

berndgoedecke

Active Member
Licensed User
Longtime User
I've tried it already with no success, but now i'll try it again more meticulously.

Best Regards

Bernd
 
Top