Information
The topic you requested is included in another documentation set. For convenience, it's displayed below. Choose Switch to see the topic in its original location.

Migrating a Shared Add-in to a Visual Studio 2005 Tools for the Office System SE Add-in

Office 2007

Brian A. Randell, MCW Technologies, LLC

October 2006

Applies to: Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System, 2007 Microsoft Office system

Summary: The 2007 Microsoft Office system provides a new opportunity for add-in developers. Read this article to learn about issues you might have with existing shared add-ins. Learn how to migrate your add-ins to Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System and how to convert command-bar code to support the new Ribbon. (55 printed pages)

Contents

The Microsoft Office system has a rich history of supporting customization. Microsoft added support for Component Object Model (COM) add-ins (in-process DLLs) to Microsoft Office 2000. In the first release of the Microsoft .NET Framework, Microsoft provided support for creating COM add-ins for Office via an interoperability layer, often referred to as COM Interop. The first release of Microsoft Visual Studio .NET provided an extensibility project type, the Shared Add-In project, that made it easier to build your add-in (it even provided a Setup project to ease distribution). Each release of Visual Studio since, including the most current at the time this article was written, Visual Studio 2005, continues to support shared add-ins. And yes, out of the box, you can write a shared add-in using Visual Studio 2005 that will work with the 2007 Microsoft Office system.

NoteNote

The Visual Studio 2005 Express Editions do not contain the Shared Add-In template. However, you can write a shared add-in, it is just a bit more work to get going.

That said, continuing investment is being made to make it easier and less troublesome to build solutions on top of the Microsoft Office family of products. This investment is manifested as Microsoft Visual Studio Tools for the Microsoft Office System. The first release, Visual Studio Tools for Office, Version 2003, brought managed code to Microsoft Office Excel 2003 and Microsoft Office Word 2003 documents and templates. The second release, Visual Studio 2005 Tools for Office, continued the existing support of Word and Excel, plus it added support for Microsoft Office InfoPath 2003 forms and application add-in support for Microsoft Office Outlook 2003. Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System (also known as Visual Studio 2005 Tools for Office Second Edition, or VSTO 2005 SE) brings application add-in support to additional Microsoft Office host applications.

NoteNote

This article was developed and tested with the first beta release of Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System. If you are using a later release, your results may vary. To make clear what release I am using, I refer to the product throughout this article as Visual Studio 2005 Tools for Office Second Edition, or VSTO 2005 SE. I refer to the add-ins you create using the Visual Studio 2005 Tools for Office Second Edition templates as VSTO 2005 SE add-ins.

This additional support means that you should consider migrating existing shared add-ins and create new add-ins using Visual Studio 2005 Tools for Office Second Edition if it supports the host you are targeting. See Table 1 for the complete list of supported hosts for shared add-ins and Visual Studio 2005 Tools for Office Second Edition.

Table 1. Office host applications and their support for add-ins

Host

Shared Add-In

VSTO 2005 SE Add-In

Access 2003

Yes

No

Excel 2003

Yes

Yes

FrontPage 2003

Yes

No

Outlook 2003*

Yes

Yes

PowerPoint 2003

Yes

Yes

Project 2003

Yes

No

Publisher 2003

Yes

No

Visio 2003

Yes

Yes

Word 2003

Yes

Yes

Access 2007

Yes

No

Excel 2007

Yes

Yes

InfoPath 2007

No**

Yes

Outlook 2007

Yes

Yes

PowerPoint 2007

Yes

Yes

Project 2007

Yes

No

Publisher 2007

Yes

No

SharePoint Designer 2007

No**

No

Visio 2007

Yes

Yes

Word 2007

Yes

Yes

* It is also possible to create Outlook 2003 application add-ins with Visual Studio 2005 Tools for Office without using VSTO 2005 SE. Thus, there are three ways to create add-ins for Outlook 2003: shared add-ins using COM Interop, Visual Studio 2005 Tools for Office, and VSTO 2005 SE.

** The Visual Studio 2005 Shared Add-In template does not list InfoPath or SharePoint Designer as supported hosts. However, if you manually create the necessary keys in the registry, both hosts will load shared add-ins created with the template.

Now, if you are happy living the 1990s COM lifestyle and prefer unmanaged code (or even if you do not prefer it but it is what you need to use), you should be very happy to know that applications in the 2007 release of Microsoft Office are friendly to you and your add-ins. In fact, if C++ is your development language of choice, living the COM lifestyle is your best choice. However, if you are living in this new century and enjoy the power and productivity of managed development using Microsoft Visual Basic or Microsoft Visual C#, then Visual Studio 2005 Tools for Office Second Edition is where the action is.

NoteNote

In this article, when I refer to Visual Basic, I am referring to the managed Visual Basic (specifically, Visual Basic 2005). If I need to refer to the unmanaged version, I use the specific terms VBA, or Visual Basic 6.0.

To understand the benefits of using Visual Studio 2005 Tools for Office Second Edition to create managed add-ins over the shared add-in model, it is important for you to understand the basic deficiencies of the out-of-box experience with shared add-ins.

Mscoree.dll: Normally a Friend, But Sometimes a Foe

Mscoree.dll is the main entry DLL to the goodness that is the common language runtime (CLR). To ensure that the CLR is correctly loaded into a host application before your shared add-in, mscoree.dll is listed in the registry as the in-process server to load, not your managed assembly. This creates a few problems.

The first problem is that if your shared add-in (or some other one) causes an unhandled exception and crashes the host, the host will provide the user with an opportunity to stop the add-in from loading the next time. If the user chooses this option, the host lists the add-in as disabled. The problem is that the host application cannot distinguish between one shared add-in and another. It will ban mscoree.dll and thus all shared managed add-ins from the host.

The second problem is that when the CLR is initialized, it creates a default application domain and all shared add-ins loaded by the host are put into the same application domain. The problem with this is that one add-in can cause another add-in to fail. For example, one add-in could call ReleaseComObject on an object that both add-ins are using. This could happen in a case where there are two add-ins loaded into Excel, ExcelAddinOne and ExcelAddinTwo. By default, the two add-ins are put into the same default application domain. One of them is aggressive about memory management, say ExcelAddinTwo, and it calls FinalReleaseComObject on the Excel.Application reference that both add-ins are holding a reference to via a shared runtime callable wrapper (RCW). When ExcelAddinOne tries to access its reference to Excel it is a System.AccessViolationException: Attempted to read or write protected memory.

The third problem is an issue of trust. While most Office applications (in Office 2003, for example) trust add-ins out of the box (Publisher 2003 being an exception), it is possible for a security-minded user or administrator to disallow unsigned add-ins (those that don not have a valid X.509 signature). If this is done, your shared add-in will not work—even if you do sign it. Why? Once again it is mscoree.dll. Your Office application is looking to see if mscoree.dll is signed, not your managed assembly.

Making Shared Add-ins Play Nice

