How to preserve the ribbon control state (or, if you develop custom Outlook ribbons…)
If you decide to build an Outlook add-in, you’re going to want to customize the user interface.
If you start customizing the Outlook user interface, you’ll probably build a custom ribbon.
If you build a custom Outlook ribbon, you’ll want to put some controls on it like a button that toggles, a checkbox, and a drop down control.
If you add these types of controls and finish writing your code, you’ll want to test your add-in.
If you hit F5, start Outlook, and test your addin, you’ll be tempted to open multiple inspector windows.
If you open multiple inspector windows, click your controls, and switch back and forth between inspectors…
You will discover the problem I am to solve today.
- Multiple Outlook inspectors, multiple problems
- A strategy for maintaining a separate ribbon control state
- Building the Outlook add-in project
Outlook utilizes two types of windows. The primary window is the Outlook Explorer window in which the user navigates between the main parts of Outlook (e.g. mail, contacts, calendar, etc.). Typically, you only have one Outlook explorer window open, although you can open multiple.
The secondary window is the Outlook Inspector window. This is the window that displays individual Outlook items (e.g. mail, contacts, calendar, etc.). It is quite common to have multiple inspector windows open at any given time. And this is the source of our problem.
What we want… what we expect… is for our custom ribbon to behave like Figure 1. But…
Custom Ribbons do not preserve their state across multiple inspector windows by default. You have to code this functionality.
Figure 1. Two Outlook inspector windows maintaining separate control states
A strategy for maintaining a separate ribbon control state
Boiled down to its essence, the strategy is to create a custom user property and store its value in the Outlook item displayed in the inspector window. If you have multiple controls you want to maintain, you add additional user properties (see Figure 2).
Figure 2. How to maintain a control’s state across all open Outlook inspector windows
When the user takes an action that changes the control’s state, you update the control’s corresponding user property. When the user switches between Outlook inspector windows, you set the control state by reading the corresponding user properties and updating the ribbon controls.
Building the Outlook add-in project
This plugin for Outlook 2013, 2010 and 2007 includes a single custom ribbon with three controls: a button, a checkbox, and a drop down. In terms of code, there are a handful of methods (custom methods and control events). We are going to use Add-in Express for Office and .net.
With Visual Studio open (I’m using Visual Studio 2012), create a new project according to these steps.
- Create a new ADX COM Add-in project. Move through the New Microsoft Office COM Add-in wizard dialog and select VB.NET as in this example, or C#, or C++, as the language of choice and Microsoft Office as the target application. The minimum supported version needs to be Office 2007 or higher.
- After you have the project open in Visual Studio, add an ADXRibbonTab control to the AddinModule‘s in design surface. Change its Caption property to Preserve State.
- Add a ribbon button group (AdxRibbonButtonGroup control) to ribbon tab and set its caption to Preservation Group.
- Add a button (AdxRibbonButtoncontrol) to the ribbon button group and set these properties:
- Caption = FlagIt!
- Size = Large
- ToggleButton = True
- ImageMSO = FlagToday
- Add a check box (AdxRibbonCheckBox control) to the button group and set its caption to Check Me Out.
- Add a drop-down control (AdxRibbonDropdown)to the group.
- Its caption needs to be Favorite Major.
- Add five items to the items collection and set their captions to: (None), Australian Open, French Open, Wimbledon, and U.S. Open respectively.
- Set the drop-down’s SelectedItemIndex property to 0.
Hopefully, your Outlook ribbon is similar to Figure 3.
Figure 3. The custom ribbon in design view
Writing the code
Before we write some code, you need to add Outlook events to the AddinModule. Right click on the AddInModule design surface and click Add Events. In the Add Application Events dialog box select Microsoft Outlook Events and click OK. This action adds an adxOutlookEvents control to the AddInModule and make Outlook events available in the AddinModule class.
Now we are ready to code.
The InspectorActivate Event
We’ll begin with the end. The InspectorActivate event is the one that syncs control state with the Outlook item’s custom user properties. Open the AddInModule’s code view and add the following code:
Private Sub adxOutlookEvents_InspectorActivate(sender As Object, _ inspector As Object, folderName As String) _ Handles adxOutlookEvents.InspectorActivate Dim flagValue As String = GetStateProperty(inspector, "FlagIt") AdxRibbonButton1.Pressed = False ' default value If Not String.IsNullOrEmpty(flagValue) Then AdxRibbonButton1.Pressed = Convert.ToBoolean(flagValue) End If Dim checkValue As String = GetStateProperty(inspector, "CheckIt") AdxRibbonCheckBox1.Pressed = False ' default value If Not String.IsNullOrEmpty(checkValue) Then AdxRibbonCheckBox1.Pressed = Convert.ToBoolean(checkValue) End If Dim favValue As String = GetStateProperty(inspector, "Favorite") AdxRibbonDropDown1.SelectedItemIndex = 0 ' default index If Not String.IsNullOrEmpty(favValue) Then AdxRibbonDropDown1.SelectedItemIndex = Convert.ToInt32(favValue) End If End Sub
This procedure does the same thing three times… once for each of our Outlook ribbon controls as follows:
- Calls the GetStateProperty function (explained further below) and stores it in a variable.
- Sets the default state of the control.
- If a value was returned by GetStateProperty, it sets the value of the control’s relevant state property (i.e. Pressed or SelectedItemIndex).
This is how the magic happens but there is a supporting cast of methods.
SetStateProperty method – save the control’s state to the user property
The SetStateProperty method accepts two parameters: propName and propValue. The method then retrieves the user property and sets its value. If the user property does not exist, the method creates it. Insert the following code into the AddinModule class.
Private Sub SetStateProperty(propName As String, propValue As String) Dim inspector As Outlook._Inspector = Me.OutlookApp.ActiveInspector Dim mailItem As Outlook._MailItem = Nothing Dim userProperties As Outlook.UserProperties = Nothing Dim userProperty As Outlook.UserProperty = Nothing mailItem = TryCast(inspector.CurrentItem, Outlook._MailItem) If mailItem IsNot Nothing Then userProperties = mailItem.UserProperties userProperty = userProperties.Find(propName) If (userProperty IsNot Nothing) Then userProperty.Value = propValue Else userProperty = userProperties.Add(propName, Outlook.OlUserPropertyType.olText) userProperty.Value = propValue End If mailItem.Save() End If If userProperty IsNot Nothing Then Marshal.ReleaseComObject(userProperty) If userProperties IsNot Nothing Then Marshal.ReleaseComObject(userProperties) If mailItem IsNot Nothing Then Marshal.ReleaseComObject(mailItem) If inspector IsNot Nothing Then Marshal.ReleaseComObject(inspector) End Sub
The method also checks to ensure it is working with the mail item and cleans up its reference COM objects.
GetStateProperty function – retrieve the user property’s value
GetStateProperty works like SetStateProperty but in reverse. This function accepts a context object and a propName string as parameters. The context is the inspector window while the propName is the user property whose values we want to retrieve. Insert the following code into the AddinModule class.
Private Function GetStateProperty(context As Object, propName As String) As String Dim result As String = String.Empty Dim inspector As Outlook._Inspector = TryCast(context, Outlook._Inspector) If inspector IsNot Nothing Then Dim mailItem As Outlook._MailItem = TryCast(inspector.CurrentItem, Outlook._MailItem) If mailItem IsNot Nothing Then Dim userProperties As Outlook.UserProperties = mailItem.UserProperties Dim userProperty As Outlook.UserProperty userProperties = mailItem.UserProperties userProperty = userProperties.Find(propName) If userProperty IsNot Nothing Then result = userProperty.Value.ToString() Marshal.ReleaseComObject(userProperty) End If Marshal.ReleaseComObject(userProperties) Marshal.ReleaseComObject(mailItem) End If End If Return result End Function
The ribbon button’s click event calls the SetStateProperty method. It specifies “FlagIt” as the propName and passes the button’s pressed state as a string value.
Private Sub AdxRibbonButton1_OnClick(sender As Object, _ control As IRibbonControl, pressed As Boolean) _ Handles AdxRibbonButton1.OnClick SetStateProperty("FlagIt", pressed.ToString()) End Sub
The ribbon check box’s click event calls the SetStateProperty method. It specifies “CheckIt” as the propName and passes the check box’s pressed state as a string value.
Private Sub AdxRibbonCheckBox1_OnClick(sender As Object, _ control As IRibbonControl, pressed As Boolean) _ Handles AdxRibbonCheckBox1.OnClick SetStateProperty("CheckIt", pressed.ToString()) End Sub
The ribbon drop down’s OnAction event also calls the SetStateProperty method. It specifies “Favorite” as the propName and passes the drop down control’s selectedIndex value as a string.
Private Sub AdxRibbonDropDown1_OnAction(sender As Object, _ control As IRibbonControl, selectedId As String, selectedIndex As Integer) _ Handles AdxRibbonDropDown1.OnAction SetStateProperty("Favorite", selectedIndex.ToString()) End Sub
Give it a test
You are now ready to test the add-in. If you have Outlook open, close it. Then press F5 in Visual Studio. When Outlook displays, open a few mail items in inspector windows. Change some custom ribbon controls’ state values and switch between inspectors. Your controls should successfully maintain their property state:
If they don’t, you can complain to the author in the comments section.
If you complain to the author, he’ll probably become bothered.
If he becomes bothered, he’ll spend all day wondering why it didn’t work.
If he spends all day wondering why it didn’t work, he’ll find a fix.
If he finds a fix, he’ll post it in the comments…
This sample Outlook add-in was developed using Add-in Express for Office and .net:
You may also be interested in:
- Customizing Microsoft Outlook 2010 and 2013 Ribbon
- How to add custom ribbon tabs & command bars in a single Outlook plugin