在 Office 文档中保存动态控件

保存和关闭文档或工作簿时,将不会保持在运行时添加的控件。 宿主控件和 Windows 窗体控件的确切行为与此不同。 对于这两种控件,通过在解决方案中添加代码,可在用户重新打开文档时重新创建它们。

在运行时向文档添加的控件称为“动态控件”。 有关动态控件的更多信息,请参见在运行时向 Office 文档添加控件

**适用于:**本主题中的信息适用于以下应用程序的文档级项目和应用程序级项目:Excel 2007 和 Excel 2010;Word 2007 和 Word 2010。有关更多信息,请参见按 Office 应用程序和项目类型提供的功能

在文档中保持宿主控件

保存并关闭文档时,将从文档中移除所有动态宿主控件。 只有基础本机 Office 对象才会留在文档中。 例如,Microsoft.Office.Tools.Excel.ListObject 宿主控件变成了 Microsoft.Office.Interop.Excel.ListObject。 本机 Office 对象未连接至宿主控件事件,且不具备宿主控件的数据绑定功能。

下表列出了每种宿主控件留在文档中的本机 Office 对象。

宿主控件类型

本机 Office 对象类型

Microsoft.Office.Tools.Excel.Chart

Microsoft.Office.Interop.Excel.Chart

Microsoft.Office.Tools.Excel.ListObject

Microsoft.Office.Interop.Excel.ListObject

Microsoft.Office.Tools.Excel.NamedRange

Microsoft.Office.Interop.Excel.Range

Microsoft.Office.Tools.Word.Bookmark

Microsoft.Office.Interop.Word.Bookmark

Microsoft.Office.Tools.Word.BuildingBlockGalleryContentControl

Microsoft.Office.Tools.Word.ComboBoxContentControl

Microsoft.Office.Tools.Word.ContentControl

Microsoft.Office.Tools.Word.DatePickerContentControl

Microsoft.Office.Tools.Word.DropDownListContentControl

Microsoft.Office.Tools.Word.GroupContentControl

Microsoft.Office.Tools.Word.PictureContentControl

Microsoft.Office.Tools.Word.PlainTextContentControl

Microsoft.Office.Tools.Word.RichTextContentControl

Microsoft.Office.Interop.Word.ContentControl

在打开文档时重新创建动态宿主控件

每次用户打开文档时,都可以重新创建动态宿主控件以代替现有的本机控件。 按此方式在打开文档时创建宿主控件可模拟用户可能期望的体验。

若要重新创建 Word 的宿主控件或者创建 Excel 的 Microsoft.Office.Tools.Excel.NamedRangeMicrosoft.Office.Tools.Excel.ListObject 宿主控件,请使用 Microsoft.Office.Tools.Excel.ControlCollectionMicrosoft.Office.Tools.Word.ControlCollection 对象的 Add<控件类> 方法。 使用具有本机 Office 对象参数的方法。

例如,如果要在打开文档时基于现有的本机 Microsoft.Office.Interop.Excel.ListObject 创建 Microsoft.Office.Tools.Excel.ListObject 宿主控件,请使用 AddListObject(ListObject) 方法并传入现有的 Microsoft.Office.Interop.Excel.ListObject。 下面的代码示例在 Excel 的文档级项目中演示此操作。 代码将在 Sheet1 类中重新创建一个基于名为 MyListObject 的现有 Microsoft.Office.Interop.Excel.ListObject 的动态 Microsoft.Office.Tools.Excel.ListObject

Private vstoListObject As Microsoft.Office.Tools.Excel.ListObject
Private Const DISP_E_BADINDEX As Integer = CInt(&H8002000B)

Private Sub Sheet1_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup
    Dim nativeListObject As Excel.ListObject = Nothing

    Try
        nativeListObject = Me.ListObjects("MyListObject")
    Catch ex As System.Runtime.InteropServices.COMException
        ' "MyListObject" does not exist.
        If ex.ErrorCode <> DISP_E_BADINDEX Then
            Throw
        End If
    End Try

    If nativeListObject IsNot Nothing Then
        vstoListObject = Me.Controls.AddListObject(nativeListObject)
    End If
End Sub
private Microsoft.Office.Tools.Excel.ListObject vstoListObject;
private const int DISP_E_BADINDEX = unchecked((int)0x8002000B);

