Context menus

Aug 8, 2012 at 12:36 PM

Hi,

I want to add some context menu to Outlook and I have problem.

View:

<customUI onLoad="Ribbon_Load" xmlns="http://schemas.microsoft.com/office/2009/07/customui">
  <contextMenus>
    <contextMenu idMso="ContextMenuMailItem">
      <menu id="AsiSubmenu" label="Save">
        <button
          id="Button1"
          label="Add"
          onAction="GetActionAdd"
          />
        <button
          id="Button2"
          label="Append"
          onAction="GetActionAppend"
          />
      </menu>
    </contextMenu>
  </contextMenus>
</customUI>

 I defined methods for actions on VM, but when i press button i get exception because method  GetContextForView returns null in OutlookViewContextProvider.cs

 

What am i doing wrong?

Coordinator
Aug 10, 2012 at 2:00 AM
Sounds like a bug, will try to look into it this weekend

Sent from my Windows Phone

From: rm1337
Sent: 8/08/2012 7:36 PM
To: Jake Ginnivan
Subject: Context menus [vstocontrib:390693]

From: rm1337

Hi,

I want to add some context menu to Outlook and I have problem.

View:

<customUI onLoad="Ribbon_Load" xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<contextMenus>
<contextMenu idMso="ContextMenuMailItem">
<menu id="AsiSubmenu" label="Save">
<button
id="Button1"
label="Add"
onAction="GetActionAdd"
/>
<button
id="Button2"
label="Append"
onAction="GetActionAppend"
/>
</menu>
</contextMenu>
</contextMenus>
</customUI>

I defined methods for actions on VM, but when i press button i get exception because method GetContextForView returns null in OutlookViewContextProvider.cs

What am i doing wrong?

Aug 14, 2012 at 1:50 PM

In method GetRibbonElements() elements menu and contextMenu is not defined.

Aug 16, 2012 at 3:50 PM

VSTOContrib.Outlook.RibbonFactory.OutlookViewContextProvider.GetContextForView(object view) returns null because the object that is provided to the method implements Microsoft.Office.Interop.Outlook.Selection, not Microsoft.Office.Interop.Outlook.Explorer. Therefore the cast fails and returns null.

I also had this issue and implemented a work-around that is not as nice as I would like:

Modified VSTOContrib.Outlook.RibbonFactory.OutlookViewContextProvider (comments removed):

    public class OutlookViewContextProvider : IViewContextProvider
    {
        public object GetContextForView(object view)
        {
            var inspector = view as Inspector;
            if (inspector != null)
                return inspector.CurrentItem;

            var explorer = view as Explorer;
            if (explorer != null)
                return explorer.CurrentFolder;

            var selection = view as Selection;
            if (selection != null)
                return selection;

            return null;
        }

        public TRibbonType GetRibbonTypeForView<TRibbonType>(object view)
        {
            if (view is Explorer)
                return (TRibbonType)(object)OutlookRibbonType.OutlookExplorer;

            if (view is Selection)
                return (TRibbonType)(object)OutlookRibbonType.OutlookSelection;
            
            return (TRibbonType) (object) InspectorToRibbonTypeConverter.Convert((Inspector) view);
        }
    }

Modified VSTOContrib.Outlook.RibbonFactory.OutlookRibbonType to add the required ribbon type:

    [Flags]
    public enum OutlookRibbonType
    {
        //...enumerations elided...
        [Description("Microsoft.Outlook.Task")]
        OutlookTask = 1 << 19,
        /// <summary>
        /// olSelection: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.olobjectclass
        /// </summary>
        [Description("Microsoft.Outlook.Selection")]
        OutlookSelection = 1 << 20,
    }

Created a ViewModel for the execution of the context menu which implements IRibbonViewModel and has the appropriate attribute:

[RibbonViewModel(OutlookRibbonType.OutlookSelection)]
    public class ContextMenuViewModel : OfficeViewModelBase, IRibbonViewModel
    {
        // ... code removed ...
        public void Initialised(object context)
        {
            Outlook.Selection selection = (Outlook.Selection)context;
        }
    }


And then implemented the ribbon xml as ContextMenuViewModel.xml because VSTO Contrib requires this file because the xml is looked up based on the loaded ViewModel:

