Version-Specific UI in Add-ins
Continuing on from my earlier posts on building add-ins for multiple versions of Office, avoiding the PIA version conflict, and add-ins for multiple versions without PIAs, a reasonable way to design your solution would be to use the lowest-common-denominator PIAs (let’s say, the Office 2003 PIAs) and then use ComImports for any additional features introduced in later versions of Office. This way, you avoid having to declare ComImports for the vast majority of the Office and Office app object models, since you can generally rely on Office maintaining close to 100% backward compatibility within the core features. The changes in the Office OMs, as new versions are released, tend to be almost completely additive.
This model also means that even though you reference say the Office 2003 PIAs in your solution at design-time, when it comes to runtime, your add-in will run against whatever PIAs are registered on the machine. In other words, you can expect that on an Office 2003 machine, your add-in will run with the Office 2003 PIAs – and on an Office 2007 machine, your add-in will run with the Office 2007 PIAs. This is, of course, exactly the behavior you want. Moreover, it doesn’t require you to stray into unsupported territory by deploying multiple PIA versions on a machine with only one Office version, or building an Office 2003 add-in that references the Office 2007 PIAs, or any other dodgy behavior.
I’m attaching the sample solution to this post, and here’s what it looks like in VS:
As you can see, I have 3 projects:
- An Excel 2003 add-in.
- A class library that implements Office 2003 UI (CommandBar-based features), and that references the Office 2003 PIA.
- A class library that implements Office 2007 UI (custom task pane and Ribbon-based), and that does NOT reference the Office PIA, but instead uses ComImports to declare the Office interfaces I’m using (ICustomTaskPaneConsumer, IRibbonExtensibility, etc).
Here’s how they fit together:
As before, the ThisAddIn class is split into 4 partial classes:
- The “hidden” wizard-generated part of the partial class (in ThisAddIn.designer.cs), that you don’t normally want to edit.
- The usual “visible” part of the class (in ThisAddIn.cs). In this example, this contains code commonly shared regardless of Office version, specifically the Startup and Shutdown behavior.
- A part of the class containing code specific to Office 2003 (in ThisAddIn2003.cs). This uses the Ui2003 class library to implement custom CommandBar-based UI.
- A part of the class containing code specific to Office 2007 (in ThisAddIn2007.cs). This uses the Ui2007 class library to implement custom task pane and Ribbon UI.
When the add-in is loaded, in ThisAddIn_Startup, it checks to see which version of the Office host it is running in: if it’s running in Office 2003, it branches out to initialize the CommandBar-based UI.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
// 2003: If we're running in 2003, setup a custom commandbar menu.
if (Application.Version == "11.0")
The part of the partial class in ThisAddIn2007.cs contains an override of RequestService, which will not be called in Office 2003. Therefore, if RequestService is called, we can assume we’re running in Office 2007 (or later), and can therefore set up custom task pane and Ribbon UI.
So, the solution only references one version of the Office PIAs (the LCD version), and uses ComImport declarations for the OM features that are not in that version. Then, at runtime, the correct version of the PIAs are loaded that match the running version of Office. I could take this further, and dispense with the PIAs altogether, using ComImports for everything – but that’s normally unnecessary, as you can generally rely on the PIAs being installed, or you can pre-req them as part of the install for your solution. Plus, I don’t really want to re-declare vast tracts of the Office OM unless I really have to. This model provides an optimal solution: using the PIAs for the core Office behavior that does not change between versions, and using ComImports for the additional features added in later versions.