Walkthrough: Displaying Custom Task Panes with E-Mail Messages in Outlook

This walkthrough demonstrates how to display a unique instance of a custom task pane with each e-mail message that is created or opened. Users can hide or display the custom task pane by using a button on the Ribbon of each e-mail message.

To display a custom task pane with multiple Explorer or Inspector windows, you must create an instance of the custom task pane for every window that is opened. For more information about the behavior of custom task panes in Outlook windows, see Custom Task Panes Overview.

Note

This walkthrough presents the add-in code in small sections to make it easier to discuss the logic behind the code. To view the entire contents of each file that you edit in this walkthrough as blocks of code, see How to: Display Custom Task Panes with E-Mail Messages in Outlook.

This walkthrough illustrates the following tasks:

  • Designing the user interface (UI) of the custom task pane.

  • Creating a class to manage Inspector windows and custom task panes.

  • Initializing and cleaning up resources used by the add-in.

  • Creating a custom Ribbon UI.

  • Specifying callback methods for a toggle button on the Ribbon.

  • Synchronizing the Ribbon toggle button with the custom task pane.

Note

The dialog boxes and menu commands you see might differ from those described in Help depending on your active settings or edition. To change your settings, select Import and Export Settings on the Tools menu. For more information, see Visual Studio Settings.

Prerequisites

You need the following components to complete this walkthrough:

  • One of these two development environments:

    • Microsoft Visual Studio 2005 Tools for the Microsoft Office System (VSTO 2005).

      -or-

    • Visual Studio 2005 Professional.

  • Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System (VSTO 2005 SE).

  • Microsoft Office Outlook 2007.

Creating the Project

Custom task panes are implemented in application-level add-ins. Start by creating an add-in project for Outlook 2007.

To create a new project

  • Create an Outlook Add-in project with the name OutlookMailItemTaskPane. Ensure that you use the Outlook Add-in project template for the 2007 Microsoft Office system. For more information, see How to: Create Visual Studio Tools for Office Projects.

    Visual Studio opens the ThisAddIn.cs or ThisAddIn.vb code file and adds the OutlookMailItemTaskPane project to Solution Explorer.

Designing the User Interface of the Custom Task Pane

There is no visual designer for custom task panes, but you can design a user control with the UI you want. The custom task pane in this add-in has a simple UI that contains a TextBox control. Later in this walkthrough, you will add the user control to the custom task pane.

To design the user interface of the custom task pane

  1. In Solution Explorer, click the OutlookMailItemTaskPane project.

  2. On the Project menu, click Add User Control.

  3. In the Add New Item dialog box, change the name of the user control to TaskPaneControl, and then click Add.

    The user control opens in the designer.

  4. From the Common Controls tab of the Toolbox, drag a TextBox control to the user control.

Creating a Class to Manage Inspector Windows and Custom Task Panes

There are several cases in which the add-in must determine which custom task pane is associated with a specific e-mail message. These cases include the following:

  • When the user closes an e-mail message. In this case, the add-in must remove the corresponding custom task pane to ensure that resources used by the add-in are cleaned up correctly.

  • When the user closes the custom task pane. In this case, the add-in must update the state of the toggle button on the Ribbon of the e-mail message.

  • When the user clicks the toggle button on the Ribbon. In this case, the add-in must hide or display the corresponding task pane.

To enable the add-in to keep track of which custom task pane is associated with each open e-mail message, create a custom class that wraps pairs of Microsoft.Office.Interop.Outlook.Inspector and CustomTaskPane objects. This class creates a new custom task pane object for each e-mail message, and it deletes the custom task pane when the corresponding e-mail message is closed.

To create a class to manage Inspector windows and custom task panes

  1. In Solution Explorer, right-click the ThisAddIn.cs or ThisAddIn.vb file, and then click View Code.

  2. Add the following statements to the top of the file.

    Imports System.Collections.Generic
    Imports Microsoft.Office.Tools
    
    using System.Collections.Generic;
    using Microsoft.Office.Tools;
    
  3. Add the following code to the ThisAddIn.cs or ThisAddIn.vb file, outside the ThisAddIn class (for Visual C#, ensure that you add this code inside the OutlookMailItemTaskPane namespace). The InspectorWrapper class manages a pair of Microsoft.Office.Interop.Outlook.Inspector and CustomTaskPane objects. You will complete the definition of this class in the following steps.

    Public Class InspectorWrapper
        Private WithEvents inspector As Outlook.Inspector
        Private WithEvents taskPane As CustomTaskPane
    
    public class InspectorWrapper
    {
        Outlook.Inspector inspector;
        CustomTaskPane taskPane;
    
  4. Add the following constructor after the code that you added in the previous step. This constructor creates and initializes a new custom task pane that is associated with the Microsoft.Office.Interop.Outlook.Inspector object that is passed in. The constructor also attaches event handlers to the Microsoft.Office.Interop.Outlook.InspectorEvents_Event.Close event of the Microsoft.Office.Interop.Outlook.Inspector object and to the VisibleChanged event of the CustomTaskPane object.

        Public Sub New(ByVal Inspector As Outlook.Inspector)
            Inspector = Inspector
            taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(New TaskPaneControl(), "My task pane", Inspector)
            taskPane.Visible = True
        End Sub
    
        public InspectorWrapper(Outlook.Inspector Inspector)
        {
            inspector = Inspector;
            ((Outlook.InspectorEvents_Event)inspector).Close +=
                new Outlook.InspectorEvents_CloseEventHandler(InspectorWrapper_Close);
    
            taskPane = Globals.ThisAddIn.CustomTaskPanes.Add(
                new TaskPaneControl(), "My task pane", inspector);
            taskPane.Visible = true;
            taskPane.VisibleChanged += new EventHandler(TaskPane_VisibleChanged);
        }
    
  5. Add the following method after the code that you added in the previous step. This method is an event handler for the VisibleChanged event of the CustomTaskPane object that is contained in the InspectorWrapper class. For now, the event handler does nothing. Later in this walkthrough, you will modify the event handler to update the state of the toggle button whenever the user opens or closes the custom task pane.

        Sub TaskPane_VisibleChanged(ByVal sender As Object, ByVal e As EventArgs) Handles taskPane.VisibleChanged
        End Sub
    
        void TaskPane_VisibleChanged(object sender, EventArgs e)
        {
        }
    
  6. Add the following method after the code that you added in the previous step. This method is an event handler for the Microsoft.Office.Interop.Outlook.InspectorEvents_Event.Close event of the Microsoft.Office.Interop.Outlook.Inspector object that contains the current e-mail message. The event handler frees resources when the e-mail message is closed. The event handler also removes the current custom task pane from the CustomTaskPanes collection. This helps prevent multiple instances of the custom task pane when the next e-mail message is opened.

        Sub InspectorWrapper_Close() Handles inspector.Close
            If Not (taskPane Is Nothing) Then
                Globals.ThisAddIn.CustomTaskPanes.Remove(taskPane)
            End If
    
            taskPane = Nothing
            Globals.ThisAddIn.inspectorWrappers.Remove(inspector)
            RemoveHandler inspector.Close, AddressOf InspectorWrapper_Close
            inspector = Nothing
        End Sub
    
        void InspectorWrapper_Close()
        {
            if (taskPane != null)
            {
                Globals.ThisAddIn.CustomTaskPanes.Remove(taskPane);
            }
    
            taskPane = null;
            Globals.ThisAddIn.inspectorWrappers.Remove(inspector);
            ((Outlook.InspectorEvents_Event)inspector).Close -=
                new Outlook.InspectorEvents_CloseEventHandler(InspectorWrapper_Close);
            inspector = null;
        }
    
  7. Add the following code after the code that you added in the previous step. Later in this walkthrough, you will call this property from methods in the custom Ribbon UI to display the custom task pane and to determine whether the custom task pane is currently visible.

        Public ReadOnly Property CustomTaskPane() As CustomTaskPane
            Get
                Return taskPane
            End Get
        End Property
    End Class
    
        public CustomTaskPane CustomTaskPane
        {
            get
            {
                return taskPane;
            }
        }
    }
    

Initializing and Cleaning Up Resources Used by the Add-In

Add code to the ThisAddIn class to initialize the add-in when it is loaded, and to clean up resources used by the add-in when it is unloaded. You initialize the add-in by setting up an event handler for the Microsoft.Office.Interop.Outlook.Inspectors.NewInspector event and by passing all existing e-mail messages to this event handler. When the add-in is unloaded, detach the event handler and clean up objects used by the add-in.

To initialize and clean up resources used by the add-in

  1. In the ThisAddIn.cs or ThisAddIn.vb file, locate the definition of the ThisAddIn class.

  2. Add the following declarations to the ThisAddIn class. The Dictionary object contains all the Microsoft.Office.Interop.Outlook.Inspector and InspectorWrapper objects that are managed by the add-in. The Microsoft.Office.Interop.Outlook.Inspectors object is used to maintain a reference to the collection of Inspector windows in the current Outlook instance. This reference prevents the garbage collector from freeing the memory that contains the event handler for the Microsoft.Office.Interop.Outlook.Inspectors.NewInspector event, which you will declare in the next step.

    Public inspectorWrappers As New Dictionary(Of Outlook.Inspector, InspectorWrapper)
    Private WithEvents inspectors As Outlook.Inspectors
    
    public Dictionary<Outlook.Inspector, InspectorWrapper> inspectorWrappers =
        new Dictionary<Outlook.Inspector, InspectorWrapper>();
    private Outlook.Inspectors inspectors;
    
  3. Replace the ThisAddIn_Startup method with the following code. This code attaches an event handler to the Microsoft.Office.Interop.Outlook.Inspectors.NewInspector event, and it passes every existing Microsoft.Office.Interop.Outlook.Inspector object to the event handler. The add-in uses this information to create custom task panes for all e-mail messages that are already open when the add-in is loaded.

    Private Sub ThisAddIn_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup
        inspectors = Me.Application.Inspectors
    
        Dim inspector As Outlook.Inspector
        For Each inspector In inspectors
            Inspectors_NewInspector(inspector)
        Next inspector
    End Sub
    
    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        inspectors = this.Application.Inspectors;
        inspectors.NewInspector +=
            new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
    
        foreach (Outlook.Inspector inspector in inspectors)
        {
            Inspectors_NewInspector(inspector);
        }
    }
    
  4. Replace the ThisAddIn_ShutDown method with the following code. This code detaches the Microsoft.Office.Interop.Outlook.Inspectors.NewInspector event handler and cleans up objects used by the add-in.

    Private Sub ThisAddIn_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shutdown
        RemoveHandler inspectors.NewInspector, AddressOf Inspectors_NewInspector
        inspectors = Nothing
        inspectorWrappers = Nothing
    End Sub
    
    private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
    {
        inspectors.NewInspector -=
            new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
        inspectors = null;
        inspectorWrappers = null;
    }
    
  5. Add the following Microsoft.Office.Interop.Outlook.Inspectors.NewInspector event handler to the ThisAddIn class. For now, the event handler does nothing. Later in this walkthrough, you will add code to this event handler to create a custom task pane for every e-mail message that is opened.

    Sub Inspectors_NewInspector(ByVal Inspector As Outlook.Inspector) _
        Handles inspectors.NewInspector
    End Sub
    
    void Inspectors_NewInspector(Outlook.Inspector Inspector)
    {
    }
    

Checkpoint

Build your project to ensure that it compiles without errors.

To build your project

  • In Solution Explorer, right-click the OutlookMailItemTaskPane project and then click Build. Verify that the project compiles without errors.

Creating a Custom Ribbon User Interface

You create a custom Ribbon UI by adding a Ribbon support item to your project. Later in this walkthrough, you will modify the default toggle button that is included in the item template to hide or display the custom task pane.

To create a custom Ribbon UI

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

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

  3. Change the name of the new Ribbon support item to Ribbon, and then click Add.

    The Ribbon.cs or Ribbon.vb file opens in the designer. An XML file that is named Ribbon.xml is also added to your project.

  4. Add the following statements to the top of the Ribbon.cs or Ribbon.vb file.

    Imports Outlook = Microsoft.Office.Interop.Outlook
    Imports Microsoft.Office.Tools
    
    using Outlook = Microsoft.Office.Interop.Outlook;
    using Microsoft.Office.Tools;
    
  5. Locate the code comments that start with TODO:, and uncomment the definition of the ThisAddIn class. This code enables Outlook to discover and load your custom Ribbon UI. For more information, see Ribbon Extensibility Overview.

  6. In the code that you uncommented in the previous step, change the declaration of the ribbon field from private to public so that other classes can use it.

    After you complete this step, the uncommented code should resemble the following example.

    Partial Public Class ThisAddIn
    
        Public ribbon As Ribbon
    
        Protected Overrides Function RequestService(ByVal serviceGuid As Guid) As Object
            If serviceGuid = GetType(Office.IRibbonExtensibility).GUID Then
                If ribbon Is Nothing Then
                    ribbon = New Ribbon()
                End If
                Return ribbon
            End If
    
            Return MyBase.RequestService(serviceGuid)
        End Function
    
    End Class
    
    public partial class ThisAddIn
    {
        public Ribbon ribbon;
    
        protected override object RequestService(Guid serviceGuid)
        {
            if (serviceGuid == typeof(Office.IRibbonExtensibility).GUID)
            {
                if (ribbon == null)
                    ribbon = new Ribbon();
                return ribbon;
            }
    
            return base.RequestService(serviceGuid);
        }
    }
    

Specifying Callback Methods for the Ribbon Toggle Button

One of the goals for this add-in is to give users a way to hide or display the custom task pane from the Ribbon of each e-mail message. To provide the user interface, you will specify the names of callback methods for the default Ribbon toggle button by modifying the Ribbon XML file. Later in this walkthrough, you will define these callback methods in the Ribbon code file.

To specify callback methods for the toggle button

  1. In Solution Explorer, right-click Ribbon.xml, and then click Open.

  2. Replace the contents of the tab element with the following XML. This XML changes the label of the default control group, and it specifies callback methods for the onAction and getPressed attributes of the default toggle button.

    <tab idMso="TabAddIns">
        <group id="MyGroup"
               label="Task Pane">
            <toggleButton id="toggleTaskPane" 
                          size="large"
                          label="Show/Hide Task Pane"
                          screentip="Show/Hide Task Pane"
                          onAction="OnToggleTaskPane" 
                          getPressed="GetPressedState"
                          imageMso="HappyFace" />
        </group>
    </tab>
    

Synchronizing the Ribbon Toggle Button with the Custom Task Pane

The toggle button will appear to be pressed in when the task pane is visible. To synchronize the state of the button with the custom task pane, follow these steps:

  • Display your custom Ribbon UI only with Inspector windows that contain e-mail messages.

  • Define new callback methods for the toggle button.

  • Expose the Ribbon UI programmatically to other classes in the add-in.

To display your custom Ribbon UI only with e-mail messages

  1. In the Ribbon.cs or Ribbon.vb file, locate the definition of the Ribbon class.

  2. Replace the existing definition of the GetCustomUI method with the following code. This version of the method checks the value of the ribbonID parameter, and returns the contents of the Ribbon XML file only if the ribbonID parameter identifies an e-mail message.

    Public Function GetCustomUI(ByVal ribbonID As String) As String Implements Office.IRibbonExtensibility.GetCustomUI
        Select Case ribbonID
        Case "Microsoft.Outlook.Mail.Read", "Microsoft.Outlook.Mail.Compose"
            Return GetResourceText("OutlookMailItemTaskPane.Ribbon.xml")
        Case Else
            Return String.Empty
        End Select
    End Function
    
    public string GetCustomUI(string ribbonID)
    {
        switch (ribbonID)
        {
            case "Microsoft.Outlook.Mail.Read":
            case "Microsoft.Outlook.Mail.Compose":
                return GetResourceText("OutlookMailItemTaskPane.Ribbon.xml");
    
            default:
                return String.Empty;
        }
    }
    

To define callback methods for the toggle button

  1. In the Ribbon class, replace the default OnToggleButton1 method with the following code. This is a callback method for the onAction attribute. When the user clicks the toggle button, this method hides or displays the custom task pane that is associated with the current Inspector window.

    Public Sub OnToggleTaskPane(ByVal control As Office.IRibbonControl, ByVal isPressed As Boolean)
        Dim inspector As Outlook.Inspector = control.Context
        Dim inspectorWrapper As InspectorWrapper = Globals.ThisAddIn.inspectorWrappers(inspector)
        Dim taskPane As CustomTaskPane = inspectorWrapper.CustomTaskPane
        If Not (taskPane Is Nothing) Then
            taskPane.Visible = isPressed
        End If
    End Sub
    
    public void OnToggleTaskPane(Office.IRibbonControl control, bool isPressed)
    {
        Outlook.Inspector inspector = (Outlook.Inspector)control.Context;
        InspectorWrapper inspectorWrapper = Globals.ThisAddIn.inspectorWrappers[inspector];
        CustomTaskPane taskPane = inspectorWrapper.CustomTaskPane;
        if (taskPane != null)
        {
            taskPane.Visible = isPressed;
        }
    }
    
  2. Add the following method to the Ribbon class. This is a callback method for the getPressed attribute. When the UI of the toggle button is refreshed, this method returns true or false, depending on whether the custom task pane is visible. Outlook uses this return value to determine how to draw the toggle button.

    Public Function GetPressedState(ByVal control As Office.IRibbonControl) As Boolean
        Dim inspector As Outlook.Inspector = control.Context
        If Globals.ThisAddIn.inspectorWrappers.ContainsKey(inspector) Then
            Dim inspectorWrapper As InspectorWrapper = Globals.ThisAddIn.inspectorWrappers(inspector)
            Dim taskPane As CustomTaskPane = inspectorWrapper.CustomTaskPane
    
            If taskPane IsNot Nothing Then
                Return taskPane.Visible
            Else
                Return False
            End If
        Else
            Return False
        End If
    End Function
    
    public bool GetPressedState(Office.IRibbonControl control)
    {
        Outlook.Inspector inspector = (Outlook.Inspector)control.Context;
        if (Globals.ThisAddIn.inspectorWrappers.ContainsKey(inspector))
        {
            InspectorWrapper inspectorWrapper = Globals.ThisAddIn.inspectorWrappers[inspector];
            CustomTaskPane taskPane = inspectorWrapper.CustomTaskPane;
    
            if (null != taskPane)
            {
                return taskPane.Visible;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    

To expose the Ribbon UI programmatically to other classes in the add-in

  1. Add the following method to the Ribbon class. Later in this walkthrough, you will call this method from the InspectorWrapper and ThisAddIn classes to refresh the state of the toggle button when the custom task pane is opened or closed.

    Public Sub RefreshControl(ByVal controlID As String)
        ribbon.InvalidateControl(controlID)
    End Sub
    
    public void RefreshControl(string controlID)
    {
        ribbon.InvalidateControl(controlID);
    }
    
  2. Add the following property to the Ribbon class. Later in this walkthrough, you will add code to the ThisAddIn class that uses this property to determine whether the Ribbon UI has been successfully initialized.

    Public ReadOnly Property RibbonUI() As Office.IRibbonUI
        Get
            Return ribbon
        End Get
    End Property
    
    public Office.IRibbonUI RibbonUI
    {
        get 
        {
            return ribbon;
        }
    }
    

Checkpoint

Build and run your project to ensure that it is working correctly.

To build and run your project

  1. In Solution Explorer, right-click the OutlookMailItemTaskPane project and then click Build. Verify that the project builds without errors.

  2. Press F5.

  3. In Outlook, click New to create a new e-mail message.

    On the Ribbon of the e-mail message, verify that the Add-Ins tab is visible.

  4. Click the Add-Ins tab.

    Verify that the Show/Hide Task Pane button is visible on the Ribbon. This button does nothing when you click it. Later in this walkthrough, you will add code to make this button display or hide the task pane.

  5. Close the e-mail message.

  6. Exit Outlook.

Updating the Toggle Button When the User Opens or Closes the Custom Task Pane

The VisibleChanged event handler that you defined earlier is called when the custom task pane opens and when it closes. You can use this event to refresh the state of the toggle button.

To update the toggle button when the user opens or closes the custom task pane

  1. In the ThisAddIn.cs or ThisAddIn.vb file, locate the TaskPane_VisibleChanged method in the InspectorWrapper class.

  2. Replace the TaskPane_VisibleChanged method with the following code. This code calls the RefreshControl method to update the state of the toggle button whenever the user opens or closes the custom task pane.

        Sub TaskPane_VisibleChanged(ByVal sender As Object, ByVal e As EventArgs) Handles taskPane.VisibleChanged
            Globals.ThisAddIn.ribbon.RefreshControl("toggleTaskPane")
        End Sub
    
        void TaskPane_VisibleChanged(object sender, EventArgs e)
        {
            Globals.ThisAddIn.ribbon.RefreshControl("toggleTaskPane");
        }
    

Creating a Custom Task Pane for Every E-Mail Message

The Microsoft.Office.Interop.Outlook.Inspectors.NewInspector event handler that you defined earlier is called when a new Inspector window opens. Add code to this event handler to create a custom task pane if the new Inspector window contains an e-mail message.

To create a custom task pane for every e-mail message

  1. In the ThisAddIn.cs or ThisAddIn.vb file, locate the Inspectors_NewInspector method in the ThisAddIn class.

  2. Replace the Inspectors_NewInspector method with the following method. If a new Microsoft.Office.Interop.Outlook.Inspector contains an e-mail message, the method creates an instance of a new InspectorWrapper object to manage the relationship between the e-mail message and the corresponding task pane. This method also updates the toggle button so that it appears to be pressed in.

    Sub Inspectors_NewInspector(ByVal Inspector As Outlook.Inspector) _
        Handles inspectors.NewInspector
    
        If TypeOf Inspector.CurrentItem Is Outlook.MailItem Then
            inspectorWrappers.Add(Inspector, New InspectorWrapper(Inspector))
    
            If ribbon IsNot Nothing AndAlso ribbon.RibbonUI IsNot Nothing Then
                ribbon.RefreshControl("toggleTaskPane")
            End If
        End If
    End Sub
    
    void Inspectors_NewInspector(Outlook.Inspector Inspector)
    {
        if (Inspector.CurrentItem is Outlook.MailItem)
        {
            inspectorWrappers.Add(Inspector, new InspectorWrapper(Inspector));
    
            if (null != ribbon && null != ribbon.RibbonUI)
            {
                ribbon.RefreshControl("toggleTaskPane");
            }
        }
    }
    

Testing the Project

When you start debugging the project, Outlook starts and the add-in is loaded. The add-in displays a unique instance of the custom task pane with each e-mail message that is opened. Create several new e-mail messages to test the code.

To test the add-in

  1. Press F5.

  2. In Outlook, click New to create a new e-mail message.

    Verify that a task pane with the title My task pane is displayed with the e-mail message.

  3. In the task pane, type First task pane in the text box.

  4. Close the task pane by clicking the Close button (X) in the corner of the task pane.

  5. On the Ribbon of the e-mail message, click the Add-Ins tab, and then click the Show/Hide Task Pane button.

    Verify that the task pane opens, and that the text box still contains the string First task pane.

  6. In Outlook, click New to create a second e-mail message.

    Verify that a task pane with the title My task pane is displayed with the e-mail message, and that the text box in this task pane is empty.

  7. In the task pane, type Second task pane in the text box.

  8. Change focus to the first e-mail message.

    Verify that the task pane that is associated with this e-mail message still displays First task pane in the text box.

This add-in also handles more advanced scenarios that you can try. For example, you can test the behavior when viewing e-mails by using the Next Item and Previous Item buttons, and when you unload the add-in, open several e-mail messages, and then load the add-in.

Next Steps

You can learn more about how to create custom task panes from these topics:

See Also

Tasks

How to: Add a Custom Task Pane to an Application
How to: Display Custom Task Panes with E-Mail Messages in Outlook

Concepts

Custom Task Panes Overview