private void Sheet1_Startup(object sender, System.EventArgs e)
{
    Excel.ListObject nativeListObject = null;

    try
    {
        nativeListObject = this.ListObjects.get_Item("MyListObject");
    }
    catch (System.Runtime.InteropServices.COMException ex)
    {
        // "MyListObject" does not exist.
        if (ex.ErrorCode != DISP_E_BADINDEX)
            throw;
    }

    if (nativeListObject != null)
    {
        vstoListObject = this.Controls.AddListObject(nativeListObject);
    }
}

重新创建图表

若要重新创建 Microsoft.Office.Tools.Excel.Chart 宿主控件,必须首先删除本机 Microsoft.Office.Interop.Excel.Chart,然后通过使用 AddChart(Range, String)AddChart(Double, Double, Double, Double, String) 方法重新创建 Microsoft.Office.Tools.Excel.Chart。 没有能够让您基于现有的 Microsoft.Office.Interop.Excel.Chart 创建新的 Microsoft.Office.Tools.Excel.Chart 的 Add<控件类> 方法。

如果您没有首先删除本机 Microsoft.Office.Interop.Excel.Chart,则在重新创建 Microsoft.Office.Tools.Excel.Chart 时会创建另一个重复的图表。

在文档中保持 Windows 窗体控件

保存并关闭文档时,Visual Studio Tools for Office Runtime将自动从文档中移除所有动态创建的 Windows 窗体控件。 但是,此行为在文档级项目和应用程序级项目中却有所不同。

在文档级自定义项中,这些控件及其基础 ActiveX 包装(用于在文档中承载控件)将在下次打开文档时移除。 文档中不会留下任何曾经存在这些控件的指示。

在应用程序级外接程序中,将移除这些控件,但 ActiveX 包装却留在文档中。 当用户下次打开文档时,就会看到 ActiveX 包装。 在 Excel 中,ActiveX 包装会按照这些控件在上次保存文档时的显示方式来显示它们的图像。 在 Word 中,ActiveX 包装只有在用户单击它们时才会显示出来,其图像是一条表示控件边框的虚线。 有多种方式可用于移除 ActiveX 包装。 有关更多信息,请参见移除外接程序中的 ActiveX 包装。

在打开文档时重新创建 Windows 窗体控件

在用户重新打开文档时,可以重新创建已删除的 Windows 窗体控件。 为此,解决方案必须执行下列任务:

  1. 存储有关这些控件在文档保存或关闭时的大小、位置和状态的信息。 在文档级自定义项中,可以将此数据保存至文档的数据缓存中。 在应用程序级外接程序中,可以将此数据保存至文档的自定义 XML 部件中。

  2. 在打开文档时引发的事件中重新创建这些控件。 在文档级项目中,可在 Sheet编号_Startup 或 ThisDocument_Startup 事件处理程序中实现此目的。 在应用程序级项目中,可在 WorkbookOpenDocumentOpen 事件的事件处理程序中实现此目的。

移除外接程序中的 ActiveX 包装

在使用外接程序向文档中添加动态 Windows 窗体控件时,可以防止控件的 ActiveX 包装在下次打开文档时显示在文档中,具体方法如下。

在打开文档时移除 ActiveX 包装

若要移除所有 ActiveX 包装,请调用 GetVstoObject 方法,以便为表示新打开的文档的 Microsoft.Office.Interop.Word.DocumentMicrosoft.Office.Interop.Excel.Workbook 生成一个宿主项。 例如,若要从某个 Word 文档中移除所有 ActiveX 包装,可以调用 GetVstoObject 方法,以便为传递给 DocumentOpen 事件的事件处理程序的 Document 对象生成一个宿主项。

如果已知将只在安装了相应外接程序的计算机上打开该文档,此过程将十分有用。 如果可能要将该文档传递给未安装相应外接程序的其他用户,请改为考虑在关闭文档前移除这些控件。

下面的代码示例演示如何在打开文档时调用 GetVstoObject 方法。

