Persisting Dynamic Controls in Office Documents

Controls that are added at run time are not persisted when the document or workbook is saved and closed. The exact behavior is different for host controls and Windows Forms controls. In both cases, you can add code to your solution to re-create the controls when the user reopens the document.

Controls that you add to documents at run time are called dynamic controls. For more information about dynamic controls, see Adding Controls to Office Documents at Run Time.

Applies to: The information in this topic applies to document-level projects and application-level projects for the following applications: Excel 2007 and Excel 2010; Word 2007 and Word 2010. For more information, see Features Available by Office Application and Project Type.

Persisting Host Controls in the Document

When a document is saved and then closed, all dynamic host controls are removed from the document. Only the underlying native Office objects remain behind. For example, a Microsoft.Office.Tools.Excel.ListObject host control becomes a Microsoft.Office.Interop.Excel.ListObject. The native Office objects are not connected to the host control events, and they do not have the data binding functionality of the host control.

The following table lists the native Office object that is left behind in a document for each type of host control.

Host control type

Native Office object type

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

Re-Creating Dynamic Host Controls When Documents Are Opened

You can re-create dynamic host controls in place of existing native controls every time a user opens the document. Creating host controls in this manner when a document is opened simulates the experience that users might expect.

To re-create a host control for Word, or a Microsoft.Office.Tools.Excel.NamedRange or Microsoft.Office.Tools.Excel.ListObject host control for Excel, use an Add<control class> method of an Microsoft.Office.Tools.Excel.ControlCollection or Microsoft.Office.Tools.Word.ControlCollection object. Use a method that has a parameter for the native Office object.

For example, if you want to create a Microsoft.Office.Tools.Excel.ListObject host control from an existing native Microsoft.Office.Interop.Excel.ListObject when the document is opened, use the AddListObject(ListObject) method and pass in the existing Microsoft.Office.Interop.Excel.ListObject. The following code example demonstrates this in a document-level project for Excel. The code re-creates a dynamic Microsoft.Office.Tools.Excel.ListObject that is based on an existing Microsoft.Office.Interop.Excel.ListObject named MyListObject in the Sheet1 class.

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

Re-Creating Charts

To re-create a Microsoft.Office.Tools.Excel.Chart host control, you must first delete the native Microsoft.Office.Interop.Excel.Chart, and then re-create the Microsoft.Office.Tools.Excel.Chart by using the AddChart(Range, String) or AddChart(Double, Double, Double, Double, String) method. There is no Add<control class> method that enables you to create a new Microsoft.Office.Tools.Excel.Chart based on an existing Microsoft.Office.Interop.Excel.Chart.

If you do not first delete the native Microsoft.Office.Interop.Excel.Chart, then you will create a second, duplicate chart when you re-create the Microsoft.Office.Tools.Excel.Chart.

Persisting Windows Forms Controls in Documents

When a document is saved and then closed, the Visual Studio Tools for Office runtime automatically removes all dynamically created Windows Forms controls from the document. However, the behavior is different for document-level and application-level projects.

In document-level customizations, the controls and their underlying ActiveX wrappers (which are used to host the controls on the document) are removed the next time the document is opened. There is no indication that the controls were ever there.

In application-level add-ins, the controls are removed, but the ActiveX wrappers remain in the document. The next time the user opens the document, the ActiveX wrappers are visible. In Excel, the ActiveX wrappers display images of the controls as they appeared the last time the document was saved. In Word, the ActiveX wrappers are invisible unless the user clicks on them, in which case they display a dotted line that represents the border of the controls. There are several ways you can remove the ActiveX wrappers. For more information, see Removing ActiveX Wrappers in an Add-in.

Re-Creating Windows Forms Controls When Documents Are Opened

You can re-create deleted Windows Forms controls when the user reopens the document. To do this, your solution must perform the following tasks:

  1. Store information about the size, location, and state of the controls when the document is saved or closed. In a document-level customization, you can save this data to the data cache in the document. In an application-level add-in, you can save this data to a custom XML part in the document.

  2. Re-create the controls in an event that is raised when the document is opened. In document-level projects, you can do this in the Sheetn_Startup or ThisDocument_Startup event handlers. In application-level projects, you can do this in the event handlers for the WorkbookOpen or DocumentOpen events.

Removing ActiveX Wrappers in an Add-in

When you add dynamic Windows Forms controls to documents by using an add-in, you can prevent the ActiveX wrappers for the controls from appearing in the document the next time it is opened in the following ways.

Removing ActiveX Wrappers When the Document Is Opened

To remove all ActiveX wrappers, call the GetVstoObject method to generate a host item for the Microsoft.Office.Interop.Word.Document or Microsoft.Office.Interop.Excel.Workbook that represents the newly opened document. For example, to remove all ActiveX wrappers from a Word document, you can call the GetVstoObject method to generate a host item for the Document object that is passed to the event handler for the DocumentOpen event.

This procedure is useful when you know that the document will be opened only on computers that have the add-in installed. If the document might be passed to other users who do not have the add-in installed, consider removing the controls before closing the document instead.

The following code example demonstrates how to call the GetVstoObject method when the document is opened.

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

Although the GetVstoObject method is used primarily to generate a new host item at run time, this method also clears all ActiveX wrappers from the document the first time it is called for a specific document. For more information about how to use the GetVstoObject method, see Extending Word Documents and Excel Workbooks in Application-Level Add-ins at Run Time.

Note that if your add-in creates dynamic controls when the document is opened, your add-in will already call the GetVstoObject method as part of the process to create the controls. You do not need to add a separate call to the GetVstoObject method to remove the ActiveX wrappers in this scenario.

Removing the Dynamic Controls Before the Document Is Closed

Your add-in can explicitly remove each dynamic control from the document before the document is closed. This procedure is useful for documents that might be passed to other users who do not have the add-in installed.

The following code example demonstrates how to remove all of the Windows Forms controls from a Word document when the document is closed.

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

See Also

Concepts

Adding Controls to Office Documents at Run Time

Helper Methods for Host Controls

Helper Methods for Windows Forms Controls