Pieter van der Westhuizen

Add-in Express vs. VSTO: Visual designers for Office Ribbon UI customization

Speaking for myself, whenever I get a new idea for a Microsoft Office add-in or a request from a client for a new Office enhancement, I like to start up Visual Studio and throw a quick prototype together just to see if the request or my idea is indeed possible.

Creating a Microsoft Office add-in prototype is certainly much faster with the help of Add-in Express’s visual designers. The visual designers help you to very quickly and easily build a complex Office user interface as well as customize the existing MS Office Ribbon UI (Office 2013, 2010 and 2007 are supported). These facts certainly played a big part in my decision to switch from Visual Studio Tools for Office (VSTO) to Add-in Express for Office and .net.

Although VSTO provides a way to design your Ribbon visually using the Ribbon (Visual Designer) item template, it is fairly limited in creating basic ribbon layouts; anything more complex requires hand coding the layout in XML.

VSTO item templates for creating a custom Ribbon UI

Personally I hate having to do any UI design in XML. It’s tedious and takes a tremendous amount of trial and error. It also takes away valuable time I could’ve spent working on my idea and building my application.

Creating a complex Office ribbon with VSTO

Consider the following image of a custom ribbon tab in Microsoft Outlook:

A custom ribbon tab in Microsoft Outlook

The ribbon layout above is simply not possible with the VSTO visual ribbon designer, so to accomplish this layout you would need to use a VSTO Ribbon (XML) item template. First we’ll start a new Outlook 2010 Add-in project in Visual Studio 2010.

Starting a new Outlook 2010 Add-in project in Visual Studio 2010

Add a new Ribbon (XML) item to your project and change its XML to the following:

<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="https://schemas.microsoft.com/office/2009/07/customui"
	onLoad="Ribbon_Load" loadImage="LoadImages">
  <ribbon>
    <tabs>
      <tab idMso="TabAddIns" label="Ribbon UI Demo">
        <group id="CustomUsersGroup" label="Users">
          <menu id="UserPrefsMenu" itemSize="normal" label="User Preferences">
            <button label="Post Settings" id="PostSettingsButton"></button>
            <button label="Signature Settings" id="SignatureSettingsButton"></button>
            <menuSeparator title="Privacy Settings" id="PrivacySettingsSeparator"/>
            <checkBox label="Show Real Name" id="ShowRealNameCheckbox"/>
            <menu label="E-mail" id="EmailMenu" itemSize="normal">
              <checkBox label="Always Hide" id="AlwaysHideCheckBox"/>
              <button label="Set E-mail list" id="SetEmailListButton"/>
            </menu>
            <menuSeparator title="Visual Cues" id="VisualCuesSeparator"/>
            <gallery id="MonthGallery" label="Important Message Indicator"
				columns="2" rows="4" >
              <item id="BlueDown" label="Blue Down" image="bluedown" />
              <item id="BlueLeft" label="Blue Left" image="blueleft"/>
              <item id="SilverDown" label="Silver Down" image="silverdown" />
              <item id="SilverLeft" label="Silver Left" image="silverleft"/>
              <item id="GreenDown" label="Green Down" image="greendown"/>
              <item id="GreenLeft" label="Green Left" image="greenleft"/>
              <item id="OrangeDown" label="Orange Down" image="orangedown"/>
              <item id="OrangeLeft" label="Orange Left" image="orangeleft"/>
            </gallery>
          </menu>
          <button label="Star Conversation" id="StartConversationButton"
			size="large" image="star" />
          <button label="Bookmark Conversation" id="BookmarkConversationButton"
			size="large" image="book" />
          <dialogBoxLauncher>
            <button id="groupDialogLaunch" screentip="Click here for more info"
				onAction="DialogLauncherClicked"/>
          </dialogBoxLauncher>
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

As you can see we’ve created the entire layout using xml. I want to draw your attention to the loadImage attribute of the customUI element as well as to the image attribute of the gallery items as well as the last two buttons.

To add custom images to our buttons in VSTO, we need to specify a method name as the value for the loadImage attribute. The image attribute of the buttons will then be passed as a parameter to this method, which in this case is called LoadImages.

