Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Ribbon with C++, Part 2: First Ribbon Enabled Application

5.00/5 (12 votes)
8 Jun 2011Ms-PL9 min read 34.4K  
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.

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 the 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:

XML
<?xml version='1.0' encoding='utf-8'?>
<Application xmlns='http://schemas.microsoft.com/windows/2009/Ribbon'>
  <Application.Commands>
    
    <Command Name="cmdButtonNew"
             Symbol="ID_CMD_NEW"
             LabelTitle="New"
             LabelDescription="New Description"
             TooltipTitle="New (Ctrl+N)"
             TooltipDescription="Create a new image.">
      <Command.LargeImages>
        <Image>Res/New32.bmp</Image>
      </Command.LargeImages>
    </Command>
    
    <Command Name="cmdButtonOpen"
             Symbol="ID_CMD_OPEN"
             LabelTitle="Open"
             LabelDescription="Open Description"
             TooltipTitle="Open (Ctrl+O)"
             TooltipDescription="Open an existing image.">
      <Command.LargeImages>
        <Image>Res/Open32.bmp</Image>
      </Command.LargeImages>
    </Command>

    <Command Name="cmdButtonSave"
             Symbol="ID_CMD_SAVE"
             LabelTitle="Save"
             LabelDescription="Save Description"
             TooltipTitle="Save (Ctrl+S)"
             TooltipDescription="Save the current image.">
      <Command.LargeImages>
        <Image>Res/Save32.bmp</Image>
      </Command.LargeImages>
    </Command>

    <Command Name="cmdButtonExit"
             Symbol="ID_CMD_EXIT"
             LabelTitle="Exit"
             LabelDescription="Exit Description"
             TooltipTitle="Exit (Ctrl+X)"
             TooltipDescription="Exit application.">
      <Command.LargeImages>
        <Image>Res/Exit32.bmp</Image>
      </Command.LargeImages>
    </Command>

    <Command Name="cmdButtonMoreA"
             Symbol="ID_CMD_MORE_A"
             LabelTitle="More A"
             LabelDescription="Sub button A">
      <Command.LargeImages>
        <Image>Res/MoreA32.bmp</Image>
      </Command.LargeImages>
    </Command>

    <Command Name="cmdButtonMoreB"
             Symbol="ID_CMD_MORE_B"
             LabelTitle="More B"
             LabelDescription="Sub button B">
      <Command.LargeImages>
        <Image>Res/MoreB32.bmp</Image>
      </Command.LargeImages>
    </Command>

    <Command Name="cmdButtonMoreC"
             Symbol="ID_CMD_MORE_C"
             LabelTitle="More C"
             LabelDescription="Sub button C">
      <Command.LargeImages>
        <Image>Res/MoreC32.bmp</Image>
      </Command.LargeImages>
    </Command>

    <Command Name="cmdTabMain"
             Symbol="ID_CMD_TAB_MAIN"
             LabelTitle="Main">
    </Command>

    <Command Name="cmdTabMore"
             Symbol="ID_CMD_TAB_MORE"
             LabelTitle="More">
    </Command>

    <Command Name="cmdGroupFileActions"
             Symbol="ID_CMD_GROUP_FILE"
             LabelTitle="File Actions">
    </Command>

    <Command Name="cmdGroupExit"
             Symbol="ID_CMD_GROUP_EXIT"
             LabelTitle="">
    </Command>

    <Command Name="cmdGroupMore"
             Symbol="ID_CMD_MORE"
             LabelTitle="">
    </Command>
  </Application.Commands>

  <Application.Views>
    <Ribbon>
      <Ribbon.Tabs>
        
        <Tab CommandName="cmdTabMain">
          
          <Group CommandName="cmdGroupFileActions" SizeDefinition="ThreeButtons">
            <Button CommandName="cmdButtonNew" />
            <Button CommandName="cmdButtonOpen" />
            <Button CommandName="cmdButtonSave" />
          </Group>
        
          <Group CommandName="cmdGroupExit" SizeDefinition="OneButton">
            <Button CommandName="cmdButtonExit" />
          </Group>
          
        </Tab>
        
        <Tab CommandName ="cmdTabMore">
          <Group CommandName="cmdGroupMore" SizeDefinition="ThreeButtons">
            <Button CommandName="cmdButtonMoreA" />
            <Button CommandName="cmdButtonMoreB" />
            <Button CommandName="cmdButtonMoreC" />
          </Group>
        </Tab>
        
      </Ribbon.Tabs>
    </Ribbon>
  </Application.Views>
</Application>

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:

Image 1

Figure 1: First tab

clip_image002

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 the Ribbon UI

In this step, we will see how to use the Ribbon Markup Compiler provided with the 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:

%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\UICC.exe

