2

概述

我有一个使用 VSTO 创建的 Outlook 加载项。Mail.Compose加载项具有用于功能区类型的单个功能区(视觉设计器) 。功能区选项卡ControlIdType设置为“自定义”。除了设计器代码外,加载项中的唯一代码是Load功能区的以下处理程序。this.Context.CurrentItem意外返回 null。

代码

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

脚步

  1. 打开草稿电子邮件。色带加载正常。
  2. 从收件箱打开电子邮件。
  3. 打开相同的草稿电子邮件。功能区在第 2 步失败,inspector.CurrentItem为空。

笔记

  • 我在 Outlook 2007、2010 和 2013 中对此进行了测试,在 VS2010 中创建了 Outlook 2007 和 2010 插件,在 VS2012 中创建了 Outlook 2010 插件。所有行为都相同。
  • 反复打开电子邮件草稿似乎不会导致问题,必须在两者之间打开一个 Email.Read 检查器。
  • 功能区选项卡ControlidType很重要。“自定义”会导致问题,但“Office”的默认选项不会出现问题。
  • Mail.Read如果将打开的顺序颠倒为收件箱 > 草稿 > 收件箱(失败),则翻转场景并将功能区类型设置为给出相同的结果。
  • Marshal.ReleaseComObjectinspectorand对象的调用的所有可能排列currentMailItem都没有区别。
4

3 回答 3

4

我自己也有同样的问题。

我为 Outlook 日历约会设计了一个功能区栏,其中包含一些我想在每个约会中保存的额外字段(例如“此会议是否可以节省旅行?”)

我做到了,但是,做起来很棘手。

正如您所说,当您的Ribbon1_Load函数启动时,ActiveInspector()为空......那么您应该如何获取有关当前电子邮件或日历约会的详细信息?

这是你需要做的。此示例基于日历约会,但很容易改用 EmailItems。

首先,在 ThisAddIn.cs 文件中,您需要进行一些更改:

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

当用户打开或创建一个新的 Outlook 项目时,我们的“ Inspectors_NewInspector ”函数将被调用,此时,我们能够获取有关该项目的详细信息:

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

有了这个代码,我们可以调整我们的Ribbon1_Load函数来获取这个“theCurrentAppointment”变量,并详细了解用户正在创建/修改的日历约会。

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
     }
}
于 2013-09-12T14:29:11.437 回答
3

Mikes 的评论帮助我发现了一些对这种行为更加好奇的东西。在第 1 步,RibbonComposeMail_Load事件被调用一次。但是在第 3 步,它被调用了两次。第一次在步骤 3 调用事件时this.Context.CurrentItem为空,但第二次调用事件时,该属性保存电子邮件。

它将事件中的项目值NewInspector与功能区Load事件中的项目值进行比较,这让我注意到了这一点。因为第 3 步的事件顺序是:Ribbon_Load, NewInspector, Ribbon_Load. 我正在Ribbon_Load处理MessageBox.Show中邮件项目的主题ThisAddIn.CurrentMailItem,但很惊讶地发现它是之前打开的电子邮件的主题,即步骤 2 中的收件箱电子邮件!

事实证明,Ribbon_Load如果this.Context.CurrentItem为空,则解决方案是忽略事件中的所有内容,因为Ribbon_Load将使用正确的值设置触发第二个事件。至于为什么我们在我的示例的第 3 步中看到这种奇怪的行为,而不是第 1 步?对于实施 Outlook 对象模型的人来说,这可能是一个问题。

于 2013-09-16T02:13:45.977 回答
0

我不是 100% 确定,但听起来垃圾收集器已经清除了你的 this.Context。

您能否尝试将您的 Context 放在一个私有字段中并从中获取当前项:

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

如果这没有帮助,我通常使用 Outlook.Application 对象来获取当前项目:

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

或者,如果您只需要当前项目,就像您的情况一样:

var mailItem = _application.ActiveExplorer().Selection[0];
于 2013-08-27T09:11:07.410 回答