Resting Anchor

The Anchorage

Personal website of Gregory K. Maxey, Commander USN (Retired)

Ribbon Dynamic Menu
(A Microsoft Word Help & Tip page by Gregory K. Maxey)

DISCLAIMER/TERMS OF USE

The information, illustrations and code contained in my "Microsoft Word Tips" are provided free and without risk or obligation.

Click to acces PayPal Verification Service Click to acces PayPal Verification Service

However, the work is mine. If you use it for commercial purposes or benefit from my efforts through income earned or time saved then a donation, however small, will help to ensure the continued availability of this resource.

If you would like to donate, please use the appropriate donate button to access PayPal. Thank you!

Click to donate British Pound Sterling                   Click to donate US dollars                   Click to donate EU euros

This Word Help & Microsoft Word Tips page presents "how to" steps and a functional demonstration of the "dynamicMenu" element in the Office Fluent Ribbon controls collection.

Called by some "the crown jewel of flexibility in the Ribbon," the dynamic menu works by using a getContent VBA callback to request the XML code needed to define the dynamic menu elements. As a quick review, let's look at a snippet of RibblonXML used to create a standard menu control:

VBA Script:
<menu id="Menu1" label="Simple Menu" itemSize="large" >
 <button id="Btn1" label="Run Macro 1" imageMso="Club" onAction="RibbonControl.MyBtnMacro" />
 <button id="Btn2" label="Run Macro 2" imageMso="Heart" onAction="RibbonControl.MyBtnMacro" />
 <button id="Btn3" label="Run Macro 3" imageMso="Diamond" onAction="RibbonControl.MyBtnMacro" />
 <button id="Btn4" label="Run Macro 3" imageMso="Spade" onAction="RibbonControl.MyBtnMacro" />
</menu >

