Snixtor
Snixtor

Reputation: 4297

Outlook Ribbon Load Inspector.CurrentItem is null

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

Upvotes: 2

Views: 6880

Answers (3)

Mike Gledhill
Mike Gledhill

Reputation: 29161

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
     }
}

Upvotes: 4

Snixtor
Snixtor

Reputation: 4297

Mikes comments helped reveal to me something a little more curious about the behaviour. At step 1, the RibbonComposeMail_Load event is called once. But at step 3 it is called twice. The first time the event is called at step 3, this.Context.CurrentItem is null, but the second time the event is called, the property holds the email.

It was comparing item values in the NewInspector event with those in the ribbon Load event which made me notice this. Because the sequence of events at step 3 is: Ribbon_Load, NewInspector, Ribbon_Load. I was getting Ribbon_Load to MessageBox.Show the subject of the mail item in ThisAddIn.CurrentMailItem, but was quite surprised to see it was the subject of the previous email opened, i.e. the inbox email at step 2!

As it turns out, the solution then is to ignore everything in the Ribbon_Load event if this.Context.CurrentItem is null, because a second Ribbon_Load event is about to be triggered with the correct values set. As to why we see this strange behaviour in step 3 of my example, but not step 1? That's probably a question for the people the implemented the Outlook Object Model.

Upvotes: 3

Poku
Poku

Reputation: 3178

I'm not 100% sure, but it sounds like the garbage collector have cleared your this.Context.

Could you try to put your Context in a private field and get the currentitem from it:

private readonly Context _context = new Context(); 


private void RibbonComposeMail_Load(object sender, RibbonUIEventArgs e)
{
    try
    {
        var inspector = _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);
    }
}

If this doesn't help, i normaly use the Outlook.Application object to get the currentitem:

readonly Outlook._Application _application = new Outlook.Application();

var selectionList = _application.ActiveExplorer().Selection;

foreach (Object selObject in selectionList)
{
    if (selObject is Outlook.MailItem)
    {
        var outlookMail = (selObject as Outlook.MailItem);
    }
}

Or if you only need the currentitem, as you do in your case:

var mailItem = _application.ActiveExplorer().Selection[0];

Upvotes: 0

Related Questions