To manually compile the markup, open the Windows SDK "CMD Shell" and run:

UICC.exe RibbonMarkup.xml RibbonUI.bml /header:RibbonIDs.h /res:RibbonResource.rc

UICC does the following upon run:

  1. Validates the ribbon markup syntax.
  2. Generates a compressed binary representation of the markup along with an RC file that describes it. In our demo, these are the files RibbonUI.bml and RibbonResource.rc, respectively.
  3. 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 initializes the Windows Ribbon Framework, loads the previously compiled ribbon UI, and respond 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 Class

UIRibbonFramework is a COM class provided by the Windows Ribbon Framework, which implements the 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 initializes 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.

  • Destroy()
  • This function releases all the object references held by the Windows Ribbon Framework.

IUIApplication Interface

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.

IUICommandHandler Interface

The IUICommandHandler interface should also be implemented by our application. It is used to execute our code in response 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 the 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 the commandId parameter. This can be used to update properties that change 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:

  1. On application load, call CoCreateInstance with CLSID_UIRibbonFramework. The UIRibbonFramework class implements the IUIFramework interface.
  2. Call IUIFramework::Initialize and pass it a reference to your implementation of the IUIApplication interface along with the HWND of your application window.
  3. 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.

C++
// Here we instantiate the ribbon framework object.
CoCreateInstance(CLSID_UIRibbonFramework, 
                 NULL, 
                 CLSCTX_INPROC_SERVER, 
                 IID_PPV_ARGS(&g_pFramework));

// Next, we create the application object (IUIApplication) and call the framework 
// Initialize method, passing the application object and the host HWND 
CApplication::CreateInstance(&g_pApplication);
g_pFramework->Initialize(hWnd, g_pApplication);

// Finally, we load the binary markup. This will initiate callbacks to the  
// IUIApplication object that was provided to the framework earlier, allowing command 
// handlers to be bound to individual commands.
g_pFramework->LoadUI(GetModuleHandle(NULL), L"APPLICATION_RIBBON");

The class CApplication implements IUIApplication. The important part of it is to supply the implementation of IUICommandHandler when asked by the ribbon framework. This is done by implementing IUIApplication::OnCreateUICommand as follows:

C++
//  Called by the Ribbon framework for each command specified in markup, to allow
//  the host application to bind a command handler to that command.
STDMETHODIMP CApplication::OnCreateUICommand(
    UINT nCmdID,
    UI_COMMANDTYPE typeID,
    IUICommandHandler** ppCommandHandler)
{
    if (NULL == m_pCommandHandler)
    {
        CCommandHandler::CreateInstance(&m_pCommandHandler);
    }

    return m_pCommandHandler->QueryInterface(IID_PPV_ARGS(ppCommandHandler));
}

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:

clip_image004

Figure 3: Initializing a ribbon enabled application

Implementing Command Handlers

In order to respond to user actions on the ribbon, we must implement IUICommandHandler::Execute:

C++
// Called by the Ribbon framework when a command is executed by the user.  
// For example, when a button is pressed.
STDMETHODIMP CCommandHandler::Execute(
    UINT nCmdID,
    UI_EXECUTIONVERB verb,
    const PROPERTYKEY* key,
    const PROPVARIANT* ppropvarValue,
    IUISimplePropertySet* pCommandExecutionProperties)
{
    HWND hwnd = GetForegroundWindow();
    switch (nCmdID)
    {
    case ID_CMD_NEW:
        MessageBox(hwnd, L"New button was clicked", L"New Clicked", 0);
        break;

    case ID_CMD_EXIT:
        PostMessage(hwnd, WM_CLOSE, NULL, NULL);
        break;
    }

    return S_OK;
}

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 constant 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:

clip_image005

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:

  1. First, add the RibbonMarkup.xml file to the project.
  2. Right click the added file in Solution Explorer and select Properties, Custom Build Step.
  3. Fill the fields according to the following values:

Command line:

"%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\UICC.exe" RibbonMarkup.xml 
    $(IntDir)\RibbonUI.bml /header:RibbonIDs.h /res:RibbonResource.rc

Note, on 64 bit Operating Systems, the "%ProgramFiles% environment variable should be replaced with %ProgramW6432%. So the command line should be:

"%ProgramW6432%\Microsoft SDKs\Windows\v7.0\Bin\UICC.exe" RibbonMarkup.xml 
    $(IntDir)\RibbonUI.bml /header:RibbonIDs.h /res:RibbonResource.rc

Description: Compiling Ribbon UI markup

Outputs: RibbonUI.bml;RibbonIds.h;RibbonResource.rc

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 the ribbon markup to the 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.

clip_image006

Figure 5: First ribbon enabled application

In this post, we have seen in detail 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,
Arik Poznanski.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)