This is Part 2 of an FAQ on Using FormFields in Word. This part covers some sample VBA code that deals with fairly common FormField issues. First of all, it is important to call attention to a very important point.
Explicit Naming.
When you put a FormField in a document, Word gives it a default name. The first text FormField is named ôText1ö, the second is named ôText2ö. The first dropdown Formield is named ôDropDown1ö, the second ôDropDown2ö etc.
If you plan to use even a minor amount of VBA with FormFields, do NOT use the default names. I know it is a serious pain in the butt to rename them all. Most people do not bother to rename them. However, there is this to consider. Word gives the names dynamically. In other word, the FormField ôText1ö keeps that name ONLY if it is the first text FormField in the document. If that FormField is moved, it no longer is named ôText1ö Any code that points to Text1 (getting a result from it, setting properties), does NOT point to a specific FormField, it only points to the first one. It you want to make sure your code acts on a specific FormField (and you usually do), then exp-licitly name it. Once named, you can move it around the document all you want. Any code pointing to it, by a specific name will always point to it, regardless of where it is.
OK, here is some code.
Code A Two OnEntry macros resetting properties of the FormField, itself.
What this could be used for. Version 1 automatically fills in text with the result of another text FormField. Version 2 fill in a drop down list based on the result of a previous text FormField.
OnEntry Macro - Version 1 Assumptions: A text FormField named InitialChecker, a text FormField named AuthorizedBy.
CODE
Sub UpdateAllowChanges() æ this updates the AuthorizedBy FormField with the result of InitialChecker æ allows user to change it
Dim sPrevious As String sPrevious = ActiveDocument.FormFields(ôInitialCheckerö).Result ActiveDocument.FormFields(ôAuthorizedByö).Result = sPrevious End Sub
OnEntry Macro - Version 2 Assumptions A text FormField named InitialChecker, a dropdown FormField named R_Area. This uses Select Case to determine how to fill in the drop down list.
CODE
Sub UpdateR_Area()
Dim InitializedList(3) As String
Dim R_AreaList1(3) As String Dim R_AreaList2(2) As String Dim R_AreaList3(4) As String Dim R_AreaList4(1) As String
R_AreaList3(0) = " Warehouse - San Francisco " R_AreaList3(1) = " Warehouse - Seattle" R_AreaList3(2) = " Warehouse - Auckland " R_AreaList3(3) = " Warehouse - Tuktayuktuk " R_AreaList3(4) = " Warehouse - No where "
R_AreaList4(0) = " Mars " R_AreaList4(1) = " Moon "
Select Case ActiveDocument.FormFields("InitialChecker").Result Case InitializedList(0) ' Fred Flintstone ' clear list ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Clear For var = 0 To 4 ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Add _ R_AreaList3(i) i = i + 1 Next ' display first item in list ActiveDocument.FormFields("R_Area").DropDown.Value = 1 Case InitializedList(1) ' Yogi Bear ' clear list ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Clear For var = 0 To 3 ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Add _ R_AreaList1(i) i = i + 1 Next ' display first item in list ActiveDocument.FormFields("R_Area").DropDown.Value = 1 Case InitializedList(2) ' Harvey Keitel ' clear list ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Clear For var = 0 To 2 ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Add _ R_AreaList2(i) i = i + 1 Next ' display first item in list ActiveDocument.FormFields("R_Area").DropDown.Value = 1 Case InitializedList(3) ' Someone ' clear list ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Clear For var = 0 To 1 ActiveDocument.FormFields("R_Area").DropDown.ListEntries.Add _ R_AreaList4(i) i = i + 1 Next ' display first item in list ActiveDocument.FormFields("R_Area").DropDown.Value = 1 Case Else ' in case they enter something you don't like MsgBox "The entry in the Initial Checker field is not valid." _ & " Please check your entry and try again." ' return back to InitialChecker FormField ' note the use of the BOOKMARK object to select the FormField ActiveDocument.Bookmarks("InitialChecker").Select End Select End Sub
Code B: An OnExit macro that sets a specific value in another FormField, in this case, disabling it.
Assumptions: a checkbox named OrderNow, a dropdown named PaymentMethod
CODE
Sub OrderChoice()
If ActiveDocument.FormFields("OrderNow").CheckBox.Value = False Then ActiveDocument.FormFields("PaymentMethod").Enabled = False Else ActiveDocument.FormFields("PaymentMethod").Enabled = True End If End Sub
Code C What it could be used for: Counting Formfields, Making an array of a certain Formfield type (Textboxes in this example) and the values for each of that type.
CODE
Sub TextFFArray()
Dim mFormField As FormField Dim i As Integer Dim intFFCount As Integer Dim intTextFFCount As Integer Dim TextFormFields() As String Dim sMessage As String Dim var
For Each mFormField In ActiveDocument.FormFields() ' increase total count of fields intFFCount = intFFCount + 1 If mFormField.Type = wdFieldFormTextInput Then ' increase total count of text fields intTextFFCount = intTextFFCount + 1 ReDim Preserve TextFormFields(i) TextFormFields(i) = mFormField.Name i = i + 1 End If Next ReDim Preserve TextFormFields(i) ' may as well use this again i = 0 ' loop through array getting field name & ' using it to get resulting value For var = 1 To intTextFFCount sMessage = sMessage & _ "Field name:= " & TextFormFields(i) & " " & _ "Value:= " & ActiveDocument.FormFields(TextFormFields(i)).Result & _ vbCrLf i = i + 1 Next ' display message with: ' total number of fields, ' total number of textboxes ' each textbox name and result MsgBox "There are " & intFFCount & " FormFields in this " & _ "document, including " & intTextFFCount & " textboxes." & _ vbCrLf & " The contents of those textboxes are:" & vbCrLf & _ sMessage End Sub
Code D What it could be used for: Clears all FormFields. Use this as the exit macro on the last field, or as a separate closing macro to the document. It checks for default text, and if the textbox has default text, it resets the result to that default again.
CODE
Sub ClearAllFormFields() Dim myFormField As FormField Dim aDoc As Document Dim response Dim msg As String
Set aDoc = ActiveDocument msg = "Do you want to clear all FormField results?" response = MsgBox(msg, vbYesNo) If response = vbYes Then For Each myFormField In aDoc.FormFields() Select Case myFormField.Type Case 70 ' text ' checks to see if there is default text ' if yes, removes user inout and ' reverts to default; if no, removes user input If myFormField.TextInput.Default <> "" Then myFormField.result = myFormField.TextInput.Default Else myFormField.result = "" End If Case 71 ' check myFormField.result = False Case 83 ' dropdown myFormField.DropDown.Value = 1 End Select Next Else End If Set aDoc = Nothing End Sub
Code E What this could be used for: This is an example of looping through a number of files and retrieving data from them. This example retrieves a piece of information from each document in a folder and calculates an average from the total collected. It could also be used for picking up strings for FormFields. Maybe use this in conjunction with the other code above to create an array of text from various files.
CODE
Sub GetAv() ' assumes each file has a FormField named GetAv ' and that FormField has a piece of text that is a number ' Probably should add a error trap (IsNumeric) to ensure ' that content of text FormField is, in fact numeric! Dim aDoc Dim ThisDoc As Document Dim i As Integer Dim lngTotalCount As Long Dim lngCurNumber As Long Dim strMessage As String
' this does not have to be hard coded aDoc = Dir("c:\TempRun\*.DOC")
On Error Resume Next
Do While aDoc <> "" ' open the file and make it document object Application.Documents.Open FileName:=aDoc Set ThisDoc = ActiveDocument
' get the FormField text and convert to number lngCurNumber = CLng(ThisDoc.FormFields("getAv").Result) lngTotalCount = lngTotalCount + lngCurNumber i = i + 1 ' add filename and value to message string strMessage = strMessage & _ ThisDoc.Name & " " & lngCurNumber & vbCrLf End If ' close current doc, destroy doc object ThisDoc.Close Set ThisDoc = Nothing aDoc = Dir() Loop ' display results. This could be changed, to putting the average æ (lngTotalCount / i ) into somewhere else.
MsgBox strMessage & vbCrLf & vbCrLf & _ "Average is: " & lngTotalCount / i End Sub
Hopefully this FAQ, and these examples, will help you use WordÆs FormFields more efficiently. There is a lot more you can do with FormFields, this is just a start.