Effectively Using ActiveX Form Controls in Microsoft Word

This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.

 

Cindy Meister
INTER-Solutions
Microsoft Word MVP

May 2003

Applies to:
   Microsoft® Word 97, Word 2000 and Word 2002

Summary: Cindy Meister, a Microsoft Word MVP, presents an overview of the choices presented to a Word developer with respect to automating OLE/ActiveX objects in a Word document. Specifically, she explores how ActiveX Controls from the Control Toolbox can be used effectively in a Word document. (17 printed pages)

Download OfficeWordActiveXFormControlsSample.exe.

Contents

Introduction
Appropriateness For The Task
Building A Form
Preparing The New Form Document

Introduction

Since Microsoft® Word 6.0, Word has provided a way to create an on-line form with its form fields, from the Forms toolbar. Because these are native to Word, they work optimally in Word documents to provide a means to enter data directly into a protected Word document, be it as text, in text form fields, True/False in checkboxes, or a selection from a predefined list, offered in a Dropdown field.

What form fields can do, however, is limited, compared to the capabilities with which we have become familiar in dialog boxes, UserForms, forms created in other applications such as Microsoft Access or Microsoft Visual Basic®, or what we know from the Internet. In Word 97, Microsoft introduced Visual Basic for Applications (VBA) across the Office application suite, with some shared interfaces; among them Drawing tools, CommandBars (toolbars) and UserForms. Some of the ActiveX® controls developed for UserForms were also made available on the Controls Toolbox toolbar (see Figure 1) for use in the application interface. In this way, commonly recognized controls such as command buttons, text boxes, option buttons, checkboxes, combo boxes and list boxes can be inserted into a Word document (or an Excel spreadsheet).

Aa140269.odc_wdactivex01(en-us,office.10).gif

Figure 1. Word's Control Toolbox toolbar

Unfortunately, simply inserting the controls into a document is often not enough to use them effectively. Since they were not developed to work specifically in the Word document interface, they do not always behave as you would expect or wish. This is also important when it comes to automating them, whether from within the Word application environment, or from outside it.

Note   You may be able to insert ActiveX controls—whether from Microsoft or third-party providers—not found in the Control Toolbox into a Word document. But it is not certain that they will function as expected, or that their COM interfaces will be available.

This article explores how ActiveX Controls from the Control Toolbox can be used effectively in a Word document. In general, the information is valid for all current versions of Word (97, 2000, 2002 and Word 11), but the specifics and code examples are based on testing done in Word 2002.

Appropriateness for the Task

As mentioned above, ActiveX controls were not developed specifically with the Word document environment in mind. So there are circumstances when ActiveX controls in a Word document should be avoided. Here is a list of known issues.

  • ActiveX controls inflate document size significantly.
  • They are not available on the Macintosh platform.
  • Macro security is triggered by the presence of ActiveX controls in a document. Even if no VBA code has been written for them, they contain a VBA element that activates the "macro storage" area of a Word document.

