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"> <tabs> <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"/> </group> </tab> </tabs> </ribbon> </customUI>
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
"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
- May be 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
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?
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"
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 Sub HandleOnAction(Control As IRibbonControl) End Sub
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.