在VSTO Word插件中创建和管理多个文档的自定义任务窗格

13
我正在使用Visual Studio 2008中的VSTO开发一个Word 2007-2010插件。在我的插件中,我需要为每个打开的Word文档创建一个自定义任务窗格。基本上,我需要为每个文档创建一个任务窗格,在文档窗口中显示正确的任务窗格,在文档关闭时执行某些操作,然后删除该任务窗格以及所有对它的引用。
到目前为止,我已经完成了以下工作:
任务窗格创建
我为每个新打开或现有的加载文档创建一个自定义任务窗格,方法如下:
((ApplicationEvents4_Event) Application).NewDocument += CreateTaskPaneWrapper;
Application.DocumentOpen += CreateTaskPaneWrapper;
foreach (Document document in Application.Documents)
{
    CreateTaskPaneWrapper(document);
}

在CreateTaskPaneWrapper方法中,我会检查一个Dictionary<Document, TaskPaneWrapper>,如果文档的任务窗格已经存在。我这样做是因为如果我尝试打开已经打开的文档,打开事件会触发。如果不存在,我会创建一个新的TaskPaneWrapper类。在它的构造函数中,我会创建一个新的任务窗格,并将其添加到CustomTaskPanes集合中。
Globals.ThisAddIn.CustomTaskPanes.Add(taskPane, "Title");

根据MSDN的说法, 这将任务窗格与当前活动窗口相关联。

任务窗格关闭

既Document.Close事件又Application.DocumentBeforeClose事件都不适合我,因为它们在用户确认关闭文档之前就触发了。所以我在我的TaskPaneWrapper类中使用Microsoft.Office.Tools.Word.Document.Shutdown事件,像这样:

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

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

所有这些似乎都很好运作,任务窗格已创建,绑定到相应的窗口,并成功删除。但是我仍然有一个问题 - 当我启动 Word 时,会打开一个空白文档。如果我在不更改空白文档的情况下打开现有文档,则空白文档及其窗口将被删除,而不触发 Document.Close、Application.DocumentBeforeClose 和 Microsoft.Office.Tools.Word.Document.Shutdown 事件。由于 OnShutdown 没有被调用,因此空白文档的任务窗格没有被删除,下一个文档窗口包含两个任务窗格 - 其中一个是最新的,另一个是第一个(被遗弃的)任务窗格。如何删除这个被遗弃的任务窗格?访问已删除的文档或窗口引用会引发 COMException(“对象已被删除”)。我正在临时使用以下方法:
//A property in my TaskPaneWrapper class
public bool IsWindowAlive()
{
    try
    {
        var window = _vstoDocument.ActiveWindow;
        return true;
    }
    catch (COMException)
    {
        return false;
    }
}

在CreateTaskPaneWrapper方法中,我会检查所有现有包装器的此属性,并关闭其中属性为false的包装器。当然,捕获异常有点昂贵,而且这个解决方案相当hacky,所以我想知道,是否有更好的解决方案?在这个问题中 CustomTaskPane.Window属性被检查是否为空,但它从未对我返回null。
另外,我当前的逻辑还可能遇到其他问题吗?管理多个文档的多个任务窗格的典型方式是什么?

+1 感谢指引 Document.Shutdown 事件 :-) - jreichert
3个回答

6
这个问题在MSDN文章《管理多个Word和InfoPath文档中的任务窗格》中详细描述。
您需要创建一个方法来删除孤立的CTPs(即不再有窗口附加的CTP)。
我已经按照这篇文章的指导成功实现了一个CustomTaskPane管理器,可以删除这些孤立的CTPs。

谢谢提供链接,我不记得读过这篇文章了。现在我已经转到另一个项目上了,所以对细节有点模糊,但是文章建议检查自定义任务窗格的窗口是否为空,但在我的问题中已经提到,那并没有起作用。我刚刚在 Word 2010 中进行了检查,即使任务窗格被孤立,它也永远不会返回 null。 - sdds
Matt - 不知怎么的,我在某个地方找到了你的“CustomTaskPane管理器”的链接,但是我再也找不到了。它还可以使用吗?谢谢! - PMBottas

6

以下翻译内容来自MSDN

在打开现有文档之前,您可以通过从DocumentOpen事件处理程序调用以下方法来主动清理孤立的任务窗格,而不是在打开现有文档后反应性地清理。此代码循环遍历属于该加载项的每个自定义任务窗格。如果任务窗格没有关联的窗口,则代码将从集合中删除该任务窗格。

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

0

尝试以下几点:

  1. 在 DocumentNew 和 DocumentOpen 事件中,您可以检查“Word.Application.Documents.Count”来跟踪实际打开的文档。

  2. 要查找文档是否已打开(可能与重新打开活动文档有关),可以使用打开文档的字典。密钥可以是唯一的文档名称。

  3. 您可以使用以下方法捕获 Disposed 事件,而不是使用您的 IsWindowAlive() 方法

_vstoDocument.Disposed += _vstoDocument_Disposed;

        void _vstoDocument_Disposed(object sender, EventArgs e)
        {
            //Remove from dictionary of open documents 
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接