Note   If macro security is set to "High", Word will open a Form document containing ActiveX controls without a message to the user. However, the Control Toolbox will be displayed; Design view will be activated; and the Tools/Unprotect document option will not be available. The result: the user will not be able to use the form, and will not understand why, nor know how to fix it. Only by lowering macro security, or by signing the form digitally and trusting the digital signature can a form containing ActiveX controls be used in Word.

  • Word cannot always display them correctly on-screen, especially when the document is being scrolled. Among other things, they will appear to not move on the screen until the mouse button is released. As a rule of thumb, the older the version of Word, the greater the problems.
  • ActiveX controls are a possible source of document instability and IPF crashes (Invalid Page Faults). Here again, Microsoft has steadily and without fanfare improved the interface in newer versions of Word.
  • Keyboard shortcuts defined in Word cannot be used in ActiveX controls, even if the document is not protected as a form. Such elements are not part of the Word environment, any more than an inserted Excel spreadsheet object or chart is. Essentially, when the focus is in such a control, it has been placed in a little "window" to another application that is embedded in the Word document. As such it will follow the rules of this application. In order to use keyboard shortcuts in ActiveX controls, it is necessary to add code to its KeyDown, KeyUp and/or KeyPress events.
  • If an ActiveX control has been assigned an Accelerator (hot-key), this will take precedence over a Keyboard shortcut assignment of ALT plus that character in the Word environment: focus will move to the ActiveX control.
  • ActiveX controls are embedded objects, and a Word document handles them the same as it would any other graphical object. This means, among other things, that they will not adapt to the text flow, as a Form field does. While they can be set to expand or shrink with their content, they will never break with the text, but remain a square block.
  • This also means that the content of an ActiveX text box cannot break across pages, but is limited to, at most, a single page in a Word document.
  • It is not possible to format text in the text box control provided by the Control Toolbox. To the best of my knowledge, there is at the time of this writing no RichText ActiveX control that can work in a Word document.
  • Even though ActiveX controls, unlike form fields, will work in a document that is not protected as a form, the user can only use TAB to move from one to the next if document protection is in force. Otherwise, the mouse must be used, or VBA code in the KeyDown or KeyUp events must control the navigation.
  • In order to navigate in a protected form with the TAB key, the ActiveX controls must be positioned graphically in-line with the text; they may not be formatted with any text wrapping options. If text-wrapping is used, then the mouse or VBA is required for navigation.

Note   In Word 97, ActiveX controls are inserted by default with text wrapping. You must hold down SHIFT in order to have them inserted in-line with the text, or they must be formatted in-line with the text manually. In later versions of Word, the controls are inserted in-line with the text by default.

  • If the focus is in an ActiveX control, and the document is protected as a form, the Print Preview mode is not available. If a protected form contains only ActiveX controls, and no regular form fields or unprotected sections, Print Preview will not be available at all.
  • While all ActiveX controls from the Control Toolbox can be used in protected sections of a Word form, other controls (such as the Microsoft Calendar control) will probably not behave correctly. They may, however, work in unprotected sections.
  • The VBA properties of ActiveX controls are not always stable. For example, the Name property, given by Word upon insertion of the control, may change unexpectedly. Again, later versions of Word are more stable than earlier ones, especially Word 97.

Building a Form

A good way to learn how to deal with ActiveX controls is to build a sample form, like the one in Figure 2 (provided as a download file). This form contains textbox controls, a combo box list, a command button, option buttons, regular text input form fields, and a calendar control.

Aa140269.odc_wdactivex02(en-us,office.10).gif

Figure 2. Sample form document with ActiveX and form fields

Date field

Although this article is primarily about ActiveX controls, there are situations in which regular form fields are useful. There are a couple of these in the example, for instance the date.

The date at the top right of the letter is generated automatically by a text input form field. The type of the form field is set to Current date. The date automatically appears in the language specified in the Microsoft Windows® regional settings. This type of form field automatically fills in the current date, and disables the field for user input.

Possible alternatives to this type of form field would be:

  • A regular text input form field. An AutoNew macro or Document_New event could insert the current date into the field. An advantage of this method would be that the user could change the date. And it would remain static, unless either of the macro procedures were run again, or the user changed the date. The language would have to be handled by the VBA code.
  • An ActiveX control,, that could be handled as above. An advantage of this, or the following approaches, is that the document would not necessarily have to be protected as a form. When a document is protected as a form, certain functionality is not available to the user. For example, you cannot manipulate graphical objects or the header/footer. Sometimes, this is exactly what you want, other times, not.
  • A bookmark, that a macro would fill.
  • A CreateDate field. CreateDate reflects the date on which a file was created. Using File/Save As effectively creates a new file (rather than saving over the original). The advantage to this method would be that the date would remain static until the document name is changed using Save As from the File menu. The date language is determined by the language formatting of the document text. The document need not be protected as a form.

In the case of the fields, the date format is controlled by formatting switches in the field code. Where a macro inserts the date, the formatting must be part of the VBA code, and the date will not change dynamically.

The Address Block

As mentioned in the list of considerations when using ActiveX controls, navigation in the form using the Tab key is only possible when the document is protected as a form, and the controls are formatted in-line with the text (no graphical text-flow formatting has been applied).

