QueryStorm apps can define their own ribbons. This goes for workbook apps as well as extension apps. Adding a ribbon can make it easier for users to interact with your app (e.g. invoke actions, display status messages).
Here's an example of a ribbon defined by a workbook app:
Defining a ribbon
To define a ribbon, add a "Ribbon class" from the project's context menu:
The scaffolded code provides a sample implementation of the
To implement the
IRibbon interface, the scaffolded class defines two members:
GetRibbonUI()method that describes the ribbon as an XML string
ControlUpdateRequiredevent that notifies Excel when it should update a particular ribbon control
The format of the XML returned by the
GetRibbonUI method is described in the official Microsoft documentation here.
If the ribbon class is correctly defined and the XML is structured correctly, all that is required for the ribbon to show up in Excel is to build the project.
Handling ribbon events
When a user clicks on a ribbon button, the ribbon class can handle the event by defining a method and specifying the name of that method as the value of the
The event handler method should accept a single string argument which will contain the id of the button that was clicked.
Binding ribbon controls
Control properties (e.g.
image) can be set in the XML string returned by the
GetRibbonUI method. In this case, the values are fixed and don't change over time.
Control properties can also be dynamic and change over time. To achieve this, they should be bound to properties of the ribbon class.
For example, a button's
Enabled property can be bound to a property on the ribbon class like so:
Control properties can also bind to methods. The method should accept a single string parameter (the id of the control) and return the appropriate type of result (e.g. a boolean for
getVisible, a string for
1 2 3 4
Updating a property
If the ribbon class implements
INotifyPropertyChanged the controls in the ribbon will listen to the
PropertyChanged event and update bound controls accordingly whenever the event is fired.
Updating a control
To update all properties of a particular control, you must call the
InvalidateControl method on the ribbon class, passing in the control id. This will fire the
ControlUpdateRequired event causing the control to be updated.
Bindings are currently only supported for the most commonly used properties (
getPressed). This limitation is temporary and we will be adding support for other properties in the future, as requested by users. This limitation only applies to binding properties (i.e. properties that start with "get").
In order to do useful work, the ribbon usually needs to access other objects in the app. To do so, it can request them using constructor injection.
For example, if the ribbon needs access to the
App and the
IWorkbookAccessor, it should define a constructor that accepts these two dependencies as parameters:
1 2 3 4
It can then store those dependencies inside fields and use them as needed.
Communicating with components
In workbook apps, code that interacts with the workbook tends to be located inside components. Instead of implementing workbook-related behavior in the ribbon, it would be better if the ribbon just forwarded calls to the components that already define the behavior.
Ribbons can communicate with components in two ways:
- by referencing them directly
- by broadcasting events via the
Referencing components directly
Components can be resolved directly fom the container just like any other dependency:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
The component instance that is injected will be the same instance created by the
App(components are registered as singletons inside the container).
The app instance defines a
HandleEvent method that allows sending an event into the app. This event will then be handled by any (and all) components that define a matching
This can be useful if we want to simplify our ribbon, so that we use the same method to handle all buttons, and that method just broadcasts an event into the app, using the name of the button as the event name.
Here's what that would look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
In the above example, the
HandleClick method handles both ribbon buttons. It calls the
app.HandleEvent method to broadcast an event into the app, passing in the id of the button as the name of the event. Components can then handle the event by defining methods that are decorated with the
1 2 3 4 5 6 7 8 9 10 11 12 13 14
The approach outlined above leaves us with a simple ribbon class that does nothing except send events into the app, while code related to the behavior of the workbook is left where it belongs - inside components.
By default, if a workbook app defines a ribbon, the ribbon will be visible as long as the workbook is open, even if a different workbook is active. It is only removed once the workbook is closed.
Most often, though, it would make sense to hide the ribbon when the workbook that owns it is not the active workbook. To do so, bind the
getVisible property of the tab to a property that checks if the owning workbook is active:
The code for this check is automatically scaffolded when you add a new ribbon class.
Images in ribbons
Ribbon controls can define an image in two ways:
- by using the
imageMsoproperty to use a built-in image
- by using the
imageproperty to reference a custom image
The easiest way to use an image on a ribbon button is to use the built-in images in Excel via the
For example, a button with
...would show up like this:
For a list of the images available in the mso gallery, search for "office image mso gallery" online, or visit one of the following resources:
Using custom images
Users can also use custom images in the ribbon. To do so, first add the image to the project (as content):
Once the file is in the
content folder, you can reference it in the
image property by name:
Most of the time buttons will have a static image and the above approach will work fine. When needed, though, control images can also be dynamic.
For dynamic images, use the
getImageMso properties. The
getImage property should bind to a property that returns a
System.Drawing.Image object. To retrieve an image from the
content folder, use the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
Merging ribbon tabs
Apps do not need to use their own ribbon tabs. Instead, app can put buttons into built-in Excel ribbon tabs, or multiple apps can put their ribbon buttons into the same tab.
Adding controls to existing tabs
To put controls into an built-in ribbon tab, the idQ property of the ribbon tab should match one of the built in tabs. For example, to put buttons into the
Data tab, use
idQ='mso:TabData'. You must also make sure to define the mso namespace in the
customUI tag and set it to
For example, the
Windy.Porter extension uses this approach to add two buttons into the
1 2 3 4 5 6 7 8 9 10 11 12 13
The result can be seen here:
When reusing an existing tab, it is advisable to not specify a label for the tab, otherwise it will override the existing label of the tab.
Sharing tabs among apps
Apps can use this same approach to share ribbon tabs to save screen space.
When two or more apps need to share a ribbon, they should all use the name XML namespace and idQ for the tab name. For example, the snippet below defines a new xml namespace called
my that points to an arbitrary made up url and uses it it in the
idQ property of the tab:
1 2 3 4 5 6 7 8 9 10 11 12
When multiple apps use the same
idQ, their ribbon tabs will be merged.