There are two ways to avoid the problems with shared add-ins. When possible, use Visual Studio Tools for Office. All of the problems listed earlier are eliminated. However, because there is not a version of Visual Studio Tools for Office that works with all Office hosts (see Table 1), you need to use a shared add-in if your host is not supported and you need application add-in support. If you do write a shared add-in, please use the shims discussed in the article Isolating Microsoft Office Extensions with the COM Shim Wizard Version 2.0. Using a COM shim can help you with the major issues listed earlier related to shared add-ins.

What About Visual Studio 2005 Tools for Office Second Edition?

If you use Visual Studio 2005 Tools for Office Second Edition to create an add-in for a supported host such as Excel, what does it give you? As mentioned, it eliminates the problems associated with a shared add-in and you do not need to do the extra work of using a COM shim. In addition, you get advantages such as remote deployment from network shares and HTTP endpoints, support for the new Ribbon in the 2007 Microsoft Office system, an upgrade path to the next release of Visual Studio Tools for Office, and support from Microsoft.

If you are starting to create a new add-in from scratch, and especially if you want to build it for the 2007 release of Microsoft Office, then Visual Studio 2005 Tools for Office Second Edition makes the most sense. If you already have a shared add-in that operates against an Office host, and Visual Studio 2005 Tools for Office Second Edition supports that host, migrating makes sense if you want to integrate with the Ribbon, take advantage of the benefits listed earlier, or use the new managed application add-in model.

To see how to migrate a shared add-in to Visual Studio 2005 Tools for Office Second Edition, you are going to walk through building a shared add-in using Visual Studio 2005 that runs in Microsoft Office Excel 2003. After this is complete, you run the add-in in Microsoft Office Excel 2007 to see how it behaves unmodified. Then you migrate the add-in to Excel 2007. The add-in that you create is not that interesting. Its goal is to point out the main migration areas you will have to deal with; in particular, migrating custom command-bar code to the new Ribbon.

To create a shared add-in

  1. Start Visual Studio 2005.

  2. On the File menu, point to New, and then click Project.

    NoteNote

    This article assumes that you installed Visual Studio using the default General Development settings. If you selected Visual Basic Developer, for example, the menu items lay out slightly differently. You need to adjust the instructions here for your specific environment.

  3. In the New Project dialog box, in the list of project types, find the Other ProjectTypes node and expand it. Select Extensibility in the list of project types.

  4. In the Templates pane, select Shared Add-in. Type a name (such as SharedAddInDemo) and a location for your add-in, then click OK to start the Add-in Wizard.

  5. On the Welcome to the Add-in Wizard page, click Next.

  6. On the Select a Programming Language page, select Visual C# or Visual Basic. Click Next.

  7. On the Select An Application Host page, clear all the check boxes except Microsoft Excel. Click Next.

  8. On the Enter a Name and Description page, type SharedAddInDemo and Shared Add-in Demo in the respective fields. These values are optional, but they make it easier to work with your add-in within the hosting applications. Click Next.

  9. On the Choose Add-in Options page, select only the first option.

    The first option causes your add-in to be loaded automatically when your host application loads—this makes debugging easier. For the purposes of this demonstration, that is fine. You might want to consider this option carefully for other add-ins. You will see later in this article how to manually load the add-in. The second option determines which users can interact with the add-in—either only the user who installs the add-in, or all users on the computer. For development, it is best to isolate your work to the current user. Click Next.

  10. On the Summary page, review your choices and click Finish to generate the add-in projects.

    NoteNote

    When you create the add-in project, Visual Studio actually creates two projects that are together within a solution that has the same name as your add-in. The first project is your add-in. The second project is a Setup project that you can use to deploy your add-in to other users and to test your add-in on a clean non-development computer.

The Shared Add-In template creates a class called Connect that looks something like the following code.

namespace SharedAddInDemoCS
{
  using System;
  using Extensibility;
  using System.Runtime.InteropServices;

  // Region "Read me for add-in installation and setup information."
  // Removed

  // All XML comments removed.
  [GuidAttribute("F7698139-E63C-4F10-8A9B-47B1BD042C3D"), 
   ProgId("SharedAddInDemoCS.Connect")]
  public class Connect : Object, Extensibility.IDTExtensibility2
  {
    public Connect()
    {
    }

    public void OnConnection(object application, 
      Extensibility.ext_ConnectMode connectMode, 
      object addInInst, ref System.Array custom)
    {
      applicationObject = application;
      addInInstance = addInInst;
    }

    public void OnDisconnection(Extensibility.ext_DisconnectMode 
      disconnectMode, ref System.Array custom)
    {
    }

    public void OnAddInsUpdate(ref System.Array custom)
    {
    }

    public void OnStartupComplete(ref System.Array custom)
    {
    }

    public void OnBeginShutdown(ref System.Array custom)
    {
    }
    
    private object applicationObject;
    private object addInInstance;
  }
}

After you create the add-in project, you need to add a reference to the host's correct primary interop assembly. For this sample add-in, you need to add a reference to the Microsoft Excel 11.0 Object Library. The sample you create will use the MessageBox class from Visual C#. Thus, if you are using Visual C#, a reference to the System.WindowsForms assembly is necessary.

NoteNote

While it is possible to write Visual Basic and Visual C# in a very similar way, the code you write will generally use built-in language constructs—such as the Visual Basic MsgBox and WithEvents features—instead of a .NET Framework alternative.

