The Anchorage
Personal website of Gregory K. Maxey, Commander USN (Retired)
The information, illustrations and code contained in my "Microsoft Word Tips" are provided free and without risk or obligation.
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!
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:
<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.
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©."
Note: 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:
Note: 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.
The menu control "Refresh" is used when users add, delete, or edit Building Block categories or entries in the gallery.
Bonus 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©.
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.
It should be evident that a standard menu control is simply not up to the task of defining the menu controls illustrated above.
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.
<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 > <button id="GalleriesBtn1" onAction="RibCon.ButtonOnAction" /> </dialogBoxLauncher> </group> </tab> </tabs> </ribbon> </customUI>
Bonus Tip: Symbols can be shown on ribbon tabs or control labels by using the Unicode character reference for the symbol. (e.g., "ImageBank ‹" in the XML shown above returns ImageBank© on the menu label. Certain symbols (e.g., the ampersand) can be entered using the format &&.
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."
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.
Rembember!! To ensure you see error messages like the one shown above, select "Show add-in user interface errors" in Word Options.
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.
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
For more on template add-ins and how to load them, see: Organizing Your Macros/Template Add-ins at: Installing Macros
The information, illustrations and code contained in my "Microsoft Word Tips" are provided free and without risk or obligation.
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!