In this post we continue our review of the Windows Ribbon Framework feature brought to us in Windows 7. For more information on the ribbon feature in general and Windows Ribbon Framework in particular, make sure you read Part 1.
Today we will see how to create a ribbon enabled application in C++ using the Windows Ribbon Framework. To show it we will build a small application with a ribbon that will contain two tabs, three groups and several buttons.
As mentioned in the previous post, there are four steps we need to follow in order to create a ribbon enabled application. Let’s dive into the details of these steps.
One last note before we start, this post requires knowledge in both Win32 and COM development. Also, if you wish to compile the sample application attached to this post, make sure you have Windows 7 SDK installed.
Step 1: Define the Ribbon UI
The first thing to do is to declare the ribbon user interface. This is done by creating an XML file that will contain the ribbon UI definition using a XAML-based syntax.
For our demo application, let’s create a file named RibbonMarkup.xml (the XML suffix is not mandatory but is still recommended), with the following content:
Ok, this may look intimidating at first glance, but it is rather easy to understand.
The ribbon UI definition file has two major sections, Commands and Views.
For now, suffice to say that the Commands section defines the different actions a user can invoke using the ribbon UI controls. Each such command is defined with its required resources (strings and images). Later, we will write command handlers in the application code.
The Views section defines the UI controls we want to use, and their layout in the ribbon. Each UI control is bound to a command and thus will trigger it when the UI control executes (e.g. button clicked).
More details on the Commands and Views sections will be reviewed on the next post.
A look on the Views section reveals that in our demo application the ribbon has two tabs; the first tab has two groups and the second tab contains one group. Each group contains some buttons. In other words:
Figure 2: Second tab
If you try to follow the steps yourself, make sure the images you use are bitmaps (BMP format) that have 32 bits per pixel (32BPP). This is a mandatory need for all images used with the Windows Ribbon Framework.
Step 2: Compile Ribbon UI
In this step we will see how to use the Ribbon Markup Compiler, provided with Windows 7 SDK, to compile the ribbon markup that we wrote in the previous step, into a compact binary representation of the ribbon UI.
The compiler executable name is UICC.exe and it can be found at:
To manually compile the markup, open the Windows SDK "CMD Shell" and run:
UICC does the following upon run:
- Validates the ribbon markup syntax.
- Generates a compressed binary representation of the markup along with a RC file that describes it.
In our demo, these are the files RibbonUI.bml and RibbonResource.rc respectively.
- Generates a C++ header file containing constants for the ribbon identifiers. This will be proved useful when we we’ll implement the command handlers.
In our demo, it is the file RibbonIDs.h.
Note that since our ribbon markup is rather light, the compiler will generate warnings about some missing markup elements. These warnings are not important at this point and we can safely ignore them.
Later, in step 4, we will setup the ribbon markup compilation as a custom build event in our project.
Step 3: Write Command Handlers Code
In this step we will see how to write code that initialize the Windows Ribbon Framework, loads the previously compiled ribbon UI and responds to events generated by the ribbon UI controls.
Before we dive into the code, let’s get to know the main players participating in a ribbon enabled application, namely: UIRibbonFramework, IUIApplication and IUICommandHandler.
UIRibbonFramework is a COM class provided by the Windows Ribbon Framework, which implements IUIFramework interface.
This class is the entry point to the Windows Ribbon Framework. It provides functions for both initializing and destroying the ribbon framework within your application as well as other functions to control the ribbon behavior.
Its most important functions are:
- Initialize(HWND topWindow, IUIApplication* application)
This function initialize the Windows Ribbon Framework within your application.
It receives an IUIApplication interface which we should implement to supply callbacks that the ribbon framework will invoke. More on this interface later.
- LoadUI (HINSTANCE instance, LPCWSTR resourceName)
This function loads the ribbon UI resource. After this function gets called, the ribbon control should be visible.
This function releases all the object references held by the Windows Ribbon Framework.
As mentioned before, IUIApplication is an interface that our application should implement. It provides three callbacks that the ribbon framework will invoke when needed.
- OnCreateUICommand(UINT32 commandId, UI_COMMANDTYPE typeId, IUICommandHandler** commandHandler)
This function is called by the ribbon framework when a command, defined in the ribbon markup, should bind to a command handler. The IUICommandHandler is yet another interface we should implement to handle commands execution. More on this interface later.
- OnDestroyUICommand(UINT32 commandId, UI_COMMANDTYPE typeId, IUICommandHandler* commandHandler)
This function is called by the ribbon framework when the application window gets destroyed, for each command defined in the ribbon markup.
- OnViewChanged(UINT32 viewId, UI_VIEWTYPE typeId, IUnknown* view, UI_VIEWVERB verb, INT32 uReasonCode)
This function is called by the ribbon framework when the ribbon view has changed. For example, when the ribbon is resized, created or destroyed.
The IUICommandHandler interface should also be implemented by our application. It is used to execute our code in responds to user actions on the ribbon UI controls. You can think of it as the place to write your "OnButtonClicked" method.
This interface provides two callbacks that the ribbon framework will invoke when needed.
- Execute(UINT32 commandId, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* currentValue, IUISimplePropertySet* commandExecutionProperties)
This function is called by the ribbon framework when the user performs an action on a UI control which is bound to the command identified by commandId parameter. An example for such an action is clicking a button or selecting an item in a combobox.
- UpdateProperty(UINT32 commandId, REFPROPERTYKEY key, const PROPVARIANT* currentValue, PROPVARIANT* newValue)
This function is called by the ribbon framework when it needs to know the value of a property of a UI control which is bound to the command identified by commandId parameter. This can be used to update properties that changes dynamically, like the items list in a combobox control, or the enabled state of a button.
Initializing the Ribbon
Now that we are familiar with the main players we can better understand the flow of a ribbon enabled application.
To initialize the ribbon in your application do the following:
- On application load, call CoCreateInstance with CLSID_UIRibbonFramework. The UIRibbonFramework class implements IUIFramework interface.
- Call IUIFramework::Initialize and pass it a reference to your implementation of the IUIApplication interface along with the HWND of your application window.
- Call IUIFramework::LoadUI which loads the pre-compiled resource and shows the real ribbon.
The following code shows how to initialize the ribbon framework; it should be called when the application loads. A good place would be when handling the message WM_CREATE:
Note: error handling code was removed for clarity.
The class CApplication implements IUIApplication. The important part of it is to supply implementation of IUICommandHandler when asked by the ribbon framework.
This is done by implementing IUIApplication::OnCreateUICommand as follows:
Note that our implementation of IUIApplication returns the same CCommandHandler object regardless of the command id. Since the IUICommandHandler::Execute function receives the executing command id, we can avoid creating a separate command handler class for each command.
The following diagram (from MSDN) shows the interactions between the different components when initializing a ribbon enabled application:
Figure 3: Initializing a ribbon enabled application
Implementing Command Handlers
In order to respond to the user actions on the ribbon we must implement IUICommandHandler::Execute:
In this example we respond to the "New" button click event and present a message box. We respond to the "Exit" button click event by posting the WM_CLOSE message.
You might be asking yourself who declared the ID_CMD_NEW, ID_CMD_EXIT constants (and if not, you should!), well, if you recall, in step 2, one of the products of the ribbon markup compiler was a RibbonIDs.h file. This file contains exactly those constants. The name of the constants is what we have defined in the Symbol attribute of the command element, in the ribbon markup. The value of the constant is auto-generated, but can be manually specified by using the Id attribute in the ribbon markup.
Note that it is critical to use PostMessage and not SendMessage, otherwise the application will crash when closing using this exit command.
This is a classic case of cutting the branch you sit on. The reason for the crash when using SendMessage is that the handling of WM_CLOSE invokes IUIFramework::Destroy() which releases all the COM objects used by the ribbon framework, including CCommandHandler. Since the CCommandHandler is still in use (we called SendMessage from CCommandHandler::Execute), we get a memory corruption.
The following diagram (from MSDN) shows the interactions between the different components when the user interacts with the ribbon control:
Figure 4: Ribbon framework interaction with command handler
Step 4: Compile Entire Application
There is only one addition to the normal compilation process of our Win32 application.
We need to add a custom build step that runs the ribbon markup compiler and we need to add the generated resource to the application.
To have your project automatically compile the ribbon markup xml file do the following:
- First, add the RibbonMarkup.xml file to the project.
- Right click the added file in the Solution Explorer and select Properties, Custom Build Step.
- Fill the fields according to the following values:
Note, on 64 bit operating systems the "%ProgramFiles% environment variable should be replaced with %ProgramW6432%. So the command line should be:
Description: Compiling Ribbon UI Markup
Now that we’ve done these steps we need to compile the application at least once so that the RibbonResource.rc file gets generated.
Now we add RibbonResource.rc to the project. This will cause the compiled binary representation of the ribbon UI to be embedded as a resource in the application executable.
Finally, don’t forget to add the images you used in to the ribbon markup to project folder. Make sure you use the same relative location as specified in the markup file.
Now sit back and enjoy your first ribbon enabled application.
Figure 5: First ribbon enabled application
In this post we have seen in details the necessary steps to create an application that uses the Windows Ribbon Framework.
A working demo of the code presented in this post can be found here.
That’s it for now,