The LoadImages method needs to be added to the Ribbon.cs class and can load the images from a resource file.

public stdole.IPictureDisp LoadImages(string imageName)
{
    stdole.IPictureDisp returnPic = null;
 
    switch (imageName)
    {
        case "bluedown":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.bluedown);
            break;
 
        case "blueleft":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.blueleft);
            break;
 
        case "greendown":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.greendown);
            break;
 
        case "greenleft":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.greenleft);
            break;
 
        case "silverdown":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.silverdown);
            break;
 
        case "silverleft":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.silverleft);
            break;
 
        case "orangedown":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.orangedown);
            break;
 
        case "orangeleft":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.orangeleft);
            break;
 
        case "star":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.star);
            break;
 
        case "book":
            returnPic = PictureConverter.IconToPictureDisp(Properties.Resources.book);
            break;
 
        default:
            break;
    }
    return returnPic;
}

I had to add a PictureConverter class to the project to make it easier to load the images from the resource file and return them in the correct format.

internal class PictureConverter : AxHost
{
    private PictureConverter() : base(String.Empty) { }
 
    static public stdole.IPictureDisp ImageToPictureDisp(Image image)
    {
        return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
    }
 
    static public stdole.IPictureDisp IconToPictureDisp(Icon icon)
    {
        return ImageToPictureDisp(icon.ToBitmap());
    }
 
    static public Image PictureDispToImage(stdole.IPictureDisp picture)
    {
        return GetPictureFromIPicture(picture);
    }
}

One last detail I want to point out in the XML, is that we have to specify a method name as the value for the onAction attribute for any button element. This tells VSTO which method to call when the user clicks on the button, in our case the Dialog box launcher button. The code for this method will look like follows:

public void DialogLauncherClicked(IRibbonControl control)
{
    MessageBox.Show("You clicked the dialog launcher button");
}

Let’s recap quickly before I show you how to do the same using the visual designers provided by Add-in Express. In order to have our (a) custom ribbon tab with a number of nested menus etc., (b) with a number of buttons with images and (c) one event for a button, we had to create the xml, add one custom class to convert images add another method to actually load the images and add a method for when the button is clicked.

Creating a complex Office ribbon with Add-in Express

First, we’ll need to create a new ADX COM Add-in in Visual Studio. Add-in Express supports Visual Studio 2005 to 2010, including the Visual Studio Express editions, and the latest Visual Studio 2012 (VB.NET, C#, C++.NET).

Creating a new Add-in Express COM Add-in project in Visual Studio

You will be guided through a few choices i.e. minimum supported Office version and supported Office applications. Add-in Express provides you with an AddinModule component which has a designer surface onto which you can add various Office components including an ADXRibbonTab.

You can add various Office components onto the designer surface of the Add-in Express AddinModule component

To use custom images for your ribbon buttons all you need to do is add a standard ImageList component to the designer and add images to it.

To design the ribbon UI, select the ADXRibbonTab component and use the built-in visual designers to add controls and adjust their properties. Add-in Express provides a context-sensitive toolbar with all the controls available for the Office Ribbon UI (Office 2013, 2010 and 2007 are supported).

Add-in Express provides a context-sensitive toolbar with all the controls available for the Office Ribbon UI.

To specify images for the buttons, you specify the images list name in the ribbon buttons’ ImageList property and select the image from a dropdown list in the buttons’ Image property.

Specifying images for the buttons

You can create an event handler for a buttons’ click event like you would for any standard .Net windows forms control, by double-clicking the event name in the Visual Studio property grid.

Create an event handler for a buttons' click event by double-clicking the event name in the Visual Studio property grid.

This will automatically create an event handler for you in which you can add your logic:

private void dialogBoxLauncher_OnAction(object sender, IRibbonControl control, bool pressed)
{
    MessageBox.Show("You clicked the ADX version of the dialog launcher button");
}

All this have been accomplished without the need to write one single line of XML or C#/VB code for that matter. Add-in Express allows you to focus on the application requirements, not wiring up code to create the UI.

Thank you for reading. Until next time, keep coding!

Last updated: 6-Feb-2013

Add-in Express vs. VSTO:

You may also be interested in:

Post a comment

Have any questions? Ask us right now!