Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating and managing custom task panes for multiple documents in a VSTO Word addin

I'm developing a Word 2007-2010 addin using VSTO in Visual Studio 2008. In my addin, I need a custom task pane for each open word document. Basically, I need to create a task pane for each document, show the correct task pane in the document window, do something on document close and then remove the task pane and all references to it.

This is what I have done so far:

Task pane creation

I create a custom task pane for each new, opened or existing on load document like this:

((ApplicationEvents4_Event) Application).NewDocument += CreateTaskPaneWrapper;
Application.DocumentOpen += CreateTaskPaneWrapper;
foreach (Document document in Application.Documents)
{
    CreateTaskPaneWrapper(document);
}

In the CreateTaskPaneWrapper method, I check a Dictionary<Document, TaskPaneWrapper> if a task pane for a document already exists. I do this because the open event fires if I try to open an already open document. If it doesn't exist, I create a new TaskPaneWrapper class. In its constructor, I create a new task pane and add it to the CustomTaskPanes collection with

Globals.ThisAddIn.CustomTaskPanes.Add(taskPane, "Title");

According to MSDN, this associates the task pane with the currently active window.

Task pane shutdown

Both Document.Close and Application.DocumentBeforeClose events don't suit me, because they fire before before the user gives the confirmation to close the document. So I use the Microsoft.Office.Tools.Word.Document.Shutdown event in my TaskPaneWrapper class like this:

_vstoDocument = document.GetVstoObject();
_vstoDocument.Shutdown += OnShutdown;

private void OnShutdown(object sender, EventArgs eventArgs)
{
    Globals.ThisAddIn.CustomTaskPanes.Remove(_taskPane);
    //additional shutdown logic
}

All of this seems to work pretty well, the task panes are created, bound to the corresponding windows, and are successfully removed. However, I still have one problem - when I start Word, a blank document opens. If I then open an existing document without changing the blank one, the blank document and it's window is deleted without the Document.Close, Application.DocumentBeforeClose and Microsoft.Office.Tools.Word.Document.Shutdown events firing. Because OnShutdown is not called and the task pane for the blank document is not deleted, the next document window contains TWO task panes - the very new one, and the very first (orphaned) one. How can I remove this orphaned task pane? Accessing the deleted document or window reference throws a COMException ("Object was deleted"). I'm temporary using this hack:

//A property in my TaskPaneWrapper class
public bool IsWindowAlive()
{
    try
    {
        var window = _vstoDocument.ActiveWindow;
        return true;
    }
    catch (COMException)
    {
        return false;
    }
}

In the CreateTaskPaneWrapper method, I check this property for all existing wrappers and shutdown the ones where the property is false. Catching an exception is somewhat expensive, of course, and this solution is pretty hacky, so I was wondering, is there a better one? In this question CustomTaskPane.Window property is checked for null, but it never returns null for me.

Also, are there any other problems I can run into using my current logic? What's the typical way of managing multiple task panes for multiple documents?

like image 862
sdds Avatar asked Mar 21 '14 13:03

sdds


2 Answers

This problem is detailed in this MSDN article titled Managing Task Panes in Multiple Word and InfoPath Documents

You have to create a method that removes orphan CTPs (ie those that no longer have windows attached).

I have followed this article and successfully implemented a CustomTaskPane manager that removes the orphans.

like image 166
Matt Cope Avatar answered Sep 20 '22 04:09

Matt Cope


This answer in MSDN

Instead of reactively cleaning up orphaned task panes after you open an existing document, you can proactively clean up by calling the following method from the DocumentOpen event handler before you call CreateTaskPaneWrapper. This code loops through each of the custom task panes that belong to the add-in. If the task pane has no associated window, the code removes the task pane from the collection.

private void RemoveOrphanedTaskPanes()
{
    for (int i = Globals.ThisAddIn.CustomTaskPanes.Count; i > 0; i--)
    {
        CustomTaskPanes ctp = Globals.ThisAddIn.CustomTaskPanes[i - 1];
        if (ctp.Window == null)
        {
            this.CustomTaskPanes.Remove(ctp);
        }
    }
}

private void Application_DocumentOpen(Word.Document Doc)
{
    RemoveOrphanedTaskPanes();
    CreateTaskPaneWrapper(document);
}
like image 29
hoss77 Avatar answered Sep 21 '22 04:09

hoss77