Инспектор загрузки ленты Outlook.CurrentItem имеет значение null

Обзор

У меня есть надстройка Outlook, созданная с помощью VSTO. Надстройка имеет одну ленту (визуальный конструктор) для типа ленты Mail.Compose. На вкладке ленты ControlIdType установлено значение «Пользовательский». Единственный код в надстройке, кроме кода конструктора, - это следующий Load обработчик ленты. this.Context.CurrentItem неожиданно возвращает ноль.

Код

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 имеет значение null.

Примечания

  • Я тестировал это в Outlook 2007, 2010 и 2013 с надстройкой Outlook 2007 и 2010, созданной в VS2010, и надстройкой Outlook 2010, созданной в VS2012. Все ведут себя одинаково.
  • Повторное открытие черновика электронного письма не вызывает проблемы, между ними должен быть открыт Инспектор электронной почты.
  • Вкладка ленты ControlidType имеет значение. «Custom» вызовет проблему, но вариант «Office» по умолчанию не обнаруживает проблемы.
  • Переворачивание сценария с ног на голову и установка типа ленты на Mail.Read дает тот же результат при условии, что последовательность открытия обратная: «Входящие»> «Черновик»> «Входящие» (сбой).
  • Все возможные перестановки вызовов Marshal.ReleaseComObject на объектах inspector и currentMailItem не имеют значения.

person Snixtor    schedule 27.08.2013    source источник
comment
Может ли кто-нибудь еще хотя бы повторить ошибку? Насколько я понимаю, я ничего плохого не делаю. Кажется, это приемлемый способ получить элемент, связанный с лентой, но это не удается! Даже в этом блоге VSTOTeam используется почти такая же техника - blogs.msdn.com/b/vsto/archive/2010/02/23/ - я попробовал их код, который немного отличается, и он тоже выставил ошибку. Может быть, это ошибка, которую не заметили в последних трех выпусках Outlook?   -  person Snixtor    schedule 28.08.2013


Ответы (3)


Комментарии Майка помогли мне открыть кое-что более любопытное в поведении. На шаге 1 событие RibbonComposeMail_Load вызывается один раз. Но на шаге 3 он вызывается дважды. В первый раз, когда событие вызывается на шаге 3, this.Context.CurrentItem имеет значение null, но во второй раз, когда событие вызывается, свойство содержит электронное письмо.

Я заметил это при сравнении значений элементов в событии NewInspector со значениями в событии Load на ленте. Потому что последовательность событий на шаге 3: Ribbon_Load, NewInspector, Ribbon_Load. Я получал Ribbon_Load по MessageBox.Show тему почтового элемента в ThisAddIn.CurrentMailItem, но был очень удивлен, увидев, что это была тема предыдущего открытого электронного письма, т.е. почтового ящика на шаге 2!

Как выясняется, решение состоит в том, чтобы игнорировать все в событии Ribbon_Load, если this.Context.CurrentItem имеет значение null, потому что второе событие Ribbon_Load вот-вот будет запущено с заданными правильными значениями. Что касается почему мы видим это странное поведение на шаге 3 моего примера, но не на шаге 1? Вероятно, это вопрос к людям, реализовавшим объектную модель Outlook.

person Snixtor    schedule 16.09.2013

У меня была такая же проблема.

Я разработал ленточную панель для встреч в календаре Outlook с несколькими дополнительными полями, которые я хотел сохранить при каждой встрече (например, «Сохраняет ли эта встреча поездки?»)

Мне это удалось, но сделать это было непросто.

Как вы сказали, когда ваша функция Ribbon1_Load запускается, ActiveInspector () имеет значение null ... так как вы должны получить подробную информацию о текущем сообщении электронной почты или Встреча в календаре?

Вот что вам нужно сделать. Этот пример основан на встречах в календаре, но вместо этого его очень легко адаптировать к 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
     }
}
person Mike Gledhill    schedule 12.09.2013
comment
Спасибо, Майк, я задавался вопросом, будет ли переключение на Inspector модель обработчика / оболочки события единственным оставшимся мне вариантом, но изучение предложенной вами техники раскрыло мне немного больше о том, что происходит. См. Мой собственный ответ для более подробной информации. - person Snixtor; 16.09.2013

Я не уверен на 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];
person Poku    schedule 27.08.2013
comment
this.Context - это свойство класса RibbonBase, это не мой собственный класс - msdn.microsoft.com/en-AU/library/ Использование Application.ActiveExplorer().Selection тоже не помогает, поскольку я использую Inspector, а НЕ Explorer. Может даже не быть активного Explorer при загрузке. - person Snixtor; 28.08.2013
comment
Я также попытался переключить this.Context as Outlook.Inspector на Globals.ThisAddIn.Application.ActiveInspector(), и, что интересно, в тех же сценариях, где мой код не удался на шаге 2, вместо этого он потерпел неудачу на шаге 1, то есть ActiveInspector() был нулевым. - person Snixtor; 28.08.2013