In the RibbonXML shown above, all of the XML needed to define the menu control (e.g., its child elements and their attributes) is provided. The ribbon designer knows at design time that he or she needs a menu with four button commands. The designer has pre-determined the labels, images, and what action is to take place when a menu element is executed. For more information on Word 2007 Ribbon Customization see my: Customize the Ribbon (it doesn't take Rocket Science)

In designing a custom ribbon, situations can arise where the designer may not know some or part of the information required to define the menu. It's in these situations the "dynamicMenu" element is indispensable.

Demonstration:

As a practical example of dynamic menus, I created a Word template add-in "Text & Image Bank.dotm." This add-in creates the custom ribbon tab "Text & Image Bank©" shown below. The tab contains a single group "Galleries" which contains two "dynamicMenu" controls "TextBank©" and "ImageBank©."

dynamic menu 1

Site Note IconNote: The illustrations used in this tips page were created using Word 2007 and may or may not accurately depict the actual configuration of the add-in.

The dynamic menu "TextBank©" includes the the  built-in gallery control "CustomAutoTextGallery" and a custom gallery control for each Building Block category defined in the Building Block type "wdTypeCustomAutoText." The dynamic menu "ImageBank©" includes the built-in gallery control "CustomTextBoxGallery" and a custom gallery control for each Building Block category defined in the the Building Block type "wdTypeCustomTextBox." The category name defines the gallery control label. The "TextBank©" dynamic menu is shown below:

dynamic menu 2

Site Note IconNote: Word 2007/2010 each contain 35 Building Block types (or galleries). I chose the wdTypeCustomAutoText and wdTypeCustomTextBox galleries due to the type of preview that these galleries provide. For more on Building Blocks see my: Building Blocks & AutoText

Each custom gallery control listed on the dynamic menu displays a gallery item for each Building Block entry defined in that category. The Building Block entry name defines the label for each item. When a user clicks one of the gallery items, the associated Building Block entry is inserted in the document.

dynamic menu 3

The menu control "Refresh" is used when users add, delete, or edit Building Block categories or entries in the gallery.

dynamic menu 4

Site Note IconBonus Tip: New Building Block entries can be added to the template CustomAutoText or CustomTextBox galleries by selecting text (or images/shapes) in the document and pressing Alt+F3. This action opens the "Create New Building Block" dialog. The illustration below provides an example of adding a new shape object to the ImageBank©.

dynamic menu 5

The built-in gallery controls in the TextBank© and ImageBank© dynamic menus provides a truncated (TextBank) or full page (ImageBank) preview of all building blocks contained in the gallery. The illustration below shows the full page preview presented by the ImageBank© built-in gallery control. This allows users the extra flexibility of locating and inserting the appropriate builing block in their document by previewing the collection.

dynamic menu 6

It should be evident that a standard menu control is simply not up to the task of defining the menu controls illustrated above.

Creating Dynamic Menus:

Let's move on to the actual details of building the RibbonXML and VBA callbacks used to create the Text & Image Bank.dotm add-in used in the demonstration above.

The RibbonXML is pretty straight forward. The static attributes of the Tab, Group, and the two dynamicMenu controls are defined directly in the RibbonXML. The contents of the two dynamic menus is provided by the dynamicMenu attribute getContent VBA callback. I have also included a dialogBoxLauncher on the custom group that opens a simple Userform.

VBA Script:
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui"
onLoad="RibCon.OnLoad">
 <ribbon>
  <tabs>
   <tab id="TextAndImageBank" label="Text && Image Bank©" keytip="G">
    <group id="Galleries" label="Galleries">
<!-- Create and define the TextBank dynamicMenu element. -->
     <dynamicMenu id="TBDM1"
         size="large" label="TextBank©" imageMso="SignatureLineInsert"
         screentip="Select predefined text to insert in documment." 
         supertip="Make selection from the trucated preview gallery or one of the category lists."
         getContent="RibCon.GetContent"/>
<!-- Create and define the TextBank dynamicMenu element. -->
     <dynamicMenu id="TBDM2"
         size="large" label="ImageBank©" imageMso="AccountMenu" 
         screentip="Select predefined shape or image to insert in documment." 
         supertip="Make selection from the full page preview gallery or one of the category lists." 
         getContent="RibCon.GetContent"/>
<!-- Create and define the group DialogBoxLauncher. -->
     <dialogBoxLauncher &gt;
       <button id="GalleriesBtn1" onAction="RibCon.ButtonOnAction" />
     </dialogBoxLauncher> 
    </group>
   </tab>
  </tabs>
 </ribbon>
</customUI>

Site Note IconBonus Tip: Symbols can be shown on ribbon tabs or control labels by using the Unicode character reference for the symbol. (e.g., "ImageBank &#139;" in the XML shown above returns ImageBank© on the menu label. Certain symbols (e.g., the ampersand) can be entered using the format &amp;&amp;.

The VBA callbacks are a more complicated but the results are worth the effort. The VBA project contains a single Project Module "RibCon" and a Userform module "frmInfo."

VBA Script:
Option Explicit
'Declare common variables
Private myRibbon As IRibbonUI
Private oTmp As Template
Private bAval As Boolean
Private BBTs As BuildingBlockTypes
Private arrCustAT() As String
Private arrCustTB() As String
Sub OnLoad(ribbon As IRibbonUI)
'Create a ribbon object (used for invalidate method)
Set myRibbon = ribbon
lbl_Exit:
  Exit Sub
End Sub

Sub GetContent(control As IRibbonControl, ByRef content)
'Declare variables
Dim lngCats As Long
Dim BBCs As Categories
Dim BBC As Category
Dim x As Long
Dim i As Long
Dim sXML As String
  x = 0
  'Get the template object to work with (i.e., Text & Image Bank.dotm")
  For Each oTmp In Templates
    If oTmp.Name = "Text & Image Bank.dotm" Then
      bAval = True
      Exit For
    End If
  Next oTmp
  If bAval Then
    'Get name of categories in the "CustomAutoText" gallery that contain one or more building block entries.
    Set BBTs = oTmp.BuildingBlockTypes
    For lngCats = 1 To BBTs(wdTypeCustomAutoText).Categories.Count
      Set BBC = BBTs(wdTypeCustomAutoText).Categories(lngCats)
      If BBC.BuildingBlocks.Count > 0 Then
        ReDim Preserve arrCustAT(x)
        arrCustAT(x) = BBC.Name
        x = x + 1
      End If
    Next
    'Get name of categories in the "CustomTextBox" gallery that contain one or more building block entries.
    x = 0
    For lngCats = 1 To BBTs(wdTypeCustomTextBox).Categories.Count
      Set BBC = BBTs(wdTypeCustomTextBox).Categories(lngCats)
      If BBC.BuildingBlocks.Count > 0 Then
        ReDim Preserve arrCustTB(x)
        arrCustTB(x) = BBC.Name
        x = x + 1
      End If
    Next
  End If
  'Create the XML string needed to define the dynamicMenu elements.
  Select Case control.ID
    'Create XML to define the TextBank dynamicMenu elements.
    Case "TBDM1"
      sXML = "<menu xmlns=""" & _
           "http://schemas.microsoft.com/office/2006/01/customui"">" & vbCrLf
      sXML = sXML & "<gallery idMso=""CustomAutoTextGallery"" " & _
             "label=""Preview/Insert"" " & "imageMso=""SignatureLineInsert""/>" & vbCrLf
      sXML = sXML & "<menuSeparator id=""DMMS1"" " & _
             "title=""Select/Insert from category list""/>" & vbCrLf
  On Error GoTo Err_HandlerTA
      For i = 0 To UBound(arrCustAT)
        sXML = sXML & "<gallery id=""TBCatATGal" & i & """ " & _
             "columns=""1"" " & "label=""" & arrCustAT(i) & """ " & _
             "imageMso=""SignatureLineInsert"" " & _
             "tag=""" & arrCustAT(i) & """ " & _
             "getItemCount=""RibCon.GetItemCountAT"" " & _
             "getItemLabel=""RibCon.GetItemLabelAT"" " & _
             "onAction=""RibCon.GalleryOnActionAT""/>" & vbCrLf
      Next i
      sXML = sXML & _
           "<button id=""TBRefreshTA"" " & _
            "label=""Refresh"" " & _
            "imageMso=""RecurrenceEdit"" " & _
            "onAction=""RibCon.ButtonOnActionAT""/>" & vbCrLf
      content = sXML & "</menu>"
    Case "TBDM2"
      'Create XML to define the ImageBank dynamicMenu elements.
      sXML = "<menu xmlns=""" & _
           "http://schemas.microsoft.com/office/2006/01/customui"">" & vbCrLf
      sXML = sXML & "<gallery idMso=""CustomTextBoxGallery"" " & _
             "label=""Preview/Insert"" " & "imageMso=""AccountMenu""/>" & vbCrLf
      sXML = sXML & "<menuSeparator id=""DMMS1"" " & _
             "title=""Select/Insert from category list""/>" & vbCrLf
  On Error GoTo Err_HandlerTB
      For i = 0 To UBound(arrCustTB)
        sXML = sXML & "<gallery id=""TBCatTBGal" & i & """ " & _
             "columns=""1"" " & "label=""" & arrCustTB(i) & """ " & _
             "imageMso=""AccountMenu"" " & _
             "tag=""" & arrCustTB(i) & """ " & _
             "getItemCount=""RibCon.GetItemCountTB"" " & _
             "getItemLabel=""RibCon.GetItemLabelTB"" " & _
             "onAction=""RibCon.GalleryOnActionTB""/>" & vbCrLf
      Next i
      sXML = sXML & _
           "<button id=""TBRefreshTB"" " & _
            "label=""Refresh"" " & _
            "imageMso=""RecurrenceEdit"" " & _
            "onAction=""RibCon.ButtonOnActionTB""/>" & vbCrLf
      content = sXML & "</menu>"
  End Select
  Exit Sub
'Create Refresh buttons if arrays are empty (i.e., nothing currently defined)
Err_HandlerTA:
sXML = sXML & _
       "<button id=""TBRefreshTA"" " & _
        "label=""Refresh"" " & _
        "imageMso=""RecurrenceEdit"" " & _
        "onAction=""RibCon.ButtonOnActionAT""/>" & vbCrLf
content = sXML & "</menu>"
Exit Sub
Err_HandlerTB:
sXML = sXML & _
       "<button id=""TBRefreshTB"" " & _
        "label=""Refresh"" " & _
        "imageMso=""RecurrenceEdit"" " & _
        "onAction=""RibCon.ButtonOnActionTB""/>" & vbCrLf
content = sXML & "</menu>"
End Sub

Sub GetItemCountAT(control As IRibbonControl, ByRef Count)
'Get the number of items to show in the TextBank custom gallery controls.
  For Each oTmp In Templates
    If oTmp.Name = "Text & Image Bank.dotm" Then
      bAval = True
      Exit For
    End If
  Next oTmp
  If bAval Then
    Set BBTs = oTmp.BuildingBlockTypes
    Count = BBTs(wdTypeCustomAutoText).Categories(control.Tag).BuildingBlocks.Count
  End If
lbl_Exit:
  Exit Sub
End Sub

Sub GetItemLabelAT(ByVal control As IRibbonControl, Index As Integer, ByRef label)
'Get the labels to display in the TextBank custom gallery controls.
Dim oBB As BuildingBlock
  Set oBB = oTmp.BuildingBlockTypes(wdTypeCustomAutoText).Categories(control.Tag).BuildingBlocks(Index + 1)
  label = oBB.Name
lbl_Exit:
  Exit Sub
End Sub

Sub GalleryOnActionAT(control As IRibbonControl, selectedID As String, selectedIndex As Integer)
  On Error GoTo Err_Handler
  BBTs(wdTypeCustomAutoText).Categories(control.Tag).BuildingBlocks(selectedIndex + 1).Insert Selection.Range
  Exit Sub
Err_Handler:
  If Err.Number = 5941 Then
    MsgBox "The building block you selected does not exists.  Refresh your list."
  End If
End Sub

Sub ButtonOnActionAT(control As IRibbonControl)
'Refresh the TextBank dynamic menu.
  myRibbon.InvalidateControl "TBDM1"
lbl_Exit:
  Exit Sub
End Sub

Sub GetItemCountTB(control As IRibbonControl, ByRef Count)
'Get the number of items to show in the ImageBank custom gallery controls.
  For Each oTmp In Templates
    If oTmp.Name = "Text & Image Bank.dotm" Then  '*******************
      bAval = True
      Exit For
    End If
  Next oTmp
  If bAval Then
    Set BBTs = oTmp.BuildingBlockTypes
    Count = BBTs(wdTypeCustomTextBox).Categories(control.Tag).BuildingBlocks.Count
  End If
lbl_Exit:
  Exit Sub
End Sub

Sub GetItemLabelTB(ByVal control As IRibbonControl, Index As Integer, ByRef label)
'Get the labels to display in the ImageBank custom gallery controls.
Dim oBB As BuildingBlock
  Set oBB = oTmp.BuildingBlockTypes(wdTypeCustomTextBox).Categories(control.Tag).BuildingBlocks(Index + 1)
  label = oBB.Name
lbl_Exit:
  Exit Sub
End Sub

Sub GalleryOnActionTB(control As IRibbonControl, selectedID As String, selectedIndex As Integer)
  On Error GoTo Err_Handler
  BBTs(wdTypeCustomTextBox).Categories(control.Tag).BuildingBlocks(selectedIndex + 1).Insert Selection.Range
  Exit Sub
Err_Handler:
  If Err.Number = 5941 Then
    MsgBox "The building block you selected does not exists.  Refresh your list."
  End If
End Sub

Sub ButtonOnActionTB(control As IRibbonControl)
'Refresh the ImageBank dynamic menu.
  myRibbon.InvalidateControl "TBDM2"
lbl_Exit:
  Exit Sub
End Sub

Sub ButtonOnAction(control As IRibbonControl)
  Select Case control.ID
    'Call the dialogBoxLauncher custom Userform.
    Case Is = "GalleriesBtn1"
      Dim oFrm As frmInfo
      Set oFrm = New frmInfo
      oFrm.Show
      Unload oFrm
      Set oFrm = Nothing
    Case Else
      'Do nothing
  End Select
lbl_Exit:
  Exit Sub
End Sub

The tough part of the writing the getContent VBA callback is developing the sXML string. Quotation marks (") are used by the compiler to define strings and they are also needed in the XML code. What might look right to the VBA compiler can result in errors in the RibbonXML returned.

dynamic menu 7

The VB compiler is perfect content with the code above.

dynamic menu 8

The Ribbon is not!!

dynamic menu 9

The problems is a missing quote pair.

Site Note IconRembember!! To ensure you see error messages like the one shown above, select "Show add-in user interface errors" in Word Options.

dynamic menu 10

To aid you in avoiding or locating errors like these you can add temporary debugging code to your getContent VBA callback that will stop execution before the error occurs. Then you step through the code (using F8) see the resulting XML in the VBE Immediate window.

dynamic menu 11

dynamic menu 12

If you will build more that one or two dynamic menus then I suggest passing the RibbonXML attributes as arguments to a separate VBA function designed specifically to build the string.  Get this right once and your done. I've included a file in the demo pack for this page that demonstrates using this method.

That's it! I hope you have found this tips page useful and informative. You can download the Text & Image Bank© add-in and the dynamic menu XML string builder demonstration here: Dynamic Menu Demo Pack

Site Note icon For more on template add-ins and how to load them, see: Organizing Your Macros/Template Add-ins at: Installing Macros

Share

DISCLAIMER/TERMS OF USE

The information, illustrations and code contained in my "Microsoft Word Tips" are provided free and without risk or obligation.

Click to acces PayPal Verification Service Click to acces PayPal Verification Service

However, the work is mine. If you use it for commercial purposes or benefit from my efforts through income earned or time saved then a donation, however small, will help to ensure the continued availability of this resource.

If you would like to donate, please use the appropriate donate button to access PayPal. Thank you!

Click to donate British Pound Sterling                   Click to donate US dollars                   Click to donate EU euros

Search my site or the web using Google Search Engine

Google Search Logo