问题
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
- Open draft email. Ribbon loads OK.
- Open email from inbox.
- 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 theinspector
andcurrentMailItem
objects makes no difference.
回答1:
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.
回答2:
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
}
}
回答3:
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];
来源:https://stackoverflow.com/questions/18458338/outlook-ribbon-load-inspector-currentitem-is-null