The Anchorage
Personal website of Gregory K. Maxey, Commander USN (Retired)
The information, illustrations and code contained in my "Microsoft Word Tips" are provided free and without risk or obligation.
However, the work is mine. If you use it for commercial purposes or benefit from my efforts through income earned or time saved then a donation, however small, will help to ensure the continued availability of this resource.
If you would like to donate, please use the appropriate donate button to access PayPal. Thank you!
The purpose of this Microsoft Word Tips & Microsoft Word Help page is to publish a collection of VBA procedures (macros) useful for working with the Word field collection.
I love Word fields. They are a semi-advanced Word feature and with the exception of VBA, I think that they are the most powerful and useful feature in the application. For more on Word fields in general, see my: Word Fields
Before getting to the specific field macros, I think it is important to review a few technical issues (or perhaps quirks) associated with using VBA procedures in general and using them with the field collection in particular.
Many of you are familiar with Word's Find or Find and Replace feature. It has probably saved millions of people countless hours in document preparation and editing. Seemingly by magic it can find anything and replace it with anything else.
If you record a macro of running a Find and Replace operation and then run that macro, the magic of Find and Replace seems to have lost some of the shine.
Note: A full discussion of the issue addressed above is available at: Using a Macro to Replace Text Wherever It Appears in a Document
Accepting the issues discussed above, my approach to employing Word field macros is to start with a "universal" procedure that will process the entire document. With this as the starting point I can then insert calls to specific "actionable" procedures that act on the targeted fields. The basic universal procedure is provided below.
Public Sub UniversalFieldMacro() Dim rngStory As Word.Range Dim lngLink As Long Dim oShp As Shape lngLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType For Each rngStory In ActiveDocument.StoryRanges 'Iterate through all linked stories Do On Error Resume Next 'Insert call to action procedure here Select Case rngStory.StoryType Case 6, 7, 8, 9, 10, 11 If rngStory.ShapeRange.Count > 0 Then For Each oShp In rngStory.ShapeRange If oShp.TextFrame.HasText Then 'Insert call to action procedure here End If Next End If Case Else 'Do Nothing End Select On Error GoTo 0 'Get next linked story (if any) Set rngStory = rngStory.NextStoryRange Loop Until rngStory Is Nothing Next rngStory lbl_Exit: Exit Sub End Sub
See: Installing Macros for instructions on how to set up and use the macros provided in this Microsoft Word Help & Microsoft Word Tips page.
If you study the code , you should see that each stroyrange, linked storyrange and troubled shape TextFrame is processed and a Word field will have nowhere to hide.
Fields are an object and therefore a countable item in any given range. If you pass a range containing one or more fields as an argument to a VBA procedure, you can then delete all of the fields contained in that range.
Sub FieldsDeleteAll(ByRef oTargetRng As Range) With oTargetRng.Fields While .Count > 0 .Item(1).Delete Wend End With lbl_Exit: Exit Sub End Sub
Combining the procedure above with the universal field macro, you can delete all fields in a document.
Public Sub UniversalFieldMacro() Dim rngStory As Word.Range Dim lngLink As Long Dim oShp As Shape lngLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType For Each rngStory In ActiveDocument.StoryRanges 'Iterate through all linked stories Do On Error Resume Next 'Call actionable procedure FieldsDeleteAll rngStory Select Case rngStory.StoryType Case 6, 7, 8, 9, 10, 11 If rngStory.ShapeRange.Count > 0 Then For Each oShp In rngStory.ShapeRange If oShp.TextFrame.HasText Then 'Call actionable procedure FieldsDeleteAll oShp.TextFrame.TextRange End If Next End If Case Else 'Do Nothing End Select On Error GoTo 0 'Get next linked story (if any) Set rngStory = rngStory.NextStoryRange Loop Until rngStory Is Nothing Next rngStory lbl_Exit: Exit Sub End Sub Sub FieldsDeleteAll(ByRef oTargetRng As Range) With oTargetRng.Fields While .Count > 0 .Item(1).Delete Wend End With lbl_Exit: Exit Sub End Sub
Perhaps you don't want to delete all fields. You may only want to delete a particular type field. To do this you simply create an actionable procedure targeting only a specific field (or fields)and call it using the universal macro.
Sub DeleteAllSpecificFields(ByRef oTargetRng As Range)
Dim oFld As Word.Field
For Each oFld In oTargetRng.Fields
Select Case oFld.Type
Case wdFieldPage
oFld.Delete
Case Else
'Do nothing
End Select
Next oFld
lbl_Exit:
Exit Sub
End Sub
Then there is the case when you may only want to delete fields in specific storyrange (e.g., headers or footers). It can start to get complicated, but again you can use the universal macro to call a carefully crafted actionable procedure.
Public Sub UniversalFieldMacro() Dim rngStory As Word.Range Dim lngLink As Long Dim oShp As Shape lngLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType For Each rngStory In ActiveDocument.StoryRanges 'Iterate through all linked stories Do On Error Resume Next 'Call actionable procedure 'FieldsDeleteAll rngStory 'DeleteAllSpecificFields rngStory DeleteAllSpecificFieldsInSpecifStoryRange rngStory, wdPrimaryFooterStory Select Case rngStory.StoryType Case 6, 7, 8, 9, 10, 11 If rngStory.ShapeRange.Count > 0 Then For Each oShp In rngStory.ShapeRange If oShp.TextFrame.HasText Then 'Call actionable procedure 'FieldsDeleteAll oShp.TextFrame.TextRange 'DeleteAllSpecificFields rngStory End If Next End If Case Else 'Do Nothing End Select On Error GoTo 0 'Get next linked story (if any) Set rngStory = rngStory.NextStoryRange Loop Until rngStory Is Nothing Next rngStory lbl_Exit: Exit Sub End Sub Sub DeleteAllSpecificFieldsInSpecifStoryRange(ByRef oTargetRng As Range, lngStoryType As Long) Dim oFld As Field Dim oShp As Shape Select Case oTargetRng.StoryType Case lngStoryType For Each oFld In oTargetRng.Fields Select Case oFld.Type Case wdFieldPage oFld.Delete Case Else 'Do nothing End Select Next oFld Select Case lngStoryType Case 6, 7, 8, 9, 10, 11 If oTargetRng.ShapeRange.Count > 0 Then For Each oShp In oTargetRng.ShapeRange If oShp.TextFrame.HasText Then For Each oFld In oShp.TextFrame.TextRange.Fields Select Case oFld.Type Case wdFieldPage oFld.Delete Case Else 'Do nothing End Select Next oFld End If Next oShp End If Case Else 'Do nothing End Select Case Else 'Do nothing End Select lbl_Exit: Exit Sub End Sub
In the code posted above, notice in the universal macro that the actionable procedure is only called in the main For Each rngStory ... Next loop and not repeated in the For Each oShp ... Next loop. This is because shaped (even shapes in the header and footers have their own storytype definition). Processing of the header and footer storyrange shapes is incorporated in the actionable procedure.
Some Word fields themselves target a collection. For example, the DocProperty field shown below returns the value of a targeted property "Author" in the document properties collection.
If you need to target a specific target in a specific field then you can use a customized VBA procedure to peel back another layer of the onion and look a the field code.
Given the unlikelihood that the field used in this example will be located in a shape in the header or footer, I've simplified the procedure by eliminating the code required to handle that situation.
Sub TargetSpecificTargetInSpecificFields()
Dim rngStory As Word.Range
Dim oFld As Word.Field
Dim lngLink As Long
lngLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType
For Each rngStory In ActiveDocument.StoryRanges
Do
For Each oFld In rngStory.Fields
Select Case oFld.Type
Case wdFieldDocProperty
'Dig a little deeper and see what the field code contains.
If InStr(oFld.Code.Text, "Author") Then
oFld.Unlink
End If
Case Else
'Do nothing
End Select
Next
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next
lbl_Exit:
Exit Sub
End Sub
This section shows you how you can target all fields with a general actionable procedure and delete, lock, unlink, update, or unlock all fields in a document using the universal field macro.
Sub FieldsActionAll(ByRef oTargetRng As Range, strAction As String, Optional bLocked As Boolean = False) 'Action is passed as an argument by the calling universal field macro. 'Example calls: 1. In the For Each rngStory ... Next loop: FieldsActionAll rngStory, "Update" 2. In the For Each oShp ... Next loop: oShp.TextFrame.TextRange, "Update" Select Case strAction Case "Update" oTargetRng.Fields.Update Case "Unlink" oTargetRng.Fields.Unlink Case "Locked" 'Takes the optional argument oTargetRng.Fields.Locked = bLocked 'You can pass "Delete" too!! Case "Delete" With oTargetRng.Fields While .Count > 0 .Item(1).Delete Wend End With End Select lbl_Exit: Exit Sub End Sub
Note: You should see that the actionable procedure shown eliminates the need for a specific actionable procedure Sub FieldsDeleteAll() demonstrated earlier!!
In the previous examples you saw how the universal field macro was used to call and pass arguments to the actionable procedures. Taking this a step further, you can call and pass arguments to the universal field macro making it the middle man for one or more field actions you may want to perform.
For example, I've assigned the keyboard shortcut F9 to the procedure FieldsUpdateAll shown below. By revising the UniversalFieldMacro to take an argument, I can simply press F9 and all fields in my document are updated.
Sub FieldsUpdateAll() UniversalFieldMacro "Update" lbl_Exit: Exit Sub End Sub Public Sub UniversalFieldMacro(ByRef strAction As String) Dim rngStory As Word.Range Dim lngLink As Long Dim oShp As Shape lngLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType For Each rngStory In ActiveDocument.StoryRanges 'Iterate through all linked stories Do On Error Resume Next 'Call actionable procedure FieldsActionAll rngStory, strAction Select Case rngStory.StoryType Case 6, 7, 8, 9, 10, 11 If rngStory.ShapeRange.Count > 0 Then For Each oShp In rngStory.ShapeRange If oShp.TextFrame.HasText Then 'Call actionable procedure FieldsActionAll oShp.TextFrame.TextRange, strAction End If Next End If Case Else 'Do Nothing End Select On Error GoTo 0 'Get next linked story (if any) Set rngStory = rngStory.NextStoryRange Loop Until rngStory Is Nothing Next rngStory lbl_Exit: Exit Sub End Sub
I'll use this space to post custom field macros that I've created or found interesting.
Public Sub ConstructFieldsWithVBA() Dim oRng As Range Dim oFooterRng1 As Range Dim oFooterRng2 As Range Set oRng = ActiveDocument.Sections(1).Footers(wdHeaderFooterPrimary).Range With oRng .Text = "IF PAGE = ""1""""""PAGE" Set oFooterRng1 = .Duplicate Set oFooterRng2 = .Duplicate End With 'Insert the field code around the complete expression fInsertFields oFooterRng1, "IF PAGE = ""1""""""PAGE" 'Insert the field code around the Page expressions fInsertFields oFooterRng2, "PAGE" oRng.Fields.Update ActiveWindow.View.ShowFieldCodes = False lbl_Exit: Exit Sub End Sub Public Sub fInsertFields(oRng As Range, Optional strText As String) With oRng 'Find the expression and add a field around it With .Find .Text = strText .MatchCase = True While .Execute oRng.Fields.Add oRng, wdFieldEmpty, , False oRng.Collapse wdCollapseEnd Wend End With End With lbl_Exit: Exit Sub End Sub
That's it for now! I hope you have found this tips page useful and informative. You can download a Word 2003 document containing all the code examples shown above: Field Macros.
The information, illustrations and code contained in my "Microsoft Word Tips" are provided free and without risk or obligation.
However, the work is mine. If you use it for commercial purposes or benefit from my efforts through income earned or time saved then a donation, however small, will help to ensure the continued availability of this resource.
If you would like to donate, please use the appropriate donate button to access PayPal. Thank you!