13

I've added a CustomTaskPane to Excel 2013 that lets users quickly search for photos. It works well and fine if the user only opens/creates one workbook. Problem is if they open another workbook or create a new one, the task pane doesn't appear in the new window that appears. It simply stays put in the original window. I know this behavior is caused by the fact I was only initializing the panel upon opening Excel. I added an event handler to the ActiveWindow event to initialize a new panel when they open another workbook.

Problem is I cannot figure out how to tell if the CustomTaskPane is already present in a window already. If it is, it simple creates another CustomTaskPane, so there are now two within that window. I wrote the following code to dispose the original and create a new one, but it introduces some lag (1-5 seconds) that would drive users crazy every time they change workbook windows. Is there a way to see if a CustomTaskPane already exists in a window to avoid disposing and recreating a new one to avoid stacking duplicate task panes?

Microsoft.Office.Tools.CustomTaskPane PartPhotoTaskPane;
Globals.ThisAddIn.Application.WindowActivate += Application_WindowActivate;

        void Application_WindowActivate(Excel.Workbook Wb, Excel.Window Wn)
    {
        if (PartPhotoTaskPane != null)
        {
            PartPhotoTaskPane.Dispose();
            InitalizePartPhotoViewerTaskPane(EPPF);
        }
        else
        {
            InitalizePartPhotoViewerTaskPane(EPPF);
        }
    }

    /// <summary>
    /// Start up the part photo viewer task pane
    /// </summary>
    private void InitalizePartPhotoViewerTaskPane(ExcelPartPhotoFunctions _EPPF)
    {
        //intialize the part search
        try
        {
            PartPhotoTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(new PartPhotoSearchPane(_EPPF), "Part Information", Globals.ThisAddIn.Application.ActiveWindow);
            PartPhotoTaskPane.Visible = Properties.Settings.Default.InfoPaneOpenStatus;
            PartPhotoTaskPane.Width = 260;
        }
        catch (Exception e)
        {
            MessageBox.Show("Error starting Part Info Toolbar:" + Environment.NewLine +
            e.Message + Environment.NewLine + e.StackTrace, "Error!", MessageBoxButtons.OK,
            MessageBoxIcon.Error);
        }
    }
Lee Harrison
  • 2,306
  • 21
  • 32
  • Voted up. Good question - I did something similar in my code for Excel 2013. I don't think Microsoft has thought much about this awful custom new taskpane behavior, when the changed Excel to a SDI user interface with Excel 2013... :-( – jreichert May 02 '14 at 09:06
  • Having the same issue. Did you ever figure this one out? – anakic Jul 11 '14 at 12:39

1 Answers1

19

Use the hwnd (Globals.ThisAddIn.Application.Hwnd) to identify the Excel window. This will work well both for Office2013 (which uses an SDI approach) and older versions of Office that use MDI windows. Here is a class you can use for this:

public class TaskPaneManager
{
    static Dictionary<string, CustomTaskPane> _createdPanes = new Dictionary<string, 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 CustomTaskPane GetTaskPane(string taskPaneId, string taskPaneTitle, Func<UserControl> taskPaneCreatorFunc)
    {
        string key = string.Format("{0}({1})", taskPaneId, Globals.ThisAddIn.Application.Hwnd);
        if (!_createdPanes.ContainsKey(key))
        {
            var pane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneCreatorFunc(), taskPaneTitle);
            _createdPanes[key] = pane;
        }
        return _createdPanes[key];
    }
}

Here I'm actually combining the Excel window hwnd and some arbitrary string identifier to identify the taskpane. The idea is to support multiple taskpanes in the same addin.

Here is an example for how to use it from the ribbon:

    private void button1_Click(object sender, RibbonControlEventArgs e)
    {
        var taskpane = TaskPaneManager.GetTaskPane("A", "Task pane type A", () => new UserControl1());
        taskpane.Visible = !taskpane.Visible;
    }

    private void button2_Click(object sender, RibbonControlEventArgs e)
    {
        var taskpane = TaskPaneManager.GetTaskPane("B", "Task pane type B", () => new UserControl2());
        taskpane.Visible = !taskpane.Visible;
    }

If you open multiple workbooks in Excel, both Excel window will have their own taspaneA and taskpaneB.

anakic
  • 2,746
  • 1
  • 30
  • 32
  • Do you use this code for multiple togglebuttons/taskpanes? I am desperate in trying to get this to work and your code is the only thing I have found. – punkouter Dec 24 '15 at 15:52
  • @punkouter My VSTO plugin has only one type of taskpane. If you have multiple types of taskpanes, just use the same approach but with multiple dictionaries. – anakic Dec 25 '15 at 20:33
  • Each dictionary is a separate taskpane ? And the collection in the dictionary is the collection of a particular taskpane throughout the multiple instances of Excel ? It is confusing to me. Though seems like a common thing (Creating multiple plugins in Excel) but I can't find any code.. I am not the good of a programmer to figure it out myself I'm worried – punkouter Dec 27 '15 at 18:15
  • 1
    The idea behind the dictionary: have the window handle (hwnd) of the excel window as the key, and the taskpane instance as the value. That way you can make sure that you create exactly one taskpane instance per excel window which will work fine for both excel 2010 (mdi style interface) and newer versions (sdi style interface). If you have multiple types of taskpanes you could have multiple dictionaries, each dictionary for one type of taskpane. Specifics would depend on your requirements, e.g. when you need to create taskpanes and if/when you need to show/hide them. – anakic Dec 29 '15 at 15:17
  • ok. Ill try some more. This is painful. So the dictionary represents a single task pane in a collection of excel apps right? – punkouter Dec 29 '15 at 19:38
  • @punkouter I've updated my answer for your situation. – anakic Dec 29 '15 at 21:55