Extending the Outlook Ribbon programmatically

Extending the Outlook Ribbon

The MS-Office ribbon can be extended programmatically. In this article, I add a new tab to the ribbon, and new icons that open links to sites, then create a ClickOnce deployable package. We’ll explore a few tricks including how to import new icons. For a great overview of the Fluent UI, please see: https://msdn.microsoft.com/en-us/library/aa338198.aspx

Ribbon XML

Microsoft designed the Fluent UI (aka Ribbon) to be extensible. The easiest way to do this is via the Ribbon Designer. However I chose to hand-craft the solution based on Ribbon XML. After creating a new Visual Studio 2010 project, ensuring use of .NET 4.0,

To use an existing MS-Office Tab, just reference it by name, using tab idMso:

<customUI
xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon startFromScratch="false">
<!-- Ribbon XML -->
</ribbon>
<backstage>
<button idMso="FileSave" visible="false"/>
<button idMso="SaveObjectAs" visible="false"/>
<button idMso="FileSaveAsCurrentFileFormat" visible="false"/>
<button idMso="FileOpen" visible="false"/>
<button idMso="FileCloseDatabase" visible="false"/>
<tab idMso ="TabInfo" visible="false"/>
<tab idMso ="TabRecent" visible="false"/>
<tab idMso ="TabNew" visible="false"/>
<tab idMso ="TabPrint" visible="false"/>
<tab idMso ="TabShare" visible="false"/>
<tab idMso ="TabHelp" visible="false"/>
<button idMso="ApplicationOptionsDialog" visible="false"/>
<button idMso="FileExit" visible="false"/>
</backstage>
</customUI>

However you can create your own tab. Just remember to give it a unique ID, and also add the label. Note the use of “id” rather than “idMso”:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI onLoad="RibbonOnLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<commands>
<command idMso="ApplicationOptionsDialog" enabled="false"/>
<command idMso="FileExit" enabled="false"/>
</commands>
<ribbon startFromScratch="true">
<officeMenu>
<button idMso="FileNew" visible="false"/>
<button idMso="FileOpen" visible="false"/>
<button idMso="FileSave" visible="false" />
</officeMenu>
<tabs>
<tab id="MyCustomHomeTab" label="Home" insertAfterMso="TabHome" getVisible="GetVisible" tag="ribhome">
<group idMso="GroupClipboard" />
<group idMso="GroupFont" />
<group idMso="GroupAlignmentExcel" />
<group idMso="GroupNumber" />
<group idMso="GroupStyles" />
<group idMso="GroupCells" />
<group idMso="GroupEditingExcel" />
</tab>
<tab id="MyCustomInsertTab" label="Insert" insertAfterMso="TabInsert" getVisible="GetVisible" tag="ribinsert" >
<group idMso="GroupInsertTablesExcel" />
<group idMso="GroupInsertIllustrations" />
<group idMso="GroupInsertChartsExcel" />
<group idMso="GroupInsertLinks" />
<group idMso="GroupInsertText" />
</tab>
<tab id="MyCustomPageLayoutTab" label="Page Layout" insertAfterMso="TabPageLayoutExcel" getVisible="GetVisible" tag="ribpagelayout">
<group idMso="GroupThemesExcel" />
<group idMso="GroupPageSetup" />
<group idMso="GroupPageLayoutScaleToFit" />
<group idMso="GroupPageLayoutSheetOptions" />
<group idMso="GroupArrange" />
</tab>
<tab id="MyCustomFormulasTab" label="Formulas" insertAfterMso="TabFormulas" getVisible="GetVisible" tag="ribformulas">
<group idMso="GroupFunctionLibrary" />
<group idMso="GroupNamedCells" />
<group idMso="GroupFormulaAuditing" />
<group idMso="GroupCalculation" />
</tab>
<tab id="MyCustomDataTab" label="Data" insertAfterMso="TabData" getVisible="GetVisible" tag="ribdata">
<group idMso="GroupGetExternalData" />
<group idMso="GroupConnections" />
<group idMso="GroupSortFilter" />
<group idMso="GroupDataTools" />
<group idMso="GroupOutline" />
</tab>
<tab id="MyCustomReviewTab" label="Review" insertAfterMso="TabReview" getVisible="GetVisible" tag="ribreview">
<group idMso="GroupProofing" />
<group idMso="GroupComments" />
<group idMso="GroupChangesExcel" />
</tab>
<tab id="MyCustomViewTab" label="View" insertAfterMso="TabView" getVisible="GetVisible" tag="ribview">
<group idMso="GroupWorkbookViews" />
<group idMso="GroupViewShowHide" />
<group idMso="GroupZoom" />
<group idMso="GroupWindow" />
<group idMso="GroupMacros" />
</tab>
<tab id="MyCustomDeveloperTab" label="Developer" insertAfterMso="TabDeveloper" getVisible="GetVisible" tag="ribdeveloper">
<group idMso="GroupCode" />
<group idMso="GroupControls" />
<group idMso="GroupXml" />
<group idMso="GroupModify" />
</tab>
</tabs>
</ribbon>
</customUI>