The controls that make up the recipient's address block have been placed in a one-row, one-column table, right-aligned to the document margin. This helps position the controls and limit how much space they can take up on the page. The row height in the example can grow or shrink with its content; but it could also be set to an exact measurement if it's important to maintain layout.

The column width is set to an exact measurement (for Word 2000 and later, Table/Table Properties/Table/Options/Automatically resize to fit contents is disabled).

Textbox controls

The first text box control is meant for the recipient name and street address. Normally, you'd use separate fields for this, but for demonstration purposes, the fields have been combined; the user is expected to press Enter at least once. The question is: how do you get a textbox to accommodate the entire entry?

Note  Be sure to set the EnterKeyBehavior property to True for a control where the user is expected to press Enter to create new lines.

If you look at the available properties for a textbox control, (right-click on the Control and choose Properties or look at the Properties window in the Visual Basic Editor, if you have it open) you'll see, among others, AutoSize and Multiline. The latter enables the user to see multiple lines in the control if he presses Enter while typing. The former resizes the control in order to display all the content. Both work as designed (AutoSize has some problems displaying correctly in Word 97), but "as designed" may not be what you want in a Word document.

Note   In order for you to display document properties, you must unprotect the document. To do so, on the Tools menu, select Unprotect. In addition, the document must be in design mode. To do so, on the Control toolbox, click Design Mode.

If Multiline is not active, AutoSize does what you'd wish and expect: the width of the control is adjusted to the content. (Note that this functionality will not respect document, column or table margins.)

With Multiline active, however, the width of the control will no longer change, only the height will adapt to the content. And, should all the content be deleted at one point, the control's width will narrow to display only a single character—and stay there until the document is unprotected and the width reset in the Design mode. Not exactly a frustration to which you want to submit your users!

In order to get the best of both worlds, some VBA code is required, such as in Listing 1. There's really no reliable way to calculate exactly how much height a certain amount of text requires; it can only be approximated, based on the font, its size and the actual input. In this example, the font is Times New Roman (the default style in the document); increasing the size by a factor of .2 does a pretty good job of figuring the line height. Generally, Word adds 20% to the font size to determine line height, but depending on which font and font size is used, this factor may vary.

Next task is to get the number of lines. In this case, we assume that each additional line is defined by pressing Enter. All instances of vbCR (a carriage return) are determined in a Do...Loop. Finally, the control height is calculated by multiplying the line count times the font size factor, plus an arbitrary adjustment factor (here: 2) that must be determined by testing.

Listing 1. Adjust the height of a textbox control to display all the lines.

Public Function AdjustControlHeight(o_ctl As Object)
    Dim sFontSize As Single
    Dim szEntry As String
    Dim lLineCounter As Long
    Dim lPos As Long

    o_ctl.SelStart = 0
    sFontSize = o_ctl.FontSize + (0.2 * o_ctl.FontSize)
    szEntry = o_ctl.Text
    lLineCounter = 1
    Do While InStr(szEntry, vbCr)
        lPos = InStr(szEntry, vbCr)
        szEntry = Mid(szEntry, lPos + 1)
        lLineCounter = lLineCounter + 1
    Loop
    o_ctl.Height = (lLineCounter * sFontSize) + 2
    Exit Function
End Function

Note   If you have a choice, use a text input form field for multi-line data that has to adjust in both width and height. A form field is designed to fit into the text flow and will make these adjustments with no problems, whatsoever.

For the control txtAddress, AutoSize is set to False, and Multiline is set to True. The width of the control is set to the width of the table cell.

The fields for the city, state and postal code are not multi-line, and theoretically wouldn't need any support for setting their respective widths. Unfortunately, not all versions of Word handle the autosizing of an ActiveX control in a document interface well. As a rule of thumb, the older the version of Word, the less reliable Autosize will work.

And in NO version does it respect the margins.

When textboxes don't stand alone on a line, it's critical in a Word document that their width adjust properly to avoid cutting off content, or leaving big, ugly white spaces between that content and adjacent text or other controls. Test your form thoroughly in the environments where the ActiveX controls will be used. If there is any doubt about the reliability of AutoSize, you can disable the property and use a function such as that in Listing 2 to adjust the width.

Here, again, it's necessary to use an adjustment factor that will be dependent on the font used. Remember, only non-proportional fonts (like Courier New) have a predictable character width. Character widths of proportional fonts vary with the character; this makes it very difficult to calculate the required width for any range of text.

The example uses two factors: one is passed by the calling procedure that takes into account the expected content and total width of the control, and thus can be set for each control individually, if required. The other is the font size divided by 2. The width of the control is calculated based on the number of characters it contains, which is determined using the function SelLength.

These factors are completely arbitrary, and must be determined by testing with sets of the kind of data you expect the control will contain. Generally it's preferable to err on the side of generosity and take the chance of the control being too wide, than too narrow, which would cut off content.

Listing 2. Adjust the width of a text box to the content.

Public Function AdjustControlWidth(ctl As Object, sFactor As Single)
    Dim sEnd As Single, sWidthFactor As Single

    sWidthFactor = ctl.Font.Size / 2
    SelectControlContent ctl
    sEnd = ctl.SelLength
    If sEnd > 2 Then
        ctl.Width = (sEnd * sWidthFactor) - sFactor
    Else
        ctl.Width = 5
    End If
End Function

Note   The postal code has its own control in order to facilitate extraction of data into a database. Ideally, the State information should be in a separate field, as well, but for demonstration purposes, the form was set up with just these two fields on this line.

The remaining ActiveX text box controls in the document follow the same pattern as the txtCity and txtPostalcode fields.

Combo box control

The last control in the Address block, cboCountry, gives the user a list of countries from which to choose. Combo box controls cannot store their list content; it must be repopulated every time the control is re-initialized (the document is re-opened). This is often done with an AutoOpen or AutoNew macro, or a Document_Open or Document_New event.

The sample document uses ADO to get the list from an Access database (Countries2000.mdb in Access 2000 file format; Countries97.mdb in Access 97 file format are available for download), located in the same folder as the document containing the ActiveX controls, as shown in Listing 3.

Listing 3. Populate a combo box control with a list from a database.

Public Function PopulateList(cbo As MSForms.ComboBox)
    Dim conn As ADODB.Connection, rs As ADODB.Recordset
    Dim sConnectionString As String, sSQL As String
    Dim aCountries() As String, lCounter As Long, lNumRecs As Long
    
    sConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=" & ThisDocument.Path & "\Countries2000.mdb;" & _
        "User Id=admin;" & _
        "Password=;"
    sSQL = "SELECT [CountryName] From [CountryCodes] _
        ORDER BY [CountryName] ASC"
    Set conn = New ADODB.Connection
    Set rs = New ADODB.Recordset
    
    conn.Open sConnectionString
    rs.Open Source:=sSQL, ActiveConnection:=conn, _
        CursorType:=adOpenKeyset, LockType:=adLockOptimistic
    rs.MoveFirst
    lNumRecs = rs.RecordCount
    ReDim Preserve aCountries(lNumRecs - 1)
    Do While Not rs.EOF
        aCountries(lCounter) = rs.Fields(0).Value
        lCounter = lCounter + 1
        rs.MoveNext
    Loop
    rs.Close
    Set rs = Nothing
    conn.Close
    Set conn = Nothing
    cbo.List() = aCountries
End Function

There are two basic methods to pass items to a combo box control: using the Item method, or assigning an array to the List property. The sample code uses the latter approach.

The control's Style property is set to fmStyleDropDownCombo so that the user can type in something other than an entry in the list; and MatchRequired is set to False. MatchEntry is set to frmMatchEntryComplete so that the list will quickly scroll to a matching entry as the user types in a country designation. Pressing Tab will then select that item and move to the next control.

ActiveX controls carry many properties with them. Others are inherited from the environment in which they're embedded. This can be confusing if you're accustomed to using the controls in a UserForm. There is no TabIndex or TabStop property for ActiveX controls in a Word document, for example.

The Tab order for ActiveX controls in-line with the text is the same as for form fields and conforms to the document text flow: from left-to-right and top-to-bottom for left-to-right languages. (Remember: You can't navigate with Tab between ActiveX controls with text wrap formatting.) If the controls are placed in frames (from the Forms toolbar), then the Tab order corresponds to the order of the frames' anchors in the document.

In addition, there is no SetFocus method in the Word document environment.

This makes changing the order in which ActiveX controls are selected a bit of a challenge. It's especially critical if data validation is required, where the user should be returned to a control if the content is not acceptable. To compound the difficulty an ActiveX control in a Word document also lacks AfterUpdate and BeforeUpdate events.

One effective approach to this problem is to use a similar method as with form field controls. Where as macros can be assigned to the "Enter" and "Exit" form fields (Form Field Options dialog box); the GotFocus and LostFocus events can perform the same function with ActiveX controls.

Note   Be extremely careful when testing with active GetFocus and LostFocus events. Be sure to save all your work in Word before doing anything that could cause these to fire, as you could end up in an endless loop. If the focus starts to bounce back and forth between controls, triggering these two events in succession, Word will appear to freeze. Part of the problem is that GetFocus and LostFocus are not aware of each other: the one won't wait for the other to finish. You have to be very careful how code is constructed that uses these for data validation and navigation, and test thoroughly.

Referencing ActiveX Controls in VBA Code

In general, embedded objects with an OLE interface are accessed using the OLEFormat.Object property of the Shape or InlineShape object in the document. ActiveX controls can also be addressed in this manner. Listing 4, with the GotFocus event for the txtAddress control demonstrates this.

Listing 4. Access the VBA interface of an inline OLE object.

Private Sub txtAddress_GotFocus()
    Dim doc As Word.Document
    Dim o_Control As Object
    
    Set doc = ActiveDocument
    Set o_Control = Selection.InlineShapes(1).OLEFormat.Object
    ValidateData doc, o_Control
End Sub

But, because they're also integrated into and interface with a document's VBA segment—their code interface is in the ThisDocument module of the document—they can be manipulated just as any other object in the document. This is especially useful when automating a document from another application. Listing 5 shows how you can get the text value of an ActiveX control in a document, using VBA from any other application, or Visual Basic.

Listing 5. Automate Word to get the value from an ActiveX control in a document.

Sub GetActiveXValueFromWordDoc()
    Dim wdApp as Word.Application
    Dim doc As Word.Document
    
    Set wdApp = New Word.Application
    Set doc = wdApp.Documents.Open("Test.doc")
    Debug.Print doc.txtAddress.Value
    doc.close wdDoNotSaveChanges
    Set doc = Nothing
    wdApp.Quit
    Set wdApp = Nothing
End Sub

For controls within the same document where the code is executing, you can simply use the control name, as it is part of ThisDocument.

Why do the GotFocus events in the sample document use the first method, in Listing 4? Because then it's not necessary to name the control explicitly in the code; Selection.InlineShapes(1).OLEFormat.Object will pick up the ActiveX control the user is entering. It can be copied without changes to the GotFocus event of every control.

Data validation

The GotFocus event in Listing 4 passes the document containing the ActiveX control and the control to the function ValidateData, in Listing 6.

The value of the document variable DataValidation is checked. If it contains the string "True", then the control that was just exited contains valid content. In this case, the value of the document variable PreviousControl is set to the name of the control just entered, and this control is passed to the function SetControlContent.

Listing 6. Check to determine if document contains valid content

Public Function ValidateData(doc As Word.Document, o_Control As Object)
    On Error Resume Next
    If doc.Variables("DataValidation").Value = "True" Then
        doc.Variables("PreviousControl").Value = o_Control.Name
        SelectControlContent o_Control
    Else
        doc.Bookmarks(doc.Variables( _
         "PreviousControl")).Range.InlineShapes(1).Select
    End If
End Function

Public Function SelectControlContent(ctl As Object)
    Dim lTextLen As Long
    
    lTextLen = Len(ctl.Text)
    ctl.SelStart = 0
    ctl.SelLength = lTextLen
End Function

SetControlContent selects the text in a text box control. Why is this needed? Because the GotFocus event apparently prevents the setting frmEnterFieldBehaviorSelectAll of the EnterFieldBehavior property from doing its job reliably. Sometimes it will work; sometimes not. The SetControlContent function ensures that the entire content of the field being moved into is selected.

If the content of the control that was just exited is not valid, the focus is moved back into it. It is identified using the value of the document variable PreviousControl, which also represents a bookmark in the document. Unlike form fields, ActiveX controls do not automatically create a bookmark in the document; these bookmarks were inserted by selecting each ActiveX control and entering the bookmark name from the Bookmark option of the Insert menu. Even when the document is protected as a form, VBA can identify a bookmark range and, since the ActiveX control is considered unprotected, select it.

How and when are the values of the document variable and DataValidation set? During the LostFocus event of the control that was just exited. As before, the document containing the control and the control are passed to a function, in this case SetDataValidation. (See Listing 7.)

In this simplified example, the only criterion a control has to meet is that it may not be left empty. If it is empty, the value of DataValidation is set to False. This causes the ValidateData function (Listing 6) to return to this control.

Listing 7. The data is validated when exiting the control.

Private Sub txtAddress_LostFocus()
    Dim doc As Word.Document
    
    Set doc = ActiveDocument
    SetDataValidation doc, txtAddress
    'Display entire text content of the control, not just the last line
    txtAddress.SelStart = 0
    AdjustControlHeight txtAddress
    txtAddress.SelStart = 0
End Sub

Public Function SetDataValidation(doc As Word.Document, _
  o_Control As Object)
    If Len(o_Control.Text) = 0 Then
        doc.Variables("DataValidation") = "False"
    Else
        doc.Variables("DataValidation") = "True"
    End If
End Function

What about fields that do not need to be validated, such as cboCountry? In this case, the value of the document property DataValidation is simply set to "True", directly in the LostFocus event.

**Note **   When the document is initialized (in the Document_Open event, for example), the values of these document variables are reset to a default. This is especially important if the document variables were never used before in the document. Document variables cannot contain zero-length strings; they can't be "empty". If the content of a document variable is deleted, the variable itself disappears.

Command Button

The sample form contains a single command button control. Clicking it displays a calendar control. As with other ActiveX controls in a protected form, a command button should be formatted inline with the text, otherwise it may not be triggered by a mouse-click. (It will, however, react to it accelerator key, if one has been specified in the control's properties.)

If you need to position the command button with text flow, you can insert it into a Frame (from the Forms toolbar), just as with any control.

Often, you don't want a command button to appear when a form is printed. As long as it is inline with the text, simply select it while in Design Mode and apply "hidden" font formatting. Make sure the option to print hidden text is disabled (click Print on the Options submenu of the Tools menu). (Note that you will probably still see it in Print Preview, but it should not print.)

Calendar Control

The Professional versions of Office come with a calendar control, mscal.ocx, that can be used in a Word document. Since this control was not made to work with Word documents, it does not function correctly when located in a protected section of the document. While you can use VBA code to manipulate it, and it can be clicked on, the focus will generally jump to the next available, unprotected area. If you're using GotFocus and LostFocus events, this can result in unpleasant loops that make it appear as if Word has stopped responding.

So place a calendar control in an unprotected document section.

You may have noticed that this control is not visible in Figure 2. It's been formatted as hidden, so that it won't show up on the print out, but that's not the entire story. The control's height has also been set to the absolute minimum it will accept in the current environment. The smallest acceptable value appears to depend on the version of Word. In Word 2002 .5 points works; in Word 2000 .85 points. This makes the calendar essentially invisible on-screen, especially if the display of non-printing characters is turned off.

When the user tabs into the txtDate form field in the body of the text, or clicks the cmdDate command button, the procedure in Listing 8 is run, which increases the control's height (in Word 2002 a height of 144 points seems optimal; in Word 2000 200 points). It then appears on-screen as in Figure 3. Clicking on a date executes the code in Listing 9 and the LostFocus event hides the control, again.

Listing 8. Increase the height of the calendar control to make it visible.

Public Sub DisplayCalendar()
    ActiveDocument.cal.Height = 144
End Sub

Aa140269.odc_wdactivex03(en-us,office.10).gif

Figure 3. The fully displayed Calendar control (Calendar.bmp)

As an ActiveX control, a calendar also presents an interface in the ThisDocument module. In Listing 9, the control's Value property returns the selected date. The Visual Basic Format function takes care of making the date appear the way we want it, and this is set as the Result of the form field txtCourseDate. Then the focus is taken out of the calendar and placed in the next field.

The LostFocus event resets the calendar's height so that it appears invisible.

Listing 9. The calendars Click and LostFocus events.

Private Sub cal_Click()
    Dim szDate As String
    'Chr$(160) is a protected space,
    ' so that the Date stays together on a line
    szDate = Format(cal.Value, "mmmm" & Chr$(160) & "d, " _
      & Chr$(160) & "yyyy")
    ActiveDocument.FormFields("txtCourseDate").Result = szDate
    ActiveDocument.Bookmarks( _
     "txtSignatureInstructor").Range.InlineShapes( _
     1).OLEFormat.Object.Select
End Sub

Private Sub cal_LostFocus()
    cal.Height = 0.5
End Sub

Option Buttons

A frequently asked question concerning option buttons in a document is how to make them mutually exclusive. In a Userform, you have the option of inserting them into a Frame control. This is not available in the Word document environment.

But if you assign the same name to option buttons' GroupName property, then those in the same group will be mutually exclusive.

In versions of Word prior to 2002 you also need to be aware that option buttons don't print properly: they will all print as if they hadn't been activated. This problem was correct in Word 2002 and later versions.

In earlier versions of Word you'd need to use a macro named FilePrint (or in Word 2000 you could use the BeforePrint method) to overlay or replace the options buttons with font symbols that represent the controls' actual status.

Preparing the New Form Document

Another tricky aspect about using ActiveX controls with VBA is that, while the controls themselves will appear in a document created from a template, the code in the ThisDocument module will not be ported to the new document. It will still be there in the attached template, but any references to the controls that do not specify in which document to look for them will appear not to work.

Compare how the code in Listing 10 passes the cboCountry control to the PopulateList function with how the txtAddress control is passed in the LostFocus event of Listing 7. In Listing 10, it's clear that the control in question is found in the "ActiveDocument"; in Listing 7, the control is assumed to be in the same file containing the code because it's not otherwise qualified.

So, you have the choice of

  • Specifying in your code in which document VBA should look for the control.
  • Having the template make a copy of an existing document, then open that.
  • (Re-)Using a document instead of a template. (You could display the Save As dialog box (on the File menu) to remind the user to save the document to a new name for a new form.)

The Document_New event in Listing 10 sets up the form. The combo box is populated, any fields (dates, links to external files and so forth) in the document are updated, the document variables for the data validation are initialized, and the first control is selected. In addition, hidden text is displayed, while all other non-printing characters and bookmarks are suppressed.

Listing 10. Prepare the form when a new document is created from a template.

Private Sub Document_New()
    Dim doc As Word.Document
    
    Set doc = ActiveDocument
    doc.cboCountry.Clear
    doc.Fields.Update
    PopulateList doc.cboCountry
    doc.Variables("PreviousControl").Value _
      = doc.InlineShapes(1).OLEFormat.Object.Name
    doc.Variables("DataValidation").Value = "False"
    doc.InlineShapes(1).Select
    With doc.ActiveWindow.View
        .ShowHiddenText = True
        .ShowAll = False
        .ShowBookmarks = False
    End With
End Sub

About the Author

Cindy Meister is an independent Office-automation consultant specializing in data import/export with MS Word. She's bilingual in English and German. Many of her articles have appeared in Informant Publications "Microsoft Office Developer" (later renamed "Microsoft Office Pro", then "Microsoft Office Solutions" and now "Smart Solutions") in the United States (some of which are available on the MSDN Web site), and in the German edition of "Inside Word". In 2003 MS Press, Germany, published "Microsoft Word Das Profibuch", of which she is co-author. Cindy Meister has been a Microsoft MVP for MS Word since 1996.