Click here to Skip to main content
15,880,469 members
Articles / Programming Languages / C++

Ribbon with C++, Part 3: Using Buttons with Windows Ribbon Framework

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
13 Jun 2011Ms-PL10 min read 28.5K   6   1
Review of the features in the Windows Ribbon Framework with focus on the most common feature in the ribbon: Buttons

On previous posts, we have seen an introduction to the Windows Ribbon Framework and learned how to develop a ribbon enabled application.

In this post, we continue our review of the various features in the Windows Ribbon Framework. This time we will focus on the most common feature in the ribbon: Buttons.

I assume the concept of a button needs no introduction; however, there are several kinds of buttons available with the Windows Ribbon Framework and in this post, we will review them.

But first, let's dive into the details of how we use the ribbon in an application. The important parts are to define the ribbon user interface using the ribbon markup and then implement handlers for the UI we use.

The ribbon markup is composed of two main sections, Commands and Views.

Commands and Views

A command is an action that is identified by a number. It can be opening the Save-as dialog, printing the current document, closing the application, etc., everything you can do in a function call.

A view is a graphical representation of [usually several] commands. It defines the type of controls used to activate the commands, their size, order, and layout on the screen.

So using commands and views is actually just another instance of the well-known MVC design pattern, which allows us to separate business logic from presentation logic.

The basic template for all ribbon markup files looks like this:

XML
<?xml version='1.0' encoding='utf-8'?>
<Application xmlns='http://schemas.microsoft.com/windows/2009/Ribbon'>
    <Application.Commands>
        ...
    </Application.Commands>

    <Application.Views>
        <Ribbon>
            ...
        </Ribbon>
    </Application.Views>
</Application>

Commands Section

In the Commands section, we define, well, commands. Every command is defined in its own Command XML element.

Following is a common definition for a command:

XML
<Command Name="cmdButtonNew"
         Symbol="ID_CMD_NEW"
         Id="1001"
         LabelTitle="New"
         LabelDescription="New Description"
         TooltipTitle="New"
         TooltipDescription="Create a new image.">
    <Command.LargeImages>
        <Image>Res/New32.bmp</Image>
    </Command.LargeImages>
    <Command.SmallImages>
        <Image>Res/New16.bmp</Image>
    </Command.SmallImages>
</Command>

In this example, we define a command that will encapsulate the "New" button functionality.

Command Properties

The Command XML element has several properties, we divide them into categories:

Properties used for referencing the command
  • The Name attribute is used to reference this command in the markup, for example, in order to attach a button with this command.
  • The Symbol attribute represents the name of the constant that will reference this command, later, when we will write the command handler code.
  • The Id attribute is just a way to control what the constant value will be.
Properties used for defining command related resources
  • The LabelTitle attribute is used to define the label title of the command.
  • The LabelDescription attribute is used to define the label description of the command.
  • The TooltipTitle attribute is used to define the tooltip title of the command.
  • The TooltipDescription attribute is used to define the tooltip description of the command.
  • The LargeImages attribute is used to define the large image filename for the command, usually 32x32 pixels.
  • The SmallImages attribute is used to define the small image filename for the command, usually 16x16 pixels.
Setting image resources in ribbon markup

The filename defined in the markup (like in the LargeImages and SmallImages XML elements) should be a valid (relative or full) path to a filename, otherwise the Visual C++ Resource Compiler (rc.exe) will output a compilation error: “error RC2135: file not found: <filename>”.

The image file format should be BMP with 32 BPP ARGB pixel format. Many image editing programs, like Microsoft Paint, do not preserve the highest order 8-bit alpha channel when saving, thus creating only 24 bit images; the result is that the image will not appear at all.

You can use the tool convert2bmp to convert your images to the required format.

Under both image elements, you can put several image files in different sizes, the ribbon framework will choose the best size according to the current DPI setting. For us, normal users, setting two images for 32x32 and 16x16 should be enough. For more information, see "Specifying Ribbon Image Resources" on MSDN.

Views Section

The Views section defines the UI controls we want to use, and their layout in the ribbon. Following is a definition for a view which has a tab, a group, and a single button:

XML
<Application.Views>
    <Ribbon>
        <Ribbon.Tabs>
            <Tab CommandName="cmdTabMain">
                <Group CommandName="cmdGroupFileActions">
                    <Button CommandName="cmdButtonNew" />
                </Group>
            </Tab>
        </Ribbon.Tabs>
    </Ribbon>
</Application.Views>

The views syntax is somewhat self-explanatory, we use the Tab element to define a tab, a Group element to define a group, and a Button element to define a button.

The CommandName attribute is used to bind a UI element to a given command (along with its resources). In this example, our button will be bound to the predefined cmdButtonNew command element. This means our button will have the label title, tooltip, and images of this command.

clip_image001

Figure 1: Button with command resources

Handling "Button Clicked" Event

As we've seen in the previous post, handling the button click event is done by implementing the IUICommandHandler::Execute function of the IUICommandHandler instance provided by IUIApplication for the given command ID.

Following is an example where in response to the click on the "New" button, we show a message box to the screen:

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)
{
    switch (nCmdID)
    {
    case ID_CMD_NEW:
        MessageBox(GetForegroundWindow(),
                          L"New button was clicked", L"New Clicked", 0);
        break;

    }

    return S_OK;
}

A few things we should note here:

The first parameter, 0nCmdID, is the ID of the command which is bound to the button. In our case, we define this ID to be 1001 (using the Id attribute of the command).

The second parameter, verb, usually has the value UI_EXECUTIONVERB_EXECUTE. Two other values it can have are: UI_EXECUTIONVERB_PREVIEW and UI_EXECUTIONVERB_CANCELPREVIEW. These values are used with ribbon galleries.

The other parameters are not used in this context.

Other Button Types

Up until now, we have seen the most common button type, which is available to us by using the <Button> XML element.

We will now see a few more types of buttons, the main difference between the different types are their GUI representations. In fact, all of these buttons are used in the exact same way:

  • Declare them in the ribbon markup
  • Implement IUICommandHandler::Execute

Using HelpButton

The HelpButton is a button with a specific GUI. It always has the same predefined image and is always positioned in the same place in the ribbon. Also, there can be at most one help-button in the ribbon.

clip_image002

Figure 2: Help button

Note that despite its name, it doesn't have to handle any help-related functionality. That being said, it is recommended you provide help information using this button, to maintain a consistent user experience with other Windows applications.

To use a HelpButton in your application, you need to use the following ribbon markup:

XML
<Application.Views>
    <Ribbon>
        <Ribbon.HelpButton>
            <HelpButton CommandName="cmdHelpButton" />
        </Ribbon.HelpButton>
        <Ribbon.Tabs>
            ?
        </Ribbon.Tabs>
    </Ribbon>
</Application.Views>

In this case, since the HelpButton requires no resources, the command declaration is as simple as:

XML
<Command Name="cmdHelpButton" Symbol="ID_CMD_HELP">
</Command>

Using SplitButton and DropDownButton

SplitButton and DropDownButton are both ribbon UI controls which can hold several buttons and which show these buttons upon clicking on a small arrow button:

clip_image003

Figure 3: SplitButton that contains three buttons

So what is actually the difference between these two?

Well, the difference between them is that DropDownButton is in fact not a button. This means that clicking on a DropDownButton only shows the buttons list, but doesn't run any custom code.

On the other hand, SplitButton is itself a button, which you can respond to. A click on the arrow part will cause it to show the buttons list.

The common use for a DropDownButton is when you want to expose a set of items which doesn't have an obvious default option. For example, consider the “Rotate” feature in Microsoft Paint. You can rotate by 90, 180, and 270 degrees, but none of these options is an obvious default.

The common use for a SplitButton is when you want to expose a set of items which have an obvious default option. For example, consider the “Save As” button in an application that supports several file-formats but does have an obvious default save format.

The following example shows how to define both DropDownButton and SplitButton in a ribbon application:

XML
<Application.Views>
    <Ribbon>
        <Ribbon.Tabs>
            <Tab CommandName="cmdTabMain">
                <Group CommandName="cmdGroupFileActions" SizeDefinition="ThreeButtons">

                    <Button CommandName="cmdButtonNew" />

                    <DropDownButton CommandName='cmdDropDownButton'>
                        <MenuGroup Class='MajorItems'>
                            <Button CommandName='cmdButtonMoreA' />
                            <Button CommandName='cmdButtonMoreB' />
                            <Button CommandName='cmdButtonMoreC' />
                        </MenuGroup>
                    </DropDownButton>

                    <SplitButton>
                        <SplitButton.ButtonItem>
                            <Button CommandName='cmdButtonMoreB' />
                        </SplitButton.ButtonItem>
                        <SplitButton.MenuGroups>
                            <MenuGroup Class='MajorItems'>
                                <Button CommandName='cmdButtonMoreA' />
                                <Button CommandName='cmdButtonMoreB' />
                                <Button CommandName='cmdButtonMoreC' />
                            </MenuGroup>
                        </SplitButton.MenuGroups>
                    </SplitButton>

                </Group>
            </Tab>
        </Ribbon.Tabs>
    </Ribbon>
</Application.Views>

Note that in the DropDownButton XML element, you define only the buttons in the buttons list. On the other hand, the SplitButton XML element contains a definition for both the buttons list and a specific default button (defined in the SplitButton.ButtonItem element).

The commands bound to these buttons are defined and handled similarly to the commands we already saw.

CheckBox and ToggleButton

CheckBox and ToggleButton are two ribbon UI controls that represent a button with a Boolean state. The sole difference between the two is their GUI representation. The toggle button looks like a button that remains pressed until pressed again. The checkbox control looks like the familiar checkbox control we all know and love:

clip_image004

Figure 4: Toggle button and check box

Using CheckBox and ToggleButton

Following is an example of ribbon markup that defines both a ToggleButton and a CheckBox:

XML
<Application.Views>
    <Ribbon>
        <Ribbon.Tabs>
            <Tab CommandName="cmdTabMain">

                <Group CommandName="cmdGroupToggleButton" SizeDefinition="OneButton">
                    <ToggleButton CommandName="cmdToggleButton" />
                </Group>

                <Group CommandName="cmdGroupCheckBox">
                    <CheckBox CommandName="cmdCheckBox" />
                </Group>

            </Tab>
        </Ribbon.Tabs>
    </Ribbon>
</Application.Views>

What's left for us to know is how to handle the "Checked Changed" event and how to get the current value of the CheckBox / ToggleButton.

Handling the "Checked Changed" Event

Handling the "Checked Changed" event is done exactly the same way we handle the "Button Clicked" event, that is, by implementing the IUICommandHandler::Execute function.

In order to get the current Boolean value of the CheckBox, we use another function that IUIFramework provides, namely, IUIFramework::GetUICommandProperty. This GetUICommandProperty function receives three parameters. The first is the command ID we need for the property from; in our sample, it is ID_CMD_TOGGLE_BUTTON, which is defined in the auto-generated file RibbonIds.h. The second parameter is the ID of the property we want to get; in this case, it has the value UI_PKEY_BooleanValue, which is defined in UIRibbon.h (comes with Windows SDK). The last parameter is the return value of the property.

Setting the value of a property is done similarly using IUIFramework::SetUICommandProperty.

In the following example, we will see the previous concepts by handling the "Checked Changed" event of a toggle button. We get the Boolean value of the toggle button, negate it, and set it as the Enable property of the checkbox:

C++
STDMETHODIMP CCommandHandler::Execute(
    UINT nCmdID,
    UI_EXECUTIONVERB verb,
    const PROPERTYKEY* key,
    const PROPVARIANT* ppropvarValue,
    IUISimplePropertySet* pCommandExecutionProperties)
{
    HRESULT hr;
    HWND hwnd = GetForegroundWindow();

    switch (nCmdID)
    {
    case ID_CMD_TOGGLE_BUTTON:
        BOOL boolValue;
        PROPVARIANT var, varNew;

        hr = g_pFramework->GetUICommandProperty(ID_CMD_TOGGLE_BUTTON,
                                                       UI_PKEY_BooleanValue, &var); 
        if (FAILED(hr))
        {
            return hr;
        }

        hr = PropVariantToBoolean(var, &boolValue);
        if (FAILED(hr))
        {
            return hr;
        } 

        boolValue = !boolValue;
        hr = UIInitPropertyFromBoolean(UI_PKEY_Enabled, boolValue, &varNew);
        if (FAILED(hr))
        {
            return hr;
        }

        hr = g_pFramework->SetUICommandProperty(ID_CMD_CHECK_BOX, 
                    UI_PKEY_Enabled, varNew);
        if (FAILED(hr))
        {
            return hr;
        }

        break;
    }

    return S_OK;
}

