Documents can customize ribbons as well

Andrew Whitechapel was blogging about IRibbonExtensibility and how it can be used in AddIns to customize a ribbon in Office 12. I am not sure how many people have noticed this but VSTO v3 June CTP document customizations can also customize ribbons and we are going to see how. Interestingly enough, there is at least one person who noticed this was possible and left a comment on Eric's blog.

What I am about to discuss is pre-release state of the product and things might still change between now and when we actually ship. So be warned and let's get started.

As the commenter on the Eric's blog noticed Document level customizations projects do not support Ribbon Support item templates but Application level AddIn projects do. Let's use this template as a start. To do this let's create a Word 12 Document project first and then add Word AddIn project to the solution, right click on the Addin project item and choose Add New Item. Next select Ribbon Support template, name it MyRibbon.cs and click OK. You will notice that 2 files are added to the project - MyRibbon.cs and MyRibbon.xml. The XML file looks like this:

<customUI xmlns="https://schemas.microsoft.com/office/2006/01/customui" onLoad="OnLoad">

  <ribbon startFromScratch="false">

    <tabs>

      <tab id="VSTO.Tab" label="My Tab" visible="1">

        <group id="VSTO.Group" label="Hello World Group" visible="1">

          <toggleButton id="toggleButton"

                        label="Hello World"

                        screentip="Hello World Screentip"

                        onAction="OnToggleButton" />

        </group>

      </tab>

    </tabs>

  </ribbon>

</customUI>

Now, select the 2 Ribbon files and copy&paste those into your word document project. Delete the Word AddIn and Word AddIn Setup projects from the solution since they are no longer needed. Open MyRibbon.cs file, change the namespace and remove the extension of ThisApplication class. Something like this:

namespace WordAddin1

{

    #region service request implementation

    public partial class ThisApplication

    {

        protected override object RequestRibbonService()

        {

            return new MyRibbon();

        }

    }

    #endregion

namespace WordDocument1

{

Next, let’s modify the constructor on MyRibbon class to listen to ServiceRequest event and react appropriately when IRibbonExtensibility service is requested:

using Microsoft.Office.Tools;

....

public MyRibbon()

{

    Globals.ThisDocument.ServiceRequest += ThisDocument_ServiceRequest;

}

void ThisDocument_ServiceRequest(object sender, ServiceRequestEventArgs e)

{

    if (e.Guid == typeof(Office.IRibbonExtensibility).GUID)

        e.Service = this;

}

Now, all that is left to do is just instantiate MyRibbon object on document start up. Change ThisDocument_Startup method in ThisDocument.cs file:

MyRibbon r;

private void ThisDocument_Startup(object sender, System.EventArgs e)

{

    r = new MyRibbon();

}

Press F5 and watch .NET Add-In Tab appear in the ribbon!

Hmmm, there is still one little problem. If you have “Show add-in user interface errors” option checked in Word’s Advanced Options, you will notice an error message saying “The callback function “OnLoad” was not found by GetIDsofNames()”. This simply means that Office was not able to find OnLoad callback method as it was specified in the ribbon XML file. I do not want to bother you with the exact details of what is going on under the hood – but this issue will be fixed when Office 2007 ships. Meanwhile here is a workaround. Replace the declaration of MyRibbon class as follows:

[ComVisible(true)]

public class MyRibbon : Office.IRibbonExtensibility

[ComImport()]

[Guid("000C0396-0000-0000-C000-000000000046")]

[InterfaceType(ComInterfaceType.InterfaceIsDual)]

public interface IMyRibbonExtensibility

{

    [DispId(1)]

    string GetCustomUI(string RibbonID);

    [DispId(2)]

    void OnLoad(Office.IRibbonUI ribbonUI);

    [DispId(3)]

    void OnToggleButton(Office.IRibbonControl control, bool isPressed);

}

[ComVisible(true)]

public class MyRibbon : IMyRibbonExtensibility

The change is to derive the Ribbon class not from the standard Office’s IRibbonExtensibility interface, but from a custom defined IMyRibbonExtensibility interface. The latter is almost identical to IRibbonExtensibility (most importantly it shares the same Guid attribute), but also has all the callback methods (OnLoad and OnToggleButton) defined on it. F5 now and see your ribbon fully functional. In case you want to add more callbacks from the ribbon remember to add these new callbacks to IMyRibbonExtensibility interface first.

[Update 06/06] I should add that the technique of overriding somebody else's interfaces is deemed dangerous. I am presenting it here only for the sake of getting you to see how doc-level ribbons work in Office 12 Beta2. Please throw this code away once final bits of Office 12 are released and underlying bug is fixed.

Happy VSTOing.