【问题标题】:Outlook Ribbon Load Inspector.CurrentItem is nullOutlook Ribbon Load Inspector.CurrentItem 为空
【发布时间】:2013-08-27 06:22:51
【问题描述】:

概述

我有一个使用 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 会产生相同的结果,前提是打开顺序相反为收件箱 > 草稿 > 收件箱(失败)。
  • inspectorcurrentMailItem 对象上调用Marshal.ReleaseComObject 的所有可能排列都没有区别。

【问题讨论】:

  • 其他人至少可以复制错误吗?据我所知,我没有做错任何事。这看起来是获得与功能区关联的项目的公认方式,但它失败了!甚至这个 VSTOTeam 博客也使用了几乎相同的技术 - blogs.msdn.com/b/vsto/archive/2010/02/23/… - 我尝试了他们的代码,只是略有不同,它也显示了错误。这可能是最近 3 个 Outlook 版本中一直存在但未被注意到的错误吗?

标签: c# vsto outlook-addin


【解决方案1】:

我自己也有同样的问题。

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

【讨论】:

  • 谢谢 Mike,我想知道切换到 Inspector 事件处理程序/包装器模型是否是留给我的唯一选择,但是调查您提出的技术向我揭示了更多关于正在发生的事情上。有关更多详细信息,请参阅我自己的答案。
【解决方案2】:

Mikes cmets 帮助我向我揭示了一些对这种行为更加好奇的东西。在第 1 步,RibbonComposeMail_Load 事件被调用一次。但在第 3 步,它被称为 两次。第一次在第 3 步调用事件时,this.Context.CurrentItem 为 null,但第二次调用事件时,属性保存电子邮件。

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

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

【讨论】:

    【解决方案3】:

    我不是 100% 确定,但听起来垃圾收集器已经清除了你的 this.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];
    

    【讨论】:

    • this.ContextRibbonBase 类的属性,它不是我自己创建的类 - msdn.microsoft.com/en-AU/library/… 使用 Application.ActiveExplorer().Selection 也没有真正加起来,因为我正在使用Inspector,而不是 Explorer。加载时甚至可能没有活动的Explorer
    • 我还尝试将 this.Context as Outlook.Inspector 切换到 Globals.ThisAddIn.Application.ActiveInspector(),有趣的是,在我的代码在第 2 步失败的相同情况下,它在第 1 步失败,这意味着 ActiveInspector() 是空。
    猜你喜欢
    • 2021-01-23
    • 1970-01-01
    • 2017-04-23
    • 1970-01-01
    • 2019-04-17
    • 2018-10-26
    • 2019-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多