The UIInitPropertyFromBoolean is just a helper function, defined in UIRibbonPropertyHelpers.h, which sets a PROPVARIANT variable with a boolean value and does a compile time check, on the way, to make sure the property is of Boolean type.

Implementing UpdateProperty

On the previous section, we saw that to set the value of properties, we can use the function IUIFramework::SetUICommandProperty. Actually, this only works for some of the properties. Other properties are set in a different way, by implementing IUICommandHandler::UpdateProperty, which we shall review now. The details of which properties can be changed using SetUICommandProperty and which can be changed only using UpdateProperty are part of the MSDN documentation of each ribbon UI control.

The second way for updating properties is a two step process:

  • First, call IUIFramework::InvalidateUICommand, passing the command ID and the property ID which should be invalidated.
  • Second, implement IUICommandHandler::UpdateProperty. This function will get as parameters the command ID and the property ID which are needed by the framework and you should return its value.

Note that the function IUICommandHandler::UpdateProperty might not get called immediately after calling IUIFramework::InvalidateUICommand. It will be called only when the ribbon framework will actually need the property value.

In the following example, we first see how to call the InvalidateUICommand function and how to implement UpdateProperty so that the text of the checkbox changes according to the state of the toggle button:

C++
STDMETHODIMP CCommandHandler::Execute(
    UINT nCmdID,
    UI_EXECUTIONVERB verb,
    const PROPERTYKEY* key,
    const PROPVARIANT* ppropvarValue,
    IUISimplePropertySet* pCommandExecutionProperties)
{
    ...

    // invalidate checkbox label
    hr = g_pFramework->InvalidateUICommand(ID_CMD_CHECK_BOX,
                                           UI_INVALIDATIONS_PROPERTY,
                                           &UI_PKEY_Label);
    ...

    return S_OK;
}

STDMETHODIMP CCommandHandler::UpdateProperty(
    UINT nCmdID,
    REFPROPERTYKEY key,
    const PROPVARIANT* ppropvarCurrentValue,
    PROPVARIANT* ppropvarNewValue)
{
    HRESULT hr = E_FAIL;

    if (key == UI_PKEY_Label)
    {
        // update the Label of the toggle button
        if (nCmdID == ID_CMD_CHECK_BOX)
        {
            BOOL boolValue;
            PROPVARIANT var;

            // get boolean value from toggle button
            hr = g_pFramework->GetUICommandProperty(ID_CMD_TOGGLE_BUTTON,
                                       UI_PKEY_BooleanValue, &var); 
            if (FAILED(hr))
            {
                return hr;
            }

            hr = PropVariantToBoolean(var, &boolValue);
            if (FAILED(hr))
            {
                return hr;
            }

            if (boolValue)
            {
                hr = UIInitPropertyFromString(UI_PKEY_Label, 
                                            L"Checkbox Disabled", 
                                            ppropvarNewValue);
            }
            else
            {
                hr = UIInitPropertyFromString(UI_PKEY_Label, 
                                            L"Checkbox Enabled",
                                            ppropvarNewValue);
            }
          }
      }

   return hr;
}

You can find the full code of the sample application used in this post here.

clip_image005

Figure 5: Sample application for this post

In this post, we have learned about the commands and views sections in the markup. We've reviewed what different types of buttons are provided by the ribbon framework, and how to use them. Finally, we saw how to get and set property values of ribbon UI controls.

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)


Written By
Software Developer (Senior) Verint
Israel Israel
Arik Poznanski is a senior software developer at Verint. He completed two B.Sc. degrees in Mathematics & Computer Science, summa cum laude, from the Technion in Israel.

Arik has extensive knowledge and experience in many Microsoft technologies, including .NET with C#, WPF, Silverlight, WinForms, Interop, COM/ATL programming, C++ Win32 programming and reverse engineering (assembly, IL).

Comments and Discussions

 
QuestionAn efficient and concise demonstration. Pin
0bitRAKE06-Apr-13 18:02
0bitRAKE06-Apr-13 18:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.