User Rating: 5 / 5

Star ActiveStar ActiveStar ActiveStar ActiveStar Active

I've uploaded a small utility (PowerBuilder source code) to CodeXchange here that you can run on the menus in your applications and create RibbonBar XML based on them.  It's intended to quick start your migration from existing menus to the RibbonBar  I'm expecting that you're going to want to tweak the output somewhat after it's generated.  however, it is a whole lot better from having to create them from scratch. 

You can see it in operation in the short video below.  I want to explain how it works, and in the process explain some assumptions I've made about how it should operate that you may want to modify.


When you select the Select Menu button the first thing it does is prompts you to select the target file for the application.  It then parses the target file to find all the PBLs that make up the application.  Note:  The code I've written assumes that all of the PBLs are in the same directory or a sub-directory of the directory that the target file is in.  If that is not the case for your application, you may want to modify the of_gettarget function to handle it, ore move the PBLs so they are where the utility expects them.

There are two reasons that it needs the list of PBLs.  The first is so that it can loop through them searching for menus and display a list of them to the user.  The second is that in order to deal with inherited menus it adds the PBLs from the application the user selected to the library list for the utility and later create an instance of the menu they selected in order to parse it.  Note:  Since the AddToLibraryList function only works in a compiled application, you must compile the application and then run that in order to use it.   It won't work if you try to run it out of the development environment.

After the list of menus is shown to the user and they select one, the utility then creates that menu dynamically in a script. It then loops through the Item array for the menu to get the menu and toolbar options defined in the menu.  It keeps looping through the Item array of the first level items and so on in order to get all of the menu and toolbar items defined in the menu.

For each menu item, it reads Text, Tag and ToolbarItemName attributes of the menu item.  It then creates an app button menu items using those values.  When you access a menu item that has a short cut defined, it is added to the Text value separated by a tab character.  So the utility looks for that tab character and if it finds it, strips the shortcut out of the Text value and instead includes it in the ShortCut attribute for the app menu item.

The utility then loops through the menu items again, this time creating categories on the RibbonBar for the top level menu items and then large RibbonBar buttons within each category for the menu items underneath each top level menu item.  Note that this essentially flattens the menu hierarchy.  I assumed that you would create panels, change some buttons to small buttons, etc. once the initial XML file was generated.

Finally, it creates two tab buttons (Info and Help) regardless of whether or not your menu included them.

A final note on a major difference between what is generated for you, for example, by the Appeon template and what this generates.  It has to do with the events that the app button menu items and ribbon buttons call.  With the template that Appeon provides, each different app button menu item and toolbar button calls a different event.  The problem I have with that is that those events are sent to the parent RibbonBar control, and where I want to respond to them is the window the RibbonBar control is in.  It seems with that approach you would need to create an event on the RibbonBar control for every menu item and ribbon button, and then have those events all call similar events on the parent window.  It seemed like a lot of tedious work to me.

What I want instead is a generic approach.  I want to only have one (or as it turns out two) events on the RibbonBar control and for the RibbonBar control to then relay the event to a number of different events on the window.  As it turns out, app button menu items require an event to be created on the RibbonBar control that take 3 arguments ( long al_handle, long al_index, long al_subindex ) whereas RibbonBar buttons require an event that only takes one ( al_handle ).  So I created two events on a custom class user object inherited from RibbonBar:

  • ue_appbutton ( long al_handle, long al_index, long al_subindex )
  • ue_tabbutton ( long al_handle )

I then call a user defined function on this user object:

  • of_triggerparentevent (long al_handle, long al_index, long al_subindex )

And then call that from the events passing the arguments received (in the case of ue_tabbutton I pass 0 for the last two arguments).  I then have the following code in the user function to figure out where the event came from, pull the tab attribute off that item, and then dynamically trigger an event on the parent window whose name is based off the tag attribute.

PowerObject po
RibbonTabButtonItem tabbutton
RibbonSmallButtonItem smallbutton
RibbonLargeButtonItem largebutton
RibbonApplicationButtonItem appbutton
RibbonMenuItem menuitem
RibbonApplicationMenu appmenu
String ls_classname
String ls_eventname
Integer li_rc

li_rc = this.GetItem ( al_handle, po )
IF li_rc = 1 THEN
  ls_classname = po.ClassName()
  Choose Case ls_classname
    Case "ribbonsmallbuttonitem"
      smallbutton = po
      ls_eventname = "ue_" + smallbutton.tag
    Case "ribbonlargebuttonitem"
      largebutton = po
      ls_eventname = "ue_" + largebutton.tag
    Case "ribbontabbuttonitem"
      tabbutton = po
      ls_eventname = "ue_" + tabbutton.tag
    Case "ribbonapplicationbuttonitem"
      appbutton = po
      appbutton.getmenu( appmenu)
      appmenu.GetMasterItem ( al_index, al_subindex, menuitem )
      ls_eventname = "ue_" + menuitem.tag
  End Choose
  parent.PostEvent ( ls_eventname )
End if

Return li_rc

That means I can drop that user object on any window and not bother with it anymore.  It means that all the app button menu items call the ue_appbutton event and all the RibbonBar buttons call the ue_tabbutton event.  The only thing I need to do to hook up the RibbonBar items to the parent window is:

  • Put text in the tag attribute of each item that indicates the event I want to call on the window
  • Create the custom event with the matching name on the parent window

What you'll see when you look at the XML this utility generates is that it only uses those two events.  If you want to use the Appeon template style approach, you'll either need to modify the XML or modify the utility to generate the event names you want.

I've put a sample of my approach in action on CodeXchange here.


Comments (3)

  1. Teresa MacDonald

Hi Bruce,

Does the ribbonbar generator only work for PB 2019 R3? When I try to run I get an error that the runtime path does not exist C:\Program Files (x86)\Appeon\Common\PowerBuilder\Runtime I changed the path in the ribbonbarmenugenerator.xml to my current path (ie. C:\Program Files (x86)\Appeon\Shared\PowerBuilder) and I get library load failed! dll name: pbvm.dll. The pbvm file installed on my machine is pbvm190.dll. What do I need to do to run the generator?

Thank you,

Your account does not have privileges to view attachments in the comment
  1. Bruce Armstrong

Did you download it recently? I originally created it with a beta version of R3. I updated it later with the GA version.

In addition, you can just recompile it yourself using whatever build you have.

Your account does not have privileges to view attachments in the comment
  1. Teresa MacDonald    Bruce Armstrong

I seen your updated video and was able to compile it myself and use it. Thank you for your reply.

Your account does not have privileges to view attachments in the comment
There are no comments posted here yet