This article has an accompanying sample add-in named RibbonXOutlook14AddinCS. It uses Microsoft Visual C# and requires Microsoft Visual Studio 2008 Service Pack 1 and Outlook 2010.
The sample add-in demonstrates how to customize the ribbons, menus, context menus, and Backstage view in Outlook 2010. Developed in Visual Studio 2008 Tools for Office, the add-in adds ribbon controls, a custom menu, context menu items, and Backstage view controls, and then displays a message box when the user clicks the control or menu item. The add-in provides a visual element for every entry point in the Outlook 2010 UI that you can customize by using Fluent UI extensibility.
The sample add-in has some additional features that illustrate how to manage certain problem areas in the Outlook 2010 UI. For example, suppose that you want to show a custom group in the ribbon for received e-mail items only. The sample add-in displays the custom ribbon tab only when the selected item in the Outlook explorer is a received mail item or when the received mail item is displayed in an inspector. Although that might seem to be a trivial task at first, it is actually a complex problem because Outlook can display multiple explorer or inspector windows, and your code must be able to respond appropriately. For example, in two open explorer windows, you might have to hide the custom tab in the window that has a meeting selected, but display the custom tab in the window that has a received mail item selected. Once you understand how the sample add-in works, you can use the wrapper classes from the sample to build your own solution that can coordinate the display of your command UI in multiple Outlook windows.
The Outlook14RibbonXAddinCS solution is built with Microsoft Visual Studio 2008 Service Pack 1. It uses manual authoring of XML markup files instead of the ribbon designer that is provided with Visual Studio Tools for Office. The most important architectural feature is the use of wrapper classes (OutlookExplorer.cs for explorers and OutlookInspector.cs for inspectors) to handle multiple Outlook windows. The wrapper classses are implemented by using a C# Generic List(T) class.
Note: |
|---|
|
If you are not writing managed code, you must write native C++ code that mimics the use of these wrapper classes.
|
First, the following class-level instance variables are declared in the standard ThisAddin class in Visual Studio 2008 with Visual Studio Tools for Office.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
namespace RibbonXOutlook14AddinCS
{
public partial class ThisAddIn
{
#region Instance Variables
Outlook.Application m_Application;
Outlook.Explorers m_Explorers;
Outlook.Inspectors m_Inspectors;
public stdole.IPictureDisp m_pictdisp = null;
// List of tracked explorer windows.
internal static List<OutlookExplorer> m_Windows;
// List of traced inspector windows.
internal static List<OutlookInspector> m_InspectorWindows;
// Ribbon UI reference.
internal static Office.IRibbonUI m_Ribbon;
#endregion
...
}
}
Once the instance variables have been declared, they are used in the Startup method of the ThisAddin class that is provided to all Visual Studio 2008 with Visual Studio Tools for Office add-ins. In the Startup method, which typically occurs when Outlook starts, the code first hooks up an event handler to respond to the NewExplorer event on the Outlook Explorers collection object. After the NewExplorer event has been handled, the active explorer window is added by using the following code.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
// Initialize variables.
m_Application = this.Application;
m_Explorers = m_Application.Explorers;
m_Inspectors = m_Application.Inspectors;
m_Windows = new List<OutlookExplorer>();
m_InspectorWindows = new List<OutlookInspector>();
// Wire up event handlers to handle multiple Explorer windows.
m_Explorers.NewExplorer +=
new Outlook.ExplorersEvents_NewExplorerEventHandler(
m_Explorers_NewExplorer);
// Wire up event handler to handle multiple Inspector windows.
m_Inspectors.NewInspector +=
new Outlook.InspectorsEvents_NewInspectorEventHandler(
m_Inspectors_NewInspector);
// Add the ActiveExplorer to m_Windows.
Outlook.Explorer expl = m_Application.ActiveExplorer()
as Outlook.Explorer;
OutlookExplorer window = new OutlookExplorer(expl);
m_Windows.Add(window);
// Hook up event handlers for window.
window.Close += new EventHandler(WrappedWindow_Close);
window.InvalidateControl += new EventHandler<
OutlookExplorer.InvalidateEventArgs>(
WrappedWindow_InvalidateControl);
// Get IPictureDisp for CurrentUser on startup.
try
{
Outlook.AddressEntry addrEntry =
Globals.ThisAddIn.Application.Session.CurrentUser.AddressEntry;
if (addrEntry.Type == "EX")
{
Outlook.ExchangeUser exchUser =
addrEntry.GetExchangeUser() as Outlook.ExchangeUser;
m_pictdisp = exchUser.GetPicture() as stdole.IPictureDisp;
}
}
catch (Exception ex)
{
// Write exception to debug window.
Debug.WriteLine(ex.Message);
}
}
Just after Outlook starts, Office calls GetCustomUI. GetCustomUI is the method of the IRibbonExtensibility interface that loads custom XML markup. In Outlook 2007, Office called GetCustomUI when the first instance of a given inspector type, such as a contact or appointment, was displayed. In Outlook 2010, Office calls GetCustomUI before the ThisAddin.Startup method to accommodate the loading of ribbon customizations for the Outlook explorer ribbon. Because Office calls GetCustomUI only once for the first Outlook explorer during startup and multiple times for the first instance of multiple inspector types, consider using a Switch statement to control loading of XML markup for different customized Ribbons. For each ribbon in Outlook, GetCustomUI is called and Office passes the ribbon ID string that identifies the ribbon that is being loaded in Outlook. For a complete listing of ribbon IDs, see the section Ribbon Identifiers.
In the sample code, there are three XML markup files:
-
ContactCard.xml
-
Explorer.xml
-
ReadMail.xml
ContactCard.xml contains XML markup for context menus on the Contact Card. Explorer.xml contains XML markup for the explorer ribbon, context menus, and Backstage view. Readmail.xml contains XML markup for a read mail inspector.
To get a better idea of how this technique works, examine the following code from the GetCustomUI method in the OutlookRibbonX class.
public string GetCustomUI(string ribbonID)
{
string customUI = string.Empty;
Debug.WriteLine(ribbonID);
// Return the appropriate XML markup for ribbonID.
switch (ribbonID)
{
case "Microsoft.Outlook.Explorer":
customUI = GetResourceText(
"RibbonXOutlook14AddinCS.Explorer.xml");
return customUI;
case "Microsoft.Outlook.Mail.Read":
customUI= GetResourceText(
"RibbonXOutlook14AddinCS.ReadMail.xml");
return customUI;
case "Microsoft.Mso.IMLayerUI":
customUI = GetResourceText(
"RibbonXOutlook14AddinCS.ContactCard.xml");
return customUI;
default:
return string.Empty;
}
}
Take a look at the sample solution for a complete listing of ContactCard.xml, Explorer.xml, and ReadMail.xml. Once custom XML markup has been loaded for each add-in, it is time to write code in Fluent UI extensibility callbacks to hide and show the tab named MyTab, depending on whether the selected item in the explorer is a received mail item. To complete the scenario, you must hook up another set of events in the constructor of the OutlookExplorer class.
public OutlookExplorer(Outlook.Explorer explorer)
{
m_Window = explorer;
// Hook up Close event.
((Outlook.ExplorerEvents_Event)explorer).Close +=
new Outlook.ExplorerEvents_CloseEventHandler(
OutlookExplorerWindow_Close);
// Hook up SelectionChange event.
m_Window.SelectionChange +=
new Outlook.ExplorerEvents_10_SelectionChangeEventHandler(
m_Window_SelectionChange);
}
The SelectionChange event handler is very straightforward. The SelectionChange event occurs whenever the user changes the selection in an explorer window. When SelectionChange occurs, the RaiseInvalidateControl method is called and the control ID "MyTab" is passed to the method. RaiseInvalidateControl calls an event delegate named WrappedWindow_InvalidateControl in the ThisAddin class.
private void m_Window_SelectionChange()
{
RaiseInvalidateControl("MyTab");
}
void WrappedWindow_InvalidateControl(object sender,
OutlookExplorer.InvalidateEventArgs e)
{
if (m_Ribbon != null)
{
m_Ribbon.InvalidateControl(e.ControlID);
}
}
The m_Ribbon variable represents an IRibbonUI object. When the InvalidateControl method of the IRibbonUI object is called, ribbon callbacks occur for the "MyTab" control. You could also call the Invalidate method of IRibbonUI, but that method causes callbacks for all of the controls that your add-in adds to the ribbon. In general, make your invalidation more granular than global, especially when your callbacks load image resources with a getImage callback. For the "MyTab" control, a getVisible callback has been defined in the RibbonXAddin class that controls the visibility of the tab. If the getVisible callback returns True, the tab is visible; otherwise, "MyTab" is hidden. The following code is the MyTab_GetVisible callback in its entirety.
// Only show MyTab when explorer selection is
// a received mail or when inspector is a read note.
public bool MyTab_GetVisible(Office.IRibbonControl control)
{
if (control.Context is Outlook.Explorer)
{
Outlook.Explorer explorer =
control.Context as Outlook.Explorer;
Outlook.Selection selection = explorer.Selection;
if (selection.Count == 1)
{
if (selection[1] is Outlook.MailItem)
{
Outlook.MailItem oMail =
selection[1] as Outlook.MailItem;
if (oMail.Sent == true)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else if (control.Context is Outlook.Inspector)
{
Outlook.Inspector oInsp =
control.Context as Outlook.Inspector;
if (oInsp.CurrentItem is Outlook.MailItem)
{
Outlook.MailItem oMail =
oInsp.CurrentItem as Outlook.MailItem;
if (oMail.Sent == true)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return true;
}
}
This walkthrough detailed the most important strategies for extending the ribbon in Outlook 2010. In summary, your code should support the following:
-
The use of multiple explorer or inspector windows in Outlook 2010. Wrap the windows in a separate class instance that can raise events appropriate to that specific window.
-
The appropriate use of the InvalidateControl or Invalidate methods of the IRibbonUI interface to cause callbacks to occur.
-
The evaluation of different item types (based on the message class, built-in properties, or custom properties of a specific item type) in both event handlers and ribbon callbacks.