C# – Outlook Ribbon Load Inspector.CurrentItem is null

coutlook-addinvsto

Overview

I have an Outlook add-in created with VSTO. The add-in has a single ribbon (visual designer) for the Mail.Compose ribbon type. The ribbon tab ControlIdType is set to "Custom". The only code in the add-in other than designer code is the following Load handler for the ribbon. this.Context.CurrentItem is unexpectedly returning null.

Code

private void RibbonComposeMail_Load(object sender, RibbonUIEventArgs e)
{
    try
    {
        var inspector = this.Context as Outlook.Inspector;
        if (inspector == null)
        {
            throw new ApplicationException("Fail - Step 1");
        }

        var currentMailItem = inspector.CurrentItem as Outlook.MailItem;
        if (currentMailItem == null)
        {
            throw new ApplicationException("Fail - Step 2");
        }

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Steps

  1. Open draft email. Ribbon loads OK.
  2. Open email from inbox.
  3. Open same draft email. Ribbon fails at step 2, inspector.CurrentItem is null.

Notes

  • I've tested this in Outlook 2007, 2010 and 2013, with an Outlook 2007 and 2010 add-in created in VS2010, and Outlook 2010 add-in created in VS2012. All behave the same.
  • Repeatedly opening the draft email doesn't appear to cause the issue, an Email.Read inspector has to be opened in between.
  • The ribbon tab ControlidType matters. "Custom" will cause the issue, but the default option of "Office" doesn't exhibit the issue.
  • Flipping the scenario on its head and setting the ribbon type to Mail.Read gives the same result, provided the sequence of opening is reversed to Inbox > Draft > Inbox (fail).
  • All possible permutations of calls to Marshal.ReleaseComObject on the inspector and currentMailItem objects makes no difference.

Best Answer

I had the same issue myself.

I have designed a Ribbon Bar for Outlook Calendar appointments, with a few extra fields that I wanted to save with each appointment (eg "Does this Meeting save travel ?")

I managed it, but, it was tricky to do.

As you've said, when your Ribbon1_Load function gets launched, the ActiveInspector() is null... so how are you supposed to get details about the current Email message or Calendar appointment ?

Here's what you need to do. This example is based about Calendar appointments, but it's very easy to adapt to EmailItems instead.

First, in the ThisAddIn.cs file, you need to make a few changes:

public partial class ThisAddIn
{
    Outlook.Inspectors inspectors;
    public static Outlook.AppointmentItem theCurrentAppointment;

    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        inspectors = this.Application.Inspectors;
        inspectors.NewInspector += new Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
    }

When the user opens or creates a new Outlook item, our "Inspectors_NewInspector" function will get called, and at that point, we are able to get details about the item:

void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
{
     //  This function (apparently) gets kicked off whenever a user opens a new or existing item
     //  in Outlook (Calendar appointment, Email, etc).  
     //  We can intercept it, modify it's properties, before letting our Ribbon know about it's existance.
     //
     theCurrentAppointment = null;

     object item = Inspector.CurrentItem;
     if (item == null)
         return;

     if (!(item is Outlook.AppointmentItem))
         return;

     theCurrentAppointment = Inspector.CurrentItem as Outlook.AppointmentItem;
}

With this code in place, we can adapt our Ribbon1_Load function to take this "theCurrentAppointment" variable, and read in details about the Calendar appointment which the user is creating/modifying.

private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
{
     //  When this function gets called, "Globals.ThisAddIn.Application.ActiveInspector()" is always NULL, so we have
     //  to fetch the selected AppointmentItem via the ThisAddIn class.

     if (ThisAddIn.theCurrentAppointment != null)
     {
         //  Our Ribbon control contains a TextBox called "tbSubject"
         tbSubject.Text = ThisAddIn.theCurrentAppointment.Subject
     }
}
Related Topic