<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
<!-- context menus must be in the Explorer ViewModel xml file in order to appear 
and they must also appear in this file in order to not have various exceptions -->
  <contextMenus>
    <contextMenu idMso="ContextMenuMailItem">
      <button id="MyContextMenuMailItem"
              label="Perform the action..."
              onAction="EmailItems"
              imageMso="Forward"/>
    </contextMenu>
    <contextMenu idMso="ContextMenuMultipleItems">
      <button id="MyContextMenuMultipleItems"
          label="Perform the action on multiple items..."
          onAction="EmailItems"
          imageMso="Forward"/>
    </contextMenu>
  </contextMenus>
</customUI>

But as you can see from the comment in the xml file I also needed to add the contextMenus in the ExplorerViewModel.xml - otherwise they would not be loaded or appear.

However, this is not a very satisfactory result since it's hacked together and probably has other, unwanted, side effects.

I'd be very interested in a real solution, looking forward to your suggestions Jake!

 

Coordinator
Aug 22, 2012 at 3:28 AM

Thanks for the investigation! It really saved me a heap of time.

The trick is that when you have a selection, we actually want to resolve the Selections parents context.

The fix was actually quite simple, and you led me straight to it!

    public object GetContextForView(object view)
    {
        var inspector = view as Inspector;
        if (inspector != null)
            return inspector.CurrentItem;

        var explorer = view as Explorer;
        if (explorer != null)
            return explorer.CurrentFolder;

        var selection = view as Selection;
        if (selection != null)
            return GetContextForView(selection.Parent);

        return null;
    }

 

I have blogged the end result at http://jake.ginnivan.net/vstocontrib-outlook-contextmenus, updated NuGet packages will be released later today

Aug 23, 2012 at 10:06 AM
Edited Aug 23, 2012 at 3:09 PM

Thanks for fix.

This solution throws me an exception, whn clicking on the other folders, for example calendars, appointments, and then back to Inbox, and right click..

There can be only one view model for [RibbonViewModel(OutlookRibbonType.OutlookExplorer)], but contextMenus and ribbon element can't be in the same view (xml). How can I use two views and one view model?

Coordinator
Aug 24, 2012 at 12:30 AM

They must be defined in the same xml, which would also be the case if doing native VSTO extensions. See http://stackoverflow.com/questions/6652837/how-to-have-a-vsto-ribbon-and-context-menu-at-the-same-time

What is the exception? Shouldnt be too hard to fix.

Aug 24, 2012 at 1:32 PM

Ok, it was xml namespace problem.

 

Exception is at OutlookViewContextProvider.cs (last return)

        public TRibbonType GetRibbonTypeForView<TRibbonType>(object view)
        {
            if (view is Explorer)
                return (TRibbonType)(object)OutlookRibbonType.OutlookExplorer;

            return (TRibbonType) (object) InspectorToRibbonTypeConverter.Convert((Inspector) view);
        }

Exception message:

Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.Office.Interop.Outlook.Inspector'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{00063005-0000-0000-C000-000000000046}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Stack trace:

   at VSTOContrib.Outlook.RibbonFactory.OutlookViewContextProvider.GetRibbonTypeForView[TRibbonType](Object view) in d:\Dev\Personal\vstocontrib\src\VSTOContrib.Outlook\RibbonFactory\OutlookViewContextProvider.cs:line 44
   at VSTOContrib.Core.RibbonFactory.Internal.ViewModelResolver`1.ResolveInstanceFor(Object view) in d:\Dev\Personal\vstocontrib\src\VSTOContrib.Core\RibbonFactory\Internal\ViewModelResolver.cs:line 111
   at VSTOContrib.Core.RibbonFactory.RibbonFactoryController`1.InvokeGet(IRibbonControl control, Expression`1 caller, Object[] parameters) in d:\Dev\Personal\vstocontrib\src\VSTOContrib.Core\RibbonFactory\RibbonFactoryController.cs:line 216
   at VSTOContrib.Core.RibbonFactory.RibbonFactory.GetVisible(IRibbonControl control) in d:\Dev\Personal\vstocontrib\src\VSTOContrib.Core\RibbonFactory\RibbonFactory.cs:line 236

 

Coordinator
Aug 25, 2012 at 4:49 AM

Thanks for the support and reports.

Fixed this, uploading NuGet package now. Cheers!

Aug 28, 2012 at 1:59 PM

Thanks for quick solution:)

On viewmodel now currentView can be Explorer or Selection and this must be properly handled. 

public void CurrentViewChanged(object currentView)    
{        
      explorer = (Explorer) currentView;    
}