Private Sub Application_DocumentOpen_ClearActiveXWrappers( _
    ByVal Doc As Word.Document) Handles Application.DocumentOpen

    ' Use the following line of code in projects that target the .NET Framework 4.
    Dim vstoDocument As Document = Globals.Factory.GetVstoObject(Doc)

    ' In projects that target the .NET Framework 3.5, use the following line of code.
    ' Dim vstoDocument As Microsoft.Office.Tools.Word.Document = Doc.GetVstoObject()
End Sub
private void Application_DocumentOpen_ClearActiveXWrappers(Word.Document Doc)
{
    // Use the following line of code in projects that target the .NET Framework 4.
    Microsoft.Office.Tools.Word.Document vstoDocument = Globals.Factory.GetVstoObject(Doc);

    // In projects that target the .NET Framework 3.5, use the following line of code.
    // Microsoft.Office.Tools.Word.Document vstoDocument = Doc.GetVstoObject();
}

虽然 GetVstoObject 方法主要用于在运行时生成新的宿主项,但在首次针对特定文档调用此方法时,它还会清除该文档中的所有 ActiveX 包装。 有关如何使用 GetVstoObject 方法的更多信息,请参见在运行时在应用程序级外接程序中扩展 Word 文档和 Excel 工作簿

请注意,如果外接程序可在文档打开时创建动态控件,那么它在创建这些控件的过程中就已经在调用 GetVstoObject 方法。 在此情况下,无需添加对 GetVstoObject 方法的单独调用即可移除 ActiveX 包装。

在关闭文档前移除动态控件

外接程序可在文档关闭前显式移除文档中的每个动态控件。 如果可能要将文档传递给未安装该外接程序的用户,此过程将十分有用。

下面的代码示例演示如何在关闭 Word 文档时移除其中的所有 Windows 窗体控件。

Private Sub Application_DocumentBeforeClose(ByVal Doc As Word.Document, _
    ByRef Cancel As Boolean) Handles Application.DocumentBeforeClose

    ' Use the following line of code in projects that target the .NET Framework 4.
    Dim isExtended As Boolean = Globals.Factory.HasVstoObject(Doc)

    ' In projects that target the .NET Framework 3.5, use the following line of code.
    ' Dim isExtended As Boolean = Doc.HasVstoObject()

    If isExtended Then
        ' Use the following line of code in projects that target the .NET Framework 4.
        Dim vstoDocument As Document = Globals.Factory.GetVstoObject(Doc)

        ' In projects that target the .NET Framework 3.5, use the following line of code.
        ' Dim vstoDocument As Document = Doc.GetVstoObject()

        Dim controlsToRemove As System.Collections.ArrayList = _
            New System.Collections.ArrayList()

        ' Get all of the Windows Forms controls.
        For Each control As Object In vstoDocument.Controls
            If TypeOf control Is System.Windows.Forms.Control Then
                controlsToRemove.Add(control)
            End If
        Next

        ' Remove all of the Windows Forms controls from the document.
        For Each control As Object In controlsToRemove
            vstoDocument.Controls.Remove(control)
        Next
    End If
End Sub
void Application_DocumentBeforeClose(Word.Document Doc, ref bool Cancel)
{
    // Use the following line of code in projects that target the .NET Framework 4.
    bool isExtended = Globals.Factory.HasVstoObject(Doc);

    // In projects that target the .NET Framework 3.5, use the following line of code.
    // bool isExtended = Doc.HasVstoObject();

    if (isExtended)
    {
        // Use the following line of code in projects that target the .NET Framework 4.
        Microsoft.Office.Tools.Word.Document vstoDocument = Globals.Factory.GetVstoObject(Doc);

        // In projects that target the .NET Framework 3.5, use the following line of code.
        // Microsoft.Office.Tools.Word.Document vstoDocument = Doc.GetVstoObject();

        System.Collections.ArrayList controlsToRemove = 
            new System.Collections.ArrayList();

        // Get all of the Windows Forms controls.
        foreach (object control in vstoDocument.Controls)
        {
            if (control is System.Windows.Forms.Control)
            {
                controlsToRemove.Add(control);
            }
        }

        // Remove all of the Windows Forms controls from the document.
        foreach (object control in controlsToRemove)
        {
            vstoDocument.Controls.Remove(control);
        }
    }
}

请参见

概念

在运行时向 Office 文档添加控件

宿主控件的帮助器方法

Windows 窗体控件的帮助器方法