With the references in place, you can make the necessary code changes. This includes adding appropriate Imports (Visual Basic) or using (Visual C#) statements to reduce typing.

using Core = Microsoft.Office.Core;
using Excel = Microsoft.Office.Interop.Excel;
using System.Windows.Forms;

You need to change the default class-level variables (applicationObject and addInInstance) defined by the template to strongly typed references (Excel.Application and Core.COMAddin respectively). If you are using Visual Basic, you should change the Dim keyword to Private.

NoteNote

In the C# sample, you add an additional class-level variable called missing of the type System.Object. Because of the COM and VBA heritage of Microsoft Office, the APIs in most Office hosts support methods that accept optional parameters. C# does not support this. Thus, in the code examples, if a parameter is to be skipped, you pass the variable missing.

private Excel.Application applicationObject;
private Core.COMAddIn addInInstance;
private Object missing = Type.Missing;

Finally, you add some code to the OnConnection and OnDisconnection methods. These two methods are used to link and unlink your add-in. OnConnection is your opportunity to capture a reference to the host. OnDisconnection is where you perform your clean up (removing added UI elements and released variables).

In the OnConnection method, you need to interrogate the application object reference passed in to see if it is a reference to Excel or some other host. If it is a reference to Excel, then you store a reference to it. You then use a message box to display the host name and version information. In the OnDisconnection method, you release the two class-level variables hooked in the OnConnection method. This step is important if the code is loaded in the default application domain and the user chooses to unload the add-in without shutting down the host application. If you use a COM shim, as discussed earlier, this is unnecessary because the add-in would be loaded in its own application domain, which would be unloaded after the host calls the method and the add-in is released.

The following code blocks show all of the changes you should make. Only portions that are added or changed will be included. Changes appear in bold code format.

TipTip

This demonstration assumes that, if you are a Visual Basic programmer, you have set the Option Strict setting in your project to On (or have added the Option Strict statement to each module in your project). Although it is not required, setting the Option Strict setting to On requires a bit more code, as you will see, but it also ensures that you do not perform any unsafe type conversions. You can get by without it, but in the end, the discipline required to take advantage of this option far outweighs the difficulties it adds as you write code.

// All XML comments removed.
public void OnConnection(object application,
  Extensibility.ext_ConnectMode connectMode,
  object addInInst, ref System.Array custom)
{
  if (application is Excel.Application)  {    applicationObject = ((Excel.Application)(application));    addInInstance = ((Core.COMAddIn)(addInInst));    MessageBox.Show(addInInstance.Description + " is connected to "      + applicationObject.Name + " v." + applicationObject.Version,      "Shared Add-in",      MessageBoxButtons.OK, MessageBoxIcon.Information);  }
}

public void OnDisconnection(Extensibility.ext_DisconnectMode
  disconnectMode, ref System.Array custom)
{
  addInInstance = null;  applicationObject = null;
}

With these changes made, your add-in is ready to be tested. To debug the add-in, you need to make changes in the Debug tab of the add-in's project properties so that Visual Studio 2005 knows which host application to load.

To set up debugging for Excel

  1. In Solution Explorer, select the add-in project.

  2. On the Project menu, click SharedAddInDemo Properties.

  3. Click the Debug tab.

  4. Change the Start Action to Start External Program.

  5. Change the path to the location of Excel 2003 on your computer (typically C:\Program Files\Microsoft Office\OFFICE11\EXCEL.EXE).

  6. On the File menu, click Save.

  7. On the File menu, click Close to close the project designer.

  8. On the Debug menu, click Start Debugging to start Excel and load the add-in.

After Excel starts, you see the message box added to the OnConnection method. When you exit Excel, you are returned to Visual Studio 2005. You could write an add-in that lurks in the background and does not provide direct user interaction, but add-ins typically hook themselves into the command bars infrastructure exposed by all Office host applications to add menu commands, buttons, and possibly even an entire command bar. (Yes, an add-in can do work by responding only to a host application's events but then there really is not much to talk about because your event code generally should work the same.)

The next bit of code you write updates the add-in to add a custom menu item and menu item control to the built-in Excel menu bar. To make this work, you define three new class-level variables. MainMenuBar represents the Excel Worksheet menu bar. MenuBarItem represents the new menu item that is to be added to the Worksheet menu and MenuItem (using the WithEvents feature in Visual Basic).

private Core.CommandBar MainMenuBar;
private Core.CommandBarControl MenuBarItem;
private Core.CommandBarButton MenuItem;

Define a few helper methods to factor the code necessary to add the menu bar item and its menu item, and some code to clean up the added items so they do not stay if a user chooses to unload the add-in without exiting Excel.

private Core.CommandBarButton CreateMenuButton(
  Core.CommandBarPopup Parent, string Caption)
{
  try
  {
    Core.CommandBarControl cbc = null;
    cbc = Parent.Controls.Add(Core.MsoControlType.msoControlButton,
      missing, missing, missing, true);
    cbc.Caption = Caption;
    cbc.Visible = true;

    return (Core.CommandBarButton)(cbc);
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message, ex.Source,
      MessageBoxButtons.OK, MessageBoxIcon.Error);
    return null;
  }
}
private void InitMenuBarItems(string Caption)
{
  try
  {
    MainMenuBar = applicationObject.CommandBars["Worksheet Menu Bar"];
    MenuBarItem = (Core.CommandBarControl)(
      MainMenuBar.Controls.Add(Core.MsoControlType.msoControlPopup,
      missing, missing, missing, true));
    MenuBarItem.Caption = Caption;
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message, ex.Source,
      MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
}
private void CleanUpMenuItems()
{
  MenuItem.Delete(false);
      MenuBarItem.Delete(false);

      MenuItem = null;
      MenuBarItem = null;
      MainMenuBar = null;
    }

Next, modify the OnConnection and OnDisconnection methods to create the menu bar item when loading and to clean things up when shutting down, respectively. If you are writing Visual C# code, you need to add an additional line of code to hook up the Click event handler for the menu item. Finally, you move the message box code from the OnConnection method to the MenuItem's Click event handler.

NoteNote

Just a quick reminder that the following code blocks show only code blocks that have changes. Portions added or changed appear in bold code format.

public void OnConnection(object application,
  Extensibility.ext_ConnectMode connectMode,
  object addInInst, ref System.Array custom)
{
  try  {
    if (application is Excel.Application)
    {
      applicationObject = ((Excel.Application)(application));
      addInInstance = ((Core.COMAddIn)(addInInst));

      InitMenuBarItems("Shared");      MenuItem = CreateMenuButton(        ((Core.CommandBarPopup)(MenuBarItem)),"&Add-in Information");      MenuItem.Click +=        new Core._CommandBarButtonEvents_ClickEventHandler(          MenuItem_Click);
    }
  }  catch (Exception ex)  {    MessageBox.Show(ex.Message, ex.Source,      MessageBoxButtons.OK, MessageBoxIcon.Error);  }
}

public void OnDisconnection(Extensibility.ext_DisconnectMode
  disconnectMode, ref System.Array custom)
{
  if (applicationObject is Excel.Application)  {    CleanUpMenuItems();  }
  addInInstance = null;
  applicationObject = null;
}

private void MenuItem_Click(
  Core.CommandBarButton Ctrl, ref bool CancelDefault)
{
  MessageBox.Show(addInInstance.Description + " is connected to "
    + applicationObject.Name + " v." + applicationObject.Version,
    "Shared Add-in",
    MessageBoxButtons.OK, MessageBoxIcon.Information);
}

As mentioned, it is common for an add-in to define an entire command bar with buttons and other supported controls. The next chunk of changes you make will be to add code that creates a new command bar, AppCommandBar, and adds a button, AppButton, to it. You need to define these two objects as class-level variables.

private Core.CommandBar AppCommandBar;
private Core.CommandBarButton AppButton;

In addition, add a Click event handler for AppButton that shows the same message box used earlier (via a new helper method ShowVersionInfo). The code to create the command bar is defined in OnConnection with additional helper code defined in CreateCommandBar and CreateCreateCommandBarButton. OnDisconnection is modified to call the new CleanUpCommandBars method, which cleans up the new command bar object and supporting controls.

public void OnConnection(object application,
  Extensibility.ext_ConnectMode connectMode,
  object addInInst, ref System.Array custom)
{
  try
  {
    if (application is Excel.Application)
    {
      applicationObject = ((Excel.Application)(application));
      addInInstance = ((Core.COMAddIn)(addInInst));

      InitMenuBarItems("Shared");

      MenuItem = CreateMenuButton(
        ((Core.CommandBarPopup)(MenuBarItem)),"&Add-in Information");
      MenuItem.Click +=
        new Core._CommandBarButtonEvents_ClickEventHandler(
          MenuItem_Click);

      AppCommandBar = CreateCommandBar("Shared CommandBar");
      AppButton = CreateCommandBarButton("Add-In Information", 59,
        Core.MsoButtonStyle.msoButtonIconAndCaption, "AddInInfo",
        "Get Add-in Information");
      AppButton.Click +=
        new Core._CommandBarButtonEvents_ClickEventHandler(
          AppButton_Click);

      AppCommandBar.Visible = true;
    }
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message, ex.Source,
      MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
}

public void OnDisconnection(Extensibility.ext_DisconnectMode
  disconnectMode, ref System.Array custom)
{
  if (applicationObject is Excel.Application)
  {
    CleanUpCommandBars();
    CleanUpMenuItems();
  }
  addInInstance = null;
  applicationObject = null;
}

private void MenuItem_Click(
  Core.CommandBarButton Ctrl, ref bool CancelDefault)
{
  ShowVersionInfo();
}

private Core.CommandBar CreateCommandBar(string Name)
{
  return applicationObject.CommandBars.Add(Name,
    Core.MsoBarPosition.msoBarFloating, false, true);
}

private Core.CommandBarButton CreateCommandBarButton(
  string Caption, int FaceId, Core.MsoButtonStyle Style,
  string Tag, string ToolTipText)
{
  Core.CommandBarButton btn = (Core.CommandBarButton)
    AppCommandBar.Controls.Add(Core.MsoControlType.msoControlButton,
    missing, missing, missing, true);

  btn.Caption = Caption;
  btn.FaceId = FaceId;
  btn.Style = Core.MsoButtonStyle.msoButtonIconAndCaption;
  btn.Tag = Tag;
  btn.TooltipText = ToolTipText;

  return btn;
}

private void CleanUpCommandBars()
{
  AppCommandBar.Delete();
}

private void ShowVersionInfo()
{
  MessageBox.Show(addInInstance.Description + " is connected to "
    + applicationObject.Name + " v." + applicationObject.Version,
    "Shared Add-in",
    MessageBoxButtons.OK, MessageBoxIcon.Information);
}

private void AppButton_Click(
  Core.CommandBarButton Ctrl, ref bool CancelDefault)
{
  ShowVersionInfo();
}

At this point, the add-in supports making typical additions to the command bars infrastructure. Sometimes, however, you might want your add-in just to put a command on an existing menu or command bar. The next section of code you write does just that. You add a menu item with the caption of "Extra Extra" to the File Menu. You also add a command-bar button using the built-in 8-ball icon to the Standard command bar.

// Add as class-level variables.
private Core.CommandBarButton extraMenuItem;private Core.CommandBarButton extraStdButton;

public void OnConnection(object application,
   Extensibility.ext_ConnectMode connectMode,
   object addInInst, ref System.Array custom)
{
   try
   {
     if (application is Excel.Application)
     {
       applicationObject = ((Excel.Application)(application));
       addInInstance = ((Core.COMAddIn)(addInInst));

       InitMenuBarItems("Shared");

       MenuItem = CreateMenuButton(
         ((Core.CommandBarPopup)(MenuBarItem)),"&Add-in Information");
       MenuItem.Click +=
         new Core._CommandBarButtonEvents_ClickEventHandler(
           MenuItem_Click);

       AppCommandBar = CreateCommandBar("Shared CommandBar");
       AppButton = CreateCommandBarButton("Add-In Information", 59,
         Core.MsoButtonStyle.msoButtonIconAndCaption, "AddInInfo",
         "Get Add-in Information");
       AppButton.Click +=
         new Core._CommandBarButtonEvents_ClickEventHandler(
           AppButton_Click);

       AppCommandBar.Visible = true;

       AddExtraUIAdditions();
     }
   }
   catch (Exception ex)
   {
     MessageBox.Show(ex.Message, ex.Source,
       MessageBoxButtons.OK, MessageBoxIcon.Error);
   }
}

public void OnDisconnection(Extensibility.ext_DisconnectMode
   disconnectMode, ref System.Array custom)
{
   if (applicationObject is Excel.Application)
   {
     CleanupExtraAdditions();
     CleanUpCommandBars();
     CleanUpMenuItems();
   }
   addInInstance = null;
   applicationObject = null;
}

private void AddExtraUIAdditions(){   try   {     //  Add a menu item to the File menu.     Core.CommandBarControl fileMenu = MainMenuBar.Controls["File"];     extraMenuItem = CreateMenuButton(       (Core.CommandBarPopup)(fileMenu), "Extra Extra!");     extraMenuItem.BeginGroup = true;     extraMenuItem.FaceId = 279;     extraMenuItem.Tag = "PushPin";     extraMenuItem.TooltipText = "Get Extra Information";     //  Add a CommandBarButton to the Standard CommandBar.     Core.CommandBar stdToolbar =       applicationObject.CommandBars["Standard"];     extraStdButton = (Core.CommandBarButton)(       stdToolbar.Controls.Add(Core.MsoControlType.msoControlButton,       missing, missing, missing, true));     extraStdButton.FaceId = 1845;     extraStdButton.Style = Core.MsoButtonStyle.msoButtonIcon;     extraStdButton.TooltipText =       "CommandBarButton Added to Standard CommandBar";     extraStdButton.Tag = "EightBall";   }   catch (Exception ex)   {     MessageBox.Show(ex.Message, ex.Source,       MessageBoxButtons.OK, MessageBoxIcon.Error);   }}private void CleanupExtraAdditions(){   extraStdButton.Delete(true);   extraStdButton = null;   extraMenuItem.Delete(true);   extraMenuItem = null;}

At this point, you have written enough code to run the add-in. However, command bars support additional controls. It is also possible for an add-in to create more than one command bar depending on its domain logic. You add one last bit of code specific to the shared add-in that does just that. You add all of the additional supported controls that you have not already used to the existing custom command bar you created earlier. In addition, you write code to create three additional command bars. If you are wondering why three, well, after you run the add-in in Excel 2007, things will be clear.

// Add as class-level variables.
private Core.CommandBarComboBox extraCtlComboBox;private Core.CommandBarComboBox extraCtlDropDown;private Core.CommandBarComboBox extraCtlEdit;private Core.CommandBarPopup extraCtlPopUp;private Core.CommandBarButton extraPopUpCtlButton1;private Core.CommandBarButton extraPopUpCtlButton2;private Core.CommandBar extraCommandBar;private Core.CommandBarButton extraCtlButton;private Core.CommandBar extraCommandBar2;private Core.CommandBarButton extraCtlButton2;private Core.CommandBar extraCommandBar3;private Core.CommandBarButton extraCtlButton3;

private void AddExtraUIAdditions()
{
  try
  {
    //  Add a menu item to the File menu.
    Core.CommandBarControl fileMenu = MainMenuBar.Controls["File"];
    extraMenuItem = CreateMenuButton(
      (Core.CommandBarPopup)(fileMenu), "Extra Extra!");

    extraMenuItem.BeginGroup = true;
    extraMenuItem.FaceId = 279;
    extraMenuItem.Tag = "PushPin";
    extraMenuItem.TooltipText = "Get Extra Information";

    //  Add a CommandBarButton to the Standard CommandBar.
    Core.CommandBar stdToolbar =
      applicationObject.CommandBars["Standard"];

    extraStdButton = (Core.CommandBarButton)(
      stdToolbar.Controls.Add(Core.MsoControlType.msoControlButton,
      missing, missing, missing, true));

    extraStdButton.FaceId = 1845;
    extraStdButton.Style = Core.MsoButtonStyle.msoButtonIcon;
    extraStdButton.TooltipText =
      "CommandBarButton Added to Standard CommandBar";
    extraStdButton.Tag = "EightBall";

    // Add all existing controls to the first custom CommandBar.    extraCtlComboBox = (Core.CommandBarComboBox)(      AppCommandBar.Controls.Add(      Core.MsoControlType.msoControlComboBox,      missing, missing, missing, true));    extraCtlComboBox.AddItem("Red", missing);    extraCtlComboBox.AddItem("Green", missing);    extraCtlComboBox.AddItem("Blue", missing);    extraCtlComboBox.Caption = "Color";    extraCtlComboBox.Style = Core.MsoComboStyle.msoComboNormal;    extraCtlComboBox.Tag = "Colors";    extraCtlComboBox.TooltipText = "Select or type a color";    extraCtlComboBox.Text = "Color";    extraCtlDropDown = (Core.CommandBarComboBox)(      AppCommandBar.Controls.Add(      Core.MsoControlType.msoControlDropdown,      missing, missing, missing, true));    extraCtlDropDown.AddItem("Earth", missing);    extraCtlDropDown.AddItem("Air", missing);    extraCtlDropDown.AddItem("Fire", missing);    extraCtlDropDown.AddItem("Water", missing);    extraCtlDropDown.Caption = "Elements";    extraCtlDropDown.Style = Core.MsoComboStyle.msoComboLabel;    extraCtlDropDown.Tag = "FourElements";    extraCtlDropDown.TooltipText = "Select an element";    extraCtlEdit = (Core.CommandBarComboBox)(      AppCommandBar.Controls.Add(Core.MsoControlType.msoControlEdit,      missing, missing, missing, true));    extraCtlEdit.Caption = "Ask a question";    extraCtlEdit.Tag = "AskQuestion";    extraCtlEdit.Text = "Text Box";    extraCtlEdit.TooltipText = "Ask a question";    extraCtlPopUp = (Core.CommandBarPopup)(      AppCommandBar.Controls.Add(Core.MsoControlType.msoControlPopup,      missing, missing, missing, true));    extraCtlPopUp.Caption = "Other Options";    extraCtlPopUp.Tag = "OtherOptions";    extraCtlPopUp.TooltipText = "Click for additional controls";    extraPopUpCtlButton1 = (Core.CommandBarButton)(      extraCtlPopUp.Controls.Add(Core.MsoControlType.msoControlButton,      missing, missing, missing, true));    extraPopUpCtlButton2 = (Core.CommandBarButton)(      extraCtlPopUp.Controls.Add(Core.MsoControlType.msoControlButton,      missing, missing, missing, true));    extraPopUpCtlButton1.Caption = "Popup Button";    extraPopUpCtlButton1.FaceId = 59;    extraPopUpCtlButton1.Style =      Core.MsoButtonStyle.msoButtonIconAndCaption;    extraPopUpCtlButton2.Caption = "Another Popup Button";    extraPopUpCtlButton2.Style = Core.MsoButtonStyle.msoButtonCaption;    extraCommandBar = CreateCommandBar("Second Shared CommandBar");    extraCtlButton = (Core.CommandBarButton)(      extraCommandBar.Controls.Add(      Core.MsoControlType.msoControlButton,      missing, missing, missing, true));    extraCtlButton.Caption = "Heart";    extraCtlButton.FaceId = 481;    extraCtlButton.Style =       Core.MsoButtonStyle.msoButtonIconAndCaption;    extraCtlButton.TooltipText =      "Button added to second custom CommandBar";    extraCtlButton.Tag = "Heart";    extraCommandBar.Visible = true;    extraCommandBar2 = CreateCommandBar("Third Shared CommandBar");    extraCtlButton2 = (Core.CommandBarButton)(      extraCommandBar2.Controls.Add(      Core.MsoControlType.msoControlButton,      missing, missing, missing, true));    extraCtlButton2.Caption = "Diamond";    extraCtlButton2.FaceId = 482;    extraCtlButton2.Style =       Core.MsoButtonStyle.msoButtonIconAndCaption;    extraCtlButton2.TooltipText =      "Button added to third custom CommandBar";    extraCtlButton2.Tag = "Diamond";    extraCommandBar2.Visible = true;    extraCommandBar3 = CreateCommandBar("Fourth Shared CommandBar");    extraCtlButton3 = (Core.CommandBarButton)(      extraCommandBar3.Controls.Add(      Core.MsoControlType.msoControlButton,      missing, missing, missing, true));    extraCtlButton3.Caption = "Spade";    extraCtlButton3.FaceId = 483;    extraCtlButton3.Style =       Core.MsoButtonStyle.msoButtonIconAndCaption;    extraCtlButton3.TooltipText =      "Button added to fourth custom CommandBar";    extraCtlButton3.Tag = "Spade";    extraCommandBar3.Visible = true;
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message, ex.Source,
      MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
}

private void CleanupExtraAdditions()
{
  extraCtlButton3.Delete(true);  extraCtlButton3 = null;  extraCommandBar3.Delete();  extraCommandBar3 = null;  extraCtlButton2.Delete(true);  extraCtlButton2 = null;  extraCommandBar2.Delete();  extraCommandBar2 = null;  extraCtlButton.Delete(true);  extraCtlButton = null;  extraCommandBar.Delete();  extraCommandBar = null;  extraPopUpCtlButton2.Delete(true);  extraPopUpCtlButton2 = null;  extraPopUpCtlButton1.Delete(true);  extraPopUpCtlButton1 = null;  extraCtlPopUp.Delete(true);  extraCtlPopUp = null;  extraCtlEdit.Delete(true);  extraCtlEdit = null;  extraCtlDropDown.Delete(true);  extraCtlDropDown = null;  extraCtlComboBox.Delete(true);  extraCtlComboBox = null;

  extraStdButton.Delete(true);
  extraStdButton = null;

  extraMenuItem.Delete(true);
  extraMenuItem = null;
}

Under Excel 2003, you should see something like Figure 1 when you first start Excel and load the add-in.

Figure 1. Excel 2003 with the shared add-in loaded

Excel 2003 with the shared add-in loaded

Figure 2 shows the detail of the modified Excel 2003 File menu.

Figure 2. The modified File menu from Excel 2003

The modified File menu from Excel 2003

Excel 2007 Running a Shared Add-In

Before you set up and load the add-in in Excel 2007, start Excel 2007 and take a mental note, or even a screen shot, of what the Excel 2007 user interface (UI) looks like. When you are ready, exit Excel 2007. If the computer you are using to work through the above scenario has both Excel 2003 and Excel 2007 installed on it, you can start Excel 2007 from the Start menu and see how the add-in runs. If you are using a secondary computer, or possibly a virtual machine, the easiest way to try it out is to build the Setup program included by the Shared Add-in Wizard and run it on the secondary computer. After the add-in is installed, you can run Excel 2007 and see the add-in's UI modifications.

If you run the add-in under Excel 2007 Beta 2, you should see something like Figure 3.

Figure 3. Excel 2007 Beta 2 with the shared add-in loaded

Excel 2007 Beta 2 with the shared add-in loaded

The first thing you notice is that there are no floating command bars. Second, there is now a new tab on the Ribbon with a caption of Add-Ins, as in Figure 4.

Figure 4. Detail of Excel 2007 Beta 2 with the shared add-in loaded

Detail of Excel 2007 Beta 2 with the shared add-in

Clicking the Add-Ins tab shows where all of the command bars went (see Figure 5).

Figure 5. The Add-Ins tab activated showing custom command bars and items

The Add-Ins tab showing custom command bars

If you click the appropriate controls, you will notice that the event handlers continue to work. The Microsoft Office applications that support the Ribbon use the same type of logic when loading existing COM add-ins. If there are customizations to menu bars, the Office application places them in a section labeled Menu Commands (see Figure 6).

Figure 6. The Menu Commands section of the Ribbon

The Menu Commands section of the Ribbon

Office applications place customizations to built-in command bars in a section labeled Toolbar Commands (see Figure 7).

Figure 7. The Toolbar Commands section of the Ribbon

The Toolbar Commands section of the Ribbon

Finally, Office applications place custom command bars in a first-created, first-listed basis in a section labeled Custom Toolbars (see Figure 8).

Figure 8. The Custom Toolbars section of the Ribbon

The Custom Toolbars section of the Ribbon

If you place the pointer over a control to see its tooltip (known as a ScreenTip in Microsoft Office), you see the format has changed. This is also how you differentiate one command bar from another. The name of the command bar is included as a prefix before the control's ScreenTip text (see Figure 9).

Figure 9. New ScreenTip format helps differentiate custom command bars

New ScreenTip helps differentiate command bars

The good news is that the sample add-in works, and so will any of your existing add-ins. However, the default UI behavior makes your add-in look bolted on rather than professionally tuned and integrated. This is where Visual Studio 2005 Tools for Office Second Edition comes in. As mentioned earlier, not only does it solve a number of problems with shared add-ins (without requiring you to compile and use a custom COM shim), it also brings support for Ribbon customization to your add-ins.

VSTO 2005 SE add-ins are specific to a single Microsoft Office application. You do not need to run a wizard to define your project and choose your host support. Instead, you choose the host you want to support by the Visual Studio project type you choose.

Follow the steps below to create an Excel-based add-in for Excel 2007. (Exit all copies of Excel, if any are running.)

To create an add-in for Excel

  1. Start Visual Studio 2005, if it is not already running.

  2. On the File menu, point to New, and then click Project.

  3. In the New Project dialog box, in the list of project types, expand either the Visual Basic or Visual C# node and then expand the Office node.

  4. Select the 2007 Add-ins node.

  5. In the Templates pane, select Excel Add-in.

  6. In the Name box, type ManagedAddInDemo.

  7. Select a location for your add-in, and then click OK.

Visual Studio creates a new solution with two projects: the Excel add-in project and a Setup project. Instead of a Connect class, the add-in has a class called ThisAddIn. Whereas the Connect class defined five method stubs for the five methods defined by the IDTExtensibility2 interface, the ThisAddIn class exposes two event handlers: Startup and Shutdown. These two members serve the same purpose as the IDTExtensibility2OnConnection and OnDisconnection methods. You can see much of the plumbing behind the ThisAddIn class by examining the partial class that is hidden by default in Visual Studio 2005. One of the benefits of this plumbing is that the Application class is already enabled and available for use and, if you are using Visual Basic, you have access to the application-level events in the Code Editor's object and event combo-boxes.

When migrating from a shared add-in to a VSTO 2005 SE add-in, it helps if you have factored your code. One possible way for you to do this is to factor your code into four discreet units: add-in management, UI management, host interaction, and core functionality. Add-in management is any of the code related to connecting to the host, host-specific events, and overall add-in details such as add-in configuration settings. UI management is all the code specific to hooking your add-in's code into the Office UI framework, such as command bars or Ribbon. Host interaction is any code that deals with host-specific functionality such as document classes, document events, and document manipulation. This does not include items related to add-in lifetime or configuration. Finally, core functionality is everything else, for example, Microsoft Windows Forms code, user controls for actions panes, and calls to Web services. At a minimum, you should factor your behaviors across multiple classes. Where the code is reusable across Office hosts, you should consider separate assemblies.

Ribbon Support

Ribbon support for your VSTO 2005 SE add-in includes a mix of declarative XML for layout and code for behavior, such as processing events. This new model reduces the amount of grunge code you need to write. For example, the sample shared add-in created earlier has a number of helper methods to ease repetitive UI creation. These methods are no longer necessary when using the Ribbon. Another nice change to the model is that you are no longer responsible for explicit cleanup. When your add-in is shut down, the Office host cleans up all of the UI modifications for you. That said, events are still processed in code. However, you do not have to do the extra work of explicitly hooking events.

The first step to getting Ribbon support is to add a Ribbon item to your project. Select the add-in project to activate it and then do the following steps.

To add a Ribbon item to your project

  1. On the Project menu, click Add New Item.

  2. In the Add New Item dialog box, select Ribbon support, and then click Add.

Visual Studio adds two files the project. Ribbon1.vb (Visual Basic) or Ribbon1.cs (Visual C#) contains the code stubs for event handling. Ribbon1.xml contains the declarative XML that defines what objects appear on the Ribbon and the event binding information. See the listings below for examples.

#region Using directives

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Office = Microsoft.Office.Core;


#endregion

namespace ManagedAddInDemoCS
{
  // TODO:
  // This is an override of the RequestService method in ThisAddin class.
  // To hook up your custom Ribbon, uncomment this code.
  //public partial class ThisAddIn
  //{
  //     Ribbon1 ribbon;
  //     protected override object RequestService(Guid serviceGuid)
  //     {
  //         if (serviceGuid == typeof(Office.IRibbonExtensibility).GUID)
  //         {
  //             if (ribbon == null)
  //                 ribbon = new Ribbon1();
  //             return ribbon;
  //         }
  //
  //         return base.RequestService(serviceGuid);
  //     }
  //}


  [ComVisible(true)]
  public class Ribbon1 : Office.IRibbonExtensibility
  {

    #region fields

    private Office.IRibbonUI ribbon;

    #endregion


    #region Initialization

    public Ribbon1()
    {
    }

    public string GetCustomUI(string ribbonID)
    {
      return GetResourceText("Ribbon1.xml");
    }

    public void OnLoad(Office.IRibbonUI ribbonUI)
    {
      this.ribbon = ribbonUI;
    }

    #endregion

    public void OnToggleButton1(Office.IRibbonControl control, 
      bool isPressed)
    {
      if (isPressed == true)
        MessageBox.Show("Pressed!");
      else
        MessageBox.Show("Released!");
    }

    #region Helpers
    public static string GetResourceText(string resourceName)
    {
      Assembly asm = Assembly.GetExecutingAssembly();
      string[] resources = asm.GetManifestResourceNames();
      foreach (string resource in resources)
      {
        if (resource.EndsWith(resourceName))
        {
          System.IO.StreamReader resourceReader =
              New System.IO.StreamReader(
              asm.GetManifestResourceStream(resource));
          if (resourceReader != null)
          {
            return resourceReader.ReadToEnd();
          }
        }
      }
      return null;
    }
    #endregion
  }
}
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" 
  onLoad="OnLoad">
  <ribbon startFromScratch="false">
    <tabs>
      <tab id="VSTO.Tab" label="VSTO Add-in Tab" visible="1">
        <group id="VSTO.Group" label="VSTO Group" visible="1">
          <toggleButton id="toggleButton1" label="Toggle Button 1"
                        screentip="ToggleButton1 Screentip" 
                          onAction="OnToggleButton1" />
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

To integrate the Ribbon customization, you need to uncomment the first bit of commented code. After you do this, you can debug the add-in by pressing F5. As part of project creation, Visual Studio wires the add-in project to start Excel 2007 for debugging sessions. After Excel starts, you see a new tab labeled VSTO Add-in Tab (see Figure 10).

Figure 10. A VSTO 2005 SE add-in with the default Ribbon customization loaded

The default Ribbon customization

Click VSTO Add-in Tab to display a single button labeled Toggle Button 1 in a group called VSTO Group (see Figure 11). Click the toggle button. You get a message box each time you click it, telling you the state of the toggle button.

Figure 11. The custom group and toggle button

The custom group and toggle button
NoteNote

Some features, such as shortcut menus, the mini Toolbar, the Status Bar, and live preview using galleries, are not available when you customize the Ribbon UI by using add-ins.

Migrating Command Bars

Migrating command bars from the default behavior provided out of the box is a bit more complicated than just moving code. The biggest issue is design. It is important that you understand the design considerations for Ribbon customizations. Random insertion of buttons and other controls onto the Ribbon can actually hurt acceptance and overall usability of your custom UI. The articles Developer Overview of the User Interface for the 2007 Microsoft Office System and 2007 Office System Document: UI Style Guide for Solutions and Add Ins cover this is great detail, so I will not go over it again. I also do not intend this article to be a complete end-to-end discussion of all the possible permutations of Ribbon customization. However, I do want to show you how to migrate the code defined earlier in the shared add-in to a VSTO 2005 SE add-in so you can see the mechanics of how to do it.

The first big change is "translating" command bar definition code to XML. The following snippet was used earlier to define a command bar.

return applicationObject.CommandBars.Add(Name,
  Core.MsoBarPosition.msoBarFloating, false, true);

When creating a new command bar, all of the parameters are actually optional. However, you should set the parameters explicitly when possible. The first parameter, Name, is the logical name of the new command bar. The Office host assigns a default name of Custom 1 if you do not provide a name. The second parameter, Position, specifies the location of the command bar. In the new UI, there is no support for floating command bars or floating ribbons, so the application ignores this parameter. The third parameter, Menubar, specifies replacing the default menu bar with the newly defined command bar. Office applications that support the Ribbon also ignore this parameter. The last parameter, Temporary, specifies that if True, command bars are deleted when the container application is closed. A tab on the Ribbon is equivalent to a command bar. When you add a Ribbon by using Visual Studio, the XML provided has the necessary information to create a custom tab.

In the earlier Excel 2003 add-in, you added controls to a command bar in different places in the example. In particular, the AddExtraUIAdditions procedure added all of the supported control types to custom command bars and to the Standard Formatting command bar. The following XML shows a modified version of the base XML provided by the Ribbon item. It has the default control, the toggle button, and all of the standard buttons used earlier.

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui"
 onLoad="OnLoad">
  <ribbon startFromScratch="false">
    <tabs>
      <tab id="tabSharedCommandBar"            label="Shared CommandBar" visible="1">
        <group id="Shared.Group" label="Shared Group" visible="1">
          <toggleButton id="toggleButton1" label="Toggle Button 1"
           screentip="ToggleButton1 Screentip" 
           onAction="OnToggleButton1" />
          <button id="AddInInfo" label="AddInInfo"            screentip ="Get Add-in Information" imageMso="HappyFace" />          <comboBox id="Colors" label="Colors"             screentip="Select or type a color" tag ="Colors">            <item id="Red" label="Red"/>            <item id="Green" label="Green"/>            <item id="Blue" label="Blue"/>          </comboBox>          <dropDown id="Elements" label="Elements"             screentip="Select an element" tag ="Elements">            <item id="Earth" label="Earth"/>            <item id="Wind" label="Wind"/>            <item id="Fire" label="Fire"/>            <item id="Water" label="Water"/>          </dropDown>          <editBox id="AskQuestion"            label="Ask a question" tag="AskQuestion"/>          <menu id="OtherOptionsButton" label="Other Options"            tag="OtherOptions">           <button id="PopupButton1" imageMso="HappyFace"             label="Popup Button" />           <button id="PopupButton2"             label="Another Popup Button"/>          </menu>
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

If you examine the XML, you see that overall it is straightforward. Most control types have well-known element names. Whereas controls created using the COM command bars API did not require you to specify unique IDs, the Ribbon's XML schema does. It is the same for items to be listed in combo box controls and drop-down controls. You no longer use the FaceID property; instead, use the imageMso attribute. The imageMso attribute requires a string name instead of the seemingly random integer used in previous versions of Microsoft Office. Finally, the best match for an msoControlPopup control is the menu control.

At this point, if you run the add-in inside Excel and then click the Shared CommandBar tab, Excel should greet you with a UI similar to Figure 12.

Figure 12. The modified Ribbon with a customized tab

The modified Ribbon with a customized tab

While visually complete, the only control that has code responding to it is the toggle button. In the shared add-in example, the button control had code that responded to a Click event. You associated the Click event handler to the control in code. To make this work using Ribbon controls, you need to do two things. First, modify the XML to map the Click event to a particular callback procedure. Second, define the actual callback procedure in the source file. The modified XML for the button is as follows.

<button id="AddInInfo" label="AddInInfo" 
  screentip ="Get Add-in Information" imageMso="HappyFace" 
  onAction="OnAddInInfo"/>

Next, in the Ribbon1 source file, inside the Ribbon1 class, add a new callback method.

public void OnAddInInfo(Office.IRibbonControl control)
{
  MessageBox.Show("Hello from " + control.Id);
}

At this point, there are three pieces of code left for you to replicate from the shared add-in to the new VSTO 2005 SE add-in. You need to duplicate the three extra command bars, each with a single button. Also, you need to hook up the additions to the built-in Excel UI: a menu item on the File menu and a command button on the Standard toolbar. There are several ways that you could replicate the three extra command bars. Remember that earlier I mentioned the 2007 Office System Document: UI Style Guide for Solutions and Add Ins article. An important point in that article is that your solution should blend into the Ribbon whenever possible. What does this mean? It means, if possible, your add-in should add its UI elements to an existing tab first in its own group. It cannot add its controls to an existing group. If your add-in has a UI that does not make sense in the context of any of the existing tabs, then have your add-in add its own tab. However, you should logically organize the tab and add additional tabs only if necessary. So, starting with three extra command bars, putting new groups on the existing add-in tab should work. The following block of XML shows the modifications necessary to do so. Notice that some of the XML has been edited for space.

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" 
  onLoad="OnLoad">
  <ribbon startFromScratch="false">
    <tabs>
      <tab id="tabSharedCommandBar" 
           label="Shared CommandBar" visible="1">
<!-- XML deleted -->
        <group id="Second.Shared.CommandBar"                label="Second Shared CommandBar" visible="1">          <button id="Heart" label="Heart"                   screentip ="Second.Shared.CommandBar"                   imageMso="Heart" tag="Heart"/>        </group>        <group id="Third.Shared.CommandBar"                label="Third Shared CommandBar" visible="1">          <button id="Diamond" label="Diamond"                   screentip ="Button added to third custom CommandBar"                   imageMso="Heart" tag="Diamond"/>        </group>        <group id="Fourth.Shared.CommandBar"                label="Fourth Shared CommandBar" visible="1">          <button id="Spade" label="Spade"                   screentip ="Button added to fourth custom CommandBar"                   imageMso="Spade" tag="Spade"/>        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

The added XML has three new group elements, each having a contained button element. One thing you should notice if you compare this to code used in the shared add-in is that the id attribute cannot have spaces. Because of this, the code uses periods to separate the words. Figure 13 shows what the completed tab looks like.

Figure 13. The customized tab with all three command bars converted to groups

Three command bars converted to groups

Moving on to the modification of the File menu and the Standard toolbar, you are left with a design decision. If you review Figure 6 and Figure 7 you should notice that, when the shared add-in was loaded into Excel 2007, Excel placed these two particular modifications in special groups on the Add-in Tab. The File menu as it existed in Excel 2003 does not exist. Neither does the Standard toolbar. The closest way to duplicate the shared add-in's modifications would be to modify the Office menu and to place an additional button on the built-in Home tab.

The following XML snippet shows how to add a button to the Office menu.

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" 
          onLoad="OnLoad">
  <ribbon startFromScratch="false">
    <officeMenu>      <button id="ExtraExtra" label="Extra Extra!" imageMso ="Pushpin"/>    </officeMenu>
<!-- XML deleted -->

Running the add-in after you make this modification produces the effect shown in Figure 14.

Figure 14. The customized Office menu

The customized Office menu

One issue you will notice is that the pushpin image is big and a bit blurry. It would be best to choose a different image or create your own. This is something to watch out for when you migrate your add-ins. With this completed, you add the last button by referencing the existing Home tab, defining the group, and finally defining the button. See Figure 15 for the results.

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" 
  onLoad="OnLoad">
  <ribbon startFromScratch="false">
    <officeMenu>
      <button id="ExtraExtra" label="Extra Extra!" imageMso ="HappyFace"/>
    </officeMenu>
    <tabs>
      <tab idMso="TabHome">        <group id="Shared.Group.Home" label="Shared" visible ="1">          <button id="EightBall" label="Button added to the Home tab"            showLabel ="0" imageMso="MagicEightBall"/>        </group>
      </tab>
<!-- XML deleted -->

Figure 15. The customized Home tab

The customized Home tab

However, as mentioned earlier, there is no real magic in the mechanics. The hard part is the design. Should your add-in have its own tab? Should you merge your add-in's UI elements with those on an existing tab? What new controls should your add-in use? In this new world of Microsoft Office, less is more. Modify the UI so that your add-in appears to be a part of the overall application. If possible, add a group to an existing tab instead of creating your own tab. If your add-in must have its own tab, logically group items and use a mix of controls that accomplish more with less.

A shared add-in is a great way to bring managed enhancements to Microsoft Office applications. Deployment with a COM shim (that you can build using the COM Shim Wizards) helps eliminate most of the negative aspects of shared add-ins. As you move forward to the 2007 release of Microsoft Office, Visual Studio 2005 Tools for Office Second Edition provides an overall better experience for add-in developers. Naturally, if you start from scratch and Visual Studio 2005 Tools for Office Second Edition supports your host, choosing Visual Studio 2005 Tools for Office Second Edition is not a difficult decision for managed development in Visual Basic or Visual C#. Remember, Visual Studio 2005 Tools for Office Second Edition offers advantages over shared add-ins that provide compelling reasons to migrate. A key to successful migration is effective factoring of your current solution's code related to connection and disconnection, UI manipulation, and host document interaction. If done now, moving forward and taking advantage of features like application task pane support, remote deployment support, and the Ribbon will be that much easier. Couple the new feature support with safe loading and unloading of managed add-ins, and you have a persuasive argument to move to Visual Studio 2005 Tools for Office Second Edition—sooner rather than later.

About the Author

Brian A. Randell is a senior consultant with MCW Technologies, LLC. Brian spends his time teaching Microsoft .NET–based technologies to developers and working with new and emerging technologies like the 2007 Microsoft Office system, Visual Studio 2005, and Visual Studio Team System. He also helps clients worldwide (such as Microsoft, American Honda, DELL, and others) with system design and implementation. As a Microsoft MVP, Brian enjoys helping people get the most out of their software. He does this through training, consulting, and speaking at events such as VSLive!, TechEd, and the PDC. In addition, Brian shares through the written word. He is the co-author of Effective Visual Basic and has written articles for MSDN Magazine and Microsoft. He is a member of Pluralsight's technical staff and author of Pluralsight's Applied Team System course. You can reach Brian via his blog.

Show:
© 2014 Microsoft