Search code examples
xmlvbams-wordribbonx

What happens to the Word session ribbon after closing and reopening a document with a custom ribbon?


I have experienced the following situation in two totally different Windows environments (home and work) on Office 365 (Word 11929.20254) and Word 2016, using a range of documents and templates which use VBA and customUI.xml to display a custom ribbon:

  1. I open any Word document (an existing one or a new blank document based on Normal.dotm).
  2. I open another Word document, which has a custom ribbon (customUI.xml); basic example below.
  3. I close the document with the custom ribbon.
  4. I reopen the document with the custom ribbon.
  5. Several things are wrong with the custom ribbon at this point (no VBA errors occur). Even if the first tab on the ribbon of the second document belongs to the custom ribbon (ie it precedes the Home tab), it will not activate. The onLoad callback does not run. This is remedied only by clicking the custom ribbon tab, at which point it springs into life (and runs onLoad), or by closing all Word documents and reopening.

If I run the above situation in Excel or PowerPoint with spreadsheet or presentation files with the exact same custom ribbon as I used in Word above, step 5 doesn't apply; the ribbon works properly, onLoad runs, and so on.

Why does the Word ribbon appear to be deficient in a way in which other Office ribbons are not? Would developing an add-in rather than VBA and customUI.xml resolve or bypass this issue?

Edit:

A bare example of a custom ribbon and associated VBA follows. Put these in the customUI.xml and VBA of a Word document and use this as the second Word document, mentioned in steps 2 to 5.

Put the ribbon and VBA below into an Excel spreadsheet then run through steps 1 to 5 above, except using Excel spreadsheets instead of Word documents. Upon reaching step 5, the ribbon seems fine, unlike in Word.

customUI.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="modRibbon.CustomUI_OnLoad">
    <ribbon>
        <tabs>
            <tab id="tabCustom" label="Custom" insertBeforeMso="TabHome">
                <group id="grpCustom1" imageMso="EditDocument" label="Custom group 1">
                    <button id="btnA" size="large" label="Aa" imageMso="EditDocument" onAction="modRibbon.btn1A_OnAction" screentip="Aa" supertip="A button."/>
                </group>
                <group id="grpCustom2" imageMso="EditBusinessCard" label="Custom group 2">
                    <button id="btnB" size="large" label="Bb" imageMso="EditBusinessCard" onAction="modRibbon.btn2B_OnAction" screentip="Bb" supertip="Another button."/>
                </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>

modRibbon VBA module:

'Callback for customUI.onLoad
Sub CustomUI_OnLoad(ribbon As IRibbonUI)
End Sub

'Callback for btnA_onAction
Sub btn1A_OnAction(control As IRibbonControl)
End Sub

'Callback for btnB_onAction
Sub btn2B_OnAction(control As IRibbonControl)
End Sub

Solution

  • I've come up with a solution, which has been very reliable thus far, by adding the following to ThisDocument:

    Private Sub Document_Open()
    
    Application.OnTime Now, "modRibbon.ReloadRibbon"
    
    End Sub
    

    and adding the following to the modRibbon VBA module (in OP):

    Sub ReloadRibbon()
    
    With Application.CommandBars
        .Add "barRibbonRefresher", msoBarTop, False, True 'Adds a dummy bar
        .Item("barRibbonRefresher").Delete 'Deletes the dummy bar above, which appears to force the custom UI, including the custom ribbon, to (re)load
    End With
    
    End Sub
    

    This solution:

    • fixes the problem I outlined in OP; because
    • it allows the user to essentially reload the ribbon at whim via VBA; and
    • callbacks continue to work as normal after the ribbon reloads in this way.

    It's important to note that:

    • the fix kicks in when deleting the dummy bar, not when adding it;
    • simply sticking the With block in Document_Open() won't suffice; OnTime must be used;
    • the OnLoad callback runs when the ribbon reloads, exactly as it does when opening the document.

    I assume there is something special about CommandBars, and in particular the .Delete method, which shocks the ribbon (back) into life, which would be a tad ironic given that the ribbon is supposed to replace CommandBars, not be dependent on them for tricks to function properly.

    Side note: This could also provide a simpler way of restoring the ribbon when its pointer is lost, a fairly common occurrence when VBA encounters errors while executing ribbon methods. A current solution (which also applies to Word) involves storing the pointer somewhere (such as in ThisDocument.Variables) then restoring it via CopyMemory. The solution above may be able to simply create the ribbon anew and thus dispense with any such error.