XML Callbacks: Adding Functionality to the Office Ribbon

To unlock the full potential of the Microsoft Office ribbon, you need to understand the role of callback functions. This quick intro will get you started.

XML Callbacks: Adding Functionality to the Office Ribbon

The Office Ribbon interface is defined using XML.

However, XML is a markup language (and an extensible one at that).  As such, you don't embed executable code within the XML.  Also, once you've loaded the XML for a ribbon, you can't change or reload it without restarting the entire Office application.

So, if you can't embed code in your ribbon XML...and you can't modify the ribbon XML once it's been loaded...how can you make a dynamic ribbon interface that can update itself while the application is running?  And how can you run custom code instead of just re-arranging built-in commands?

Callback functions, that's how.

What Are Callback Functions?

Let's start with this introduction from Microsoft (the "Fluent UI" is Microsoft's official term for the ribbon interface):

You specify callbacks to update properties and perform actions from your Fluent UI at run time. For example, to specify an action that occurs when the user clicks a button on the Ribbon, you must supply the onAction callback function for the button.

Further down that same page, Microsoft offers an interesting example.  Normally, each button in a ribbon toolbar would get its own callback function.  However, this Microsoft sample demonstrates how you can take advantage of the Tag attribute of a ribbon button to reduce the number of callback functions you need to create and maintain:

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
  <ribbon startFromScratch="false">
      <tab id="DemoTab" label="LoadCustomUI Demo">
        <group id="loadFormsGroup" label="Load Forms">
          <button id="loadForm2Button" label="Load Form2" 
            onAction="HandleOnAction" tag="Form2"/>
          <button id="loadForm1Button" label="Load Form1" 
            onAction="HandleOnAction" tag="Form1"/>

Assuming the XML above is saved to a variable named xml, we can load this ribbon at startup using the LoadCustomUI method of the Access application object:

Application.LoadCustomUI "FormNames", xml

The string "HandleOnAction" in the XML above is the name of a callback function.  Setting the onAction attribute to "HandleOnAction" requires that we define a public Sub with that name in our VBA code.  

Here's the sample code from the same article:

Public Sub HandleOnAction(control As IRibbonControl)
    ' Load the specified form, and set its
    ' RibbonName property so that it displays
    ' the custom UI.
    DoCmd.OpenForm control.Tag
    Forms(control.Tag).RibbonName = "FormNames"
End Sub

Callback Function Signatures

"Signature" is a term that refers to the declaration line of a function or subroutine.

For example, in the HandleOnAction subroutine above, the signature is this line:

Public Sub HandleOnAction(control As IRibbonControl)

Callback functions in the Office ribbon XML must have a signature that corresponds with the specific callback.  For example, a function that corresponds with a button's onAction callback must have the following attributes:

  • Must be placed in a Standard module, not a class module
  • Cannot be marked Private
  • May be a Function or a Sub (though you should use a Sub to make the intent clear, as any value returned from a function will be ignored by the ribbon)
  • Must have a single parameter of type IRibbonControl

For a list of all the callbacks and their associated callback function signatures, refer to this link: How can I determine the correct signatures for each callback procedure?

Required References

To make use of callback functions, you will need a reference to the Microsoft Office 1X.0 Object Library:

  • Tools > References > "Microsoft Office 1x.0 Object Library"

Troubleshooting Problems

Missing Reference

You will receive the following compile error if you forget to add a reference to the Office object library as shown above:

Routine Marked Private

What happens if we mark the procedure Private?

Private Sub HandleOnAction(Control As IRibbonControl)
End Sub
Error that reads, "Test App cannot run the macro or callback function 'HandleOnAction'.  Make sure the macro or function exists and and takes the correct parameters."

Incorrect Function Signature

Consider the following invalid function signature:

Public Sub HandleOnAction(Control As IRibbonControl, Foo As Variant)
End Sub

If we use this incorrect function signature with the extra parameter, we will receive the following error:

'|' cannot run the macro or callback function 'onActionCallbackName'.

Make sure the macro or function exists and takes the correct parameters.
Look familiar? It's the exact same error message whether we mark our function as Private or use an unsupported parameter list.

All original code samples by Mike Wolfe are licensed under CC BY 4.0