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

时间:2014-03-21 13:40:53

标签: ms-word vsto add-in office-interop customtaskpane

我正在使用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>如果文档的任务窗格已存在。我这样做是因为如果我尝试打开已打开的文档,则会触发open事件。如果它不存在,我创建一个新的TaskPaneWrapper类。在其构造函数中,我创建了一个新的任务窗格,并使用

将其添加到CustomTaskPanes集合中
Globals.ThisAddIn.CustomTaskPanes.Add(taskPane, "Title");

According to 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的那些包装器。当然,捕捉异常有点贵,而且这个解决方案非常糟糕,所以我想知道,有更好的解决方案吗?检查In this question CustomTaskPane.Window属性为null,但它永远不会为我返回null。

此外,我可以使用我当前的逻辑遇到任何其他问题吗?管理多个文档的多个任务窗格的典型方法是什么?

3 个答案:

答案 0 :(得分:6)

此MSDN文章标题为Managing Task Panes in Multiple Word and InfoPath Documents

中详细说明了此问题

您必须创建一个删除孤立CTP的方法(即那些不再附加窗口的CTP)。

我已经按照这篇文章成功实现了一个删除孤儿的CustomTaskPane管理器。

答案 1 :(得分:4)

os.startfile

中的答案

在打开现有文档之后,您可以通过在调用CreateTaskPaneWrapper之前从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);
}

答案 2 :(得分:0)

要尝试的一些事情::

  1. 也许你可以在DocumentNew-和DocumentOpen-Event中检查“Word.Application.Documents.Count”来跟踪实际打开的文档。

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

  3. 您可以按照以下方式捕获Disposed-Event,而不是您的IsWindowAlive()方法

  4. _vstoDocument.Disposed += _vstoDocument_Disposed;
    
            void _vstoDocument_Disposed(object sender, EventArgs e)
            {
                //Remove from dictionary of open documents 
            }