For your image, you can reference existing MS-Office images, which is easiest and faimilar to users. Note that tag “imageMso” is used. The other advantage, as we’ll soon see, is these images are already sized, and loaded correctly:

imageMso="RecordsAddFromOutlook"

MS-Office has a great set of familiar icons you can leverage. These are easier to use than importing your own (which we will get to shortly). To select from existing icons, check out this great site, which includes the XML to reference the MS-Office images:
Ribbon images

To load your own images, first import the desired images into the Visual Studio project; under Resources is a great place. I recommend right clicking and marking each to “Include”. These images will only ever appear if you explicitly binary stream them in on Ribbon load. Here’s how. At the start of the MyRibbonxml, add a mew reference method called “GetImage” to load the images you will reference in this MyRibbon.xml:

imageMso="RecordsAddFromOutlook"

Rather than using imageMso, just use an image tag:

image="DMFLogo.ico"

Now for every image you reference, the loadImage method will be called, passing in the name of the image as a parameter. Let’s now add the getImage method to the myRibbon.cs:

public Bitmap GetImage(string imageName)
{
Assembly assembly = Assembly.GetExecutingAssembly();
//this is useful for navigating to discover the actual resource name reference
//string[] x;
//x = assembly.GetManifestResourceNames();
Stream stream = assembly.GetManifestResourceStream("MyProjectName.Resources." + imageName);
return new Bitmap(stream);  //uses system.drawing, added for now
}

At runtime, the stream is loaded based on the manifest reference. During initial debugging, it’s great to be able to see the manifest to ensure the path is correct. I’ve found it best to have a few lines of code to give you a handle on the manifest during debugging, if ever you need, just uncomment these and examine at a breakpoint:

//string[] x;
//x = assembly.GetManifestResourceNames();

As the images are stored in a folder, you’ll have to get the project name reference and the “Resources” folder path correct.

One last point is I found ICO files don’t give the best resolution. Microsoft recommends PNGs, which is a good image format to start.

To open a browser as a ribbon action, adapt this function:

public void ActionOpenInsureds(Office.IRibbonControl control)
{
string targetURL = "http ://sharepoint/sites/MySite/SitePages/Index1.aspx";
System.Diagnostics.Process.Start(targetURL);
return;
}

This function is called based on the button onAction tag in myRibbon.xml:

onAction="ActionOpenInsureds"

For completeness, here’s the full sample myRibbon.xml:

<?xml version="1.0" encoding="UTF-8"?>
<button id="textButton"></button>
<button id="DMFButton"></button>
<button id="tableButton"></button>

ClickOnce

ClickOnce is a fabulous technology. Previously we had to build a deployment project, or purchase 3rd-party software. This ribbon can be published to a central location as a ClickOnce install. It will appear in the Control Panel, and it can check in itself for updates. Here’s a great overview of ClickOnce: https://msdn.microsoft.com/en-us/library/t71a733d

To get started, simply select “Build, Publish” in Visual Studio 2010 and follow the menus.

A few things to note:
1. Unless you sign your code with a digital certificate, users will get prompted
2. The cert you use needs to have “code signing” authority, best is to get one issued by the CA (Certificate Authority) in your organization
3. If you set pre-requisites, the user needs admin authority on their machine in order to check-pre-requisites
4. There’s a registry change to control the .NET install prompts
5. Click-once does not support install for “All users” on the machine, only the current user
6. Even trying to run in silent mode still results in a single-acknowledgement window after the install

Silence is golden

I would expect running off My Documents would end up in the MyComputer Zone, which should be “Enabled” as a trusted zone for install without prompt, but here’s the keys to experiment with:

HKLMSoftwareMicrosoft.NETFrameworkSecurityTrustManagerPromptingLevel.

Then add the following string values for one or more of the security zones, as appropriate:

  • MyComputer
  • LocalIntranet
  • Internet
  • TrustedSites
  • UntrustedSites

Set the value of each security zone to one of the following:Enabled. The installation prompts the user if the solution is not signed with a trusted certificate. This is the default for the MyComputer, LocalIntranet, and TrustedSites zones.AuthenticodeRequired. The user cannot install the solution if it is not signed with a trusted root authority certificate. The installation prompts the user if the solution is not signed with a trusted publisher certificate. This is the default for the Internet zone.Disabled. The user cannot install the solution if it is not signed with a trusted publisher certificate. This is the default for the UntrustedSites zone.

The last option is to add this solution to the inclusion list, which is a list of trusted solutions.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *