0

I am trying to add a User control on Microsoft.Office.Tools.CustomTaskPane myCustomTaskPane by using following line:

myUserControl = new IssueAddition(categoryList);
myCustomTaskPane = this.CustomTaskPanes.Add(myUserControl, "Issue Reporting Pane");  --> errornous line
myCustomTaskPane.Visible = true;

I am getting the below exception:

 An exception of type 'System.InvalidCastException' occurred in Microsoft.Office.Tools.Common.Implementation.dll but was not handled in user code
Additional information: 
Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.VisualStudio.Tools.Office.Runtime.Interop.ICustomTaskPaneSite'.
This operation failed because the QueryInterface call on the COM component for the interface with IID '{3CA8CD11-274A-41B6-A999-28562DAB3AA2}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

I am unsure about what could be the cause for System.InvalidCastException exception.

Edit 1: Adding the code of the latest implmentation which is also giving the same error: I have a separate class,IssueAddition inheriting System.Windows.Forms.UserControl class like below:

   public partial class IssueAddition : UserControl
    {
        public IssueAddition()
        {
            InitializeComponent();
        }

        public IssueAddition(List<String> categories)
        {
            InitializeComponent();
            foreach (string cat in categories)
                this.i_issue_category.Items.Add(cat);
        }
        public string IssueCategoryInputText
        {
            get { return (string)this.i_issue_category.SelectedItem; }
            set { this.i_issue_category.SelectedItem = 
            this.i_issue_category.FindStringExact(value); }
        }
        public string MessageInputText
        {
            get { return this.i_error_message.Text; }
            set { this.i_error_message.Text = value; }
        }
}

And it's being used in ThisAddIn.cs like below:

Declaration:

#region Instance Variables

        Outlook.Application m_Application;
        internal static List<Outlook.Explorer> m_Windows; // List of tracked explorer windows.
        internal static List<Outlook.Inspector> m_InspectorWindows;// List of traced inspector windows.
        private IssueAddition myUserControl; <--

Usage:

public async void AddIssue()
        {
            List<String> categoryList = new List<String>();
            await Task.Run(() =>
            {
                categoryList = dAO.loadCategoryAsync("all").GetAwaiter().GetResult();
                if (categoryList == null)
                    categoryList = new List<String>();
                myUserControl = new IssueAddition(categoryList);
                Add add = new Add(MyCustomControlAdd);
                Globals.ThisAddIn.myUserControl.Invoke(add);                
            });
        }
        delegate void Add();

        private void MyCustomControlAdd()

        {
            Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl, "test").Visible = true;
        }

Edit 2: I also tried Invoke method on but it also throws same exception. I am unable to understand how to overcome this problem.

       public async void AddIssue()
        {
            List<String> categoryList = new List<String>();
            await Task.Run(() =>
            {
                categoryList = 
               dAO.loadCategoryAsync("all").GetAwaiter().GetResult();
                if (categoryList == null)
                    categoryList = new List<String>();
                myUserControl = new IssueAddition(categoryList);
                if (!Dispatcher.CurrentDispatcher.CheckAccess())
                    Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
                    {
                        Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl, "test").Visible = true;

                    }));
                else
                    Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
                    {
                        Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl, "test").Visible = true;

                    }));
            });
        }

Edit 3: Adding the code changes to make it work as suggested by the accepted answer: Add Dispatcher _dispatcher; instance variable in ThisAddIn.cs. Intialise it in ThisAddIn_Startup function:

       private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
        }

Usage:

public async void AddIssue()
        {
            List<String> categoryList = new List<String>();
            await Task.Run(() =>
            {
                categoryList = dAO.loadCategoryAsync("all").GetAwaiter().GetResult();
                if (categoryList == null)
                    categoryList = new List<String>();
                myUserControl = new IssueAddition(categoryList);
                _dispatcher.Invoke(new Action(() =>
                {
                    Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl, "test").Visible = true;
                }));
            });
        }
Akanksha_p
  • 916
  • 12
  • 20
  • Where and when do you try to create a new task pane instance in Outlook? Does it work with a newly created empty user control? – Eugene Astafiev Mar 02 '23 at 22:41
  • I am creating this UserControl in one of the function triggered on a click. Like when user clicks a button, this control will be initialised and add to TaskPane to become visible to on screen. – Akanksha_p Mar 04 '23 at 17:43

2 Answers2

0

Most likely you are calling that code on a secondary thread.

If you absolutely must do so from a secondary thread, use Dispatcher.Invoke (where Dispatcher object is retrieved from Dispatcher.CurrentDispatcher on the main thread).

Dmitry Streblechenko
  • 62,942
  • 4
  • 53
  • 78
  • This code is part of ThisAddIn class. I tried Dispatcher.Invoke function too on userControl but that also gave the same error. Will update that code too here. Is there any other way i can add my User control to CustomeTaskPane ? – Akanksha_p Mar 02 '23 at 03:34
  • When and how is this code called? – Dmitry Streblechenko Mar 02 '23 at 15:46
  • After your suggestion, i tried adding these lines after initialising myUserControl object. ```Dispatcher.CurrentDispatchher```: ```Dispatcher.CurrentDispatcher.Invoke(new Action(() => { Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl, "test").Visible = true; }));``` But this also gives the same error. – Akanksha_p Mar 04 '23 at 18:16
  • Also, to get hold of Main thread, i read that ```Application.Current.Dispatcher.Invoke``` is needed. However, I can't use ```Current``` property of ```Application``` which is of type ```Microsoft.Office.Interop.Outlook.Application```. This gives ```'Application' does not contain a definition for 'Current' and no accessible extension method 'Current' accepting a first argument of type 'Application' could be found.``` – Akanksha_p Mar 04 '23 at 18:50
  • `Dispatcher.CurrentDispatcher.Invoke` is useless - you are essentially saying "take this code and execute it on the current thread". You don't need a dispatcher for that. Capture `Dispatcher.CurrentDispatcher` into a dedicated variable on the main thread when your addin is loading `_dsipatcher = Dispatcher.CurrentDispatcher`. Then when your are about to create a control, use `_dispatcher.Invoke` – Dmitry Streblechenko Mar 05 '23 at 00:46
  • I don't know where `Application.Current.Dispatcher` comes from. I certainly never suggested that. – Dmitry Streblechenko Mar 05 '23 at 00:47
0

First of all, make sure that your user control instance passed to the Add method is inherited from the System.Windows.Forms.UserControl class.

The following code example demonstrates how to create a custom task pane by using the Add(UserControl, String) method. The example also uses properties of the CustomTaskPane object to modify the default appearance of the custom task pane.

private MyUserControl myUserControl1;
private Microsoft.Office.Tools.CustomTaskPane myCustomTaskPane;

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    myUserControl1 = new MyUserControl();
    myCustomTaskPane = this.CustomTaskPanes.Add(myUserControl1,
        "New Task Pane");

    myCustomTaskPane.DockPosition =
        Office.MsoCTPDockPosition.msoCTPDockPositionFloating;
    myCustomTaskPane.Height = 500;
    myCustomTaskPane.Width = 500;

    myCustomTaskPane.DockPosition =
        Office.MsoCTPDockPosition.msoCTPDockPositionRight;
    myCustomTaskPane.Width = 300;

    myCustomTaskPane.Visible = true;
    myCustomTaskPane.DockPositionChanged +=
        new EventHandler(myCustomTaskPane_DockPositionChanged);
}
Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
  • I implemented the above solution. I am getting same exception, ```Additional information: Unable to cast COM object of type 'System.__ComObject' to interface type ```. Updated the code for reference. – Akanksha_p Mar 04 '23 at 17:41