Stu-co
Stu-co

Reputation: 39

Trouble attaching an event to a custom task pane. VSTO, Excel 2016

Background

I'm currently working on an application in VSTO2015 and Excel 2016. The application manages a number of CustomTaskPanes in different windows. I am trying to get // some code to fire when the task pane is opened or closed. In order to handle the various windows, I've implemented a structure very similar to this;

CustomTaskPane in Excel doesn't appear in new Workbooks

ThisAddIn.cs contains the following class;

public class TaskPaneManager
    {
        static Dictionary<string, Microsoft.Office.Tools.CustomTaskPane> _createdPanes = new Dictionary<string, Microsoft.Office.Tools.CustomTaskPane>();

        /// <summary>
        /// Gets the taskpane by name (if exists for current excel window then returns existing instance, otherwise uses taskPaneCreatorFunc to create one). 
        /// </summary>
        /// <param name="taskPaneId">Some string to identify the taskpane</param>
        /// <param name="taskPaneTitle">Display title of the taskpane</param>
        /// <param name="taskPaneCreatorFunc">The function that will construct the taskpane if one does not already exist in the current Excel window.</param>
        public static Microsoft.Office.Tools.CustomTaskPane GetTaskPane(string taskPaneId, string taskPaneTitle, Func<UserControl> taskPaneCreatorFunc)
        {
            string key = string.Format("{0}({1})", taskPaneId, Globals.ThisAddIn.Application.Hwnd);
            string title = taskPaneId;
            string windowId = Globals.ThisAddIn.Application.Hwnd.ToString();

            if (!_createdPanes.ContainsKey(key))
            {
                var customTaskPane = taskPaneCreatorFunc();
                var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
                _createdPanes[key] = pane;

                //
                // Set the task pane width as set in the App.Config
                //
                pane.Width = Convert.ToInt32(ConfigurationManager.AppSettings["TaskPaneWidth"]);
            }
            return _createdPanes[key];
        }
    ....

My calls from Ribbon.cs;

private void btnUploadWizard_Click(object sender, RibbonControlEventArgs e)
{
    // Check for configuration sheet first.

    string title = "Upload Wizard";

    TaskPaneManager.isConfigurationCreated();

    var UploadWizardTaskpane = TaskPaneManager.GetTaskPane(title, title, () => new TaskPaneUploadWizard());
    UploadWizardTaskpane.Visible = !UploadWizardTaskpane.Visible;

}

The Problem: Event Handlers

I'm having difficulty getting an event handler to fire. I can't tell what I'm doing wrong. In the TaskPaneDesigner I am attaching the event using this.VisibleChanged += new System.EventHandler(this.TaskPaneUploadWizard_VisibleChanged);, and then defining it in my TaskPaneUploadWizard class as follows;

public partial class TaskPaneUploadWizard : UserControl
{
    ...

    public TaskPaneUploadWizard()
    {
        InitializeComponent();
    }

    private void TaskPaneUploadWizard_VisibleChanged(object sender, EventArgs e)
    {
        // Some code
    }

My thoughts

It seems to me as though I am either attaching the eventHandler to something other than the CustomTaskPane object, or I am attaching it before the CustomTaskPane is created.

Help!

Upvotes: 1

Views: 957

Answers (2)

Fabrice T
Fabrice T

Reputation: 663

var UploadWizardTaskpane = TaskPaneManager.GetTaskPane(title, title, () => new TaskPaneUploadWizard());

This is creating a CustomTaskPane not a TaskPaneUploadWizard object

It seems like this.VisibleChanged += new System.EventHandler(this.TaskPaneUploadWizard_VisibleChanged); is acting on TaskPaneUploadWizard which is only the guest of CustomTaskPane

Note that The visibility of CustomTaskPane doesn't affect TaskPaneUploadWizard

My suggestion

You remove VisibleChanged from the designer, you will add it manually in your TaskPaneUploadWizard

public partial class TaskPaneUploadWizard : UserControl
{
    //{---}
    CustomTaskPane _HostPane;
    public CustomTaskPane HostPane
    {
         get => _HostPane;
         set
         {
             if(_HostPane == value)
                 return;
             _HostPane?.VisibleChanged -= TaskPaneUploadWizard_VisibleChanged;
             _HostPane = value;
             _HostPane?.VisibleChanged += TaskPaneUploadWizard_VisibleChanged;
         }
    }
    //{---}
    private void TaskPaneUploadWizard_VisibleChanged(object sender, EventArgs e)
    {
        // Some code
    }
    //{---}

Then in GetTaskPane you say

//{---}
var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
(customTaskPane as TaskPaneUploadWizard).HostPane = pane;
//{---}

Upvotes: 0

haindl
haindl

Reputation: 3221

To detect if the task pane was opened or closed, you have to attach to the VisibleChanged event of pane.

So the simplest solution would be to add just one line of code to the GetTaskPane method:

var pane = Globals.ThisAddIn.CustomTaskPanes.Add(customTaskPane, taskPaneTitle);
// This is the new line to be added.
pane.VisibleChanged += (s, e) => customTaskPane.Visible = pane.Visible;
_createdPanes[key] = pane;

Now the visibility of the whole task pane will be passed on to its content and // some code should be executed.

Alternatively, if you don't want to manually set customTaskPane.Visible for whatever reason, you could as well execute your code directly in this new event handler:

pane.VisibleChanged += (s, e) => { /* some code */ };

But personally I would rather recommend the first approach because it seems to fit a bit better into your existing code.

Upvotes: 2

Related Questions