System.InvalidCastException while adding User control to CustomTaskPane

158 Views Asked by At

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

There are 2 best solutions below

6
Dmitry Streblechenko On BEST ANSWER

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).

1
Eugene Astafiev On

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