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 discuss and provide a VBA solution to find and replace text wherever it may appear in a document. This content is a modified version of my article on the same topic previously published at the Word MVP FAQ website. Acknowledgments to Doug Robbins, Peter Hewett and Jonathan West for their contributions to that article.
Using the Find or Replace utility on the Edit menu you can find or replace text "almost" anywhere it appears in the document. If you record that action however, the scope or "range" of the resulting recorded macro will only act on the text contained in the body of the document (or more accurately, it will only act on the part of the document that contains the insertion point). This means that if the insertion point is located in the main body of the document when your macro is executed it will have no effect on text that is in the headers or footers of the document, for example, or in a textbox, footnotes, or any other area that is outside the main body of the document.
In Word 2007 or earlier even the built-in Find & Replace utility has a shortcoming. For example, text in a textbox located in a header or footer is outside the scope of the Find and Replace utility search range.
There are eleven to seventeen wdStoryType constants that can form the StoryRanges (or parts) of a document. Not all storyranges are used in every document. The seventeen possible storytypes in a Word 2016 document is shown below.
The comprehensive code to ensure you look for and find text wherever is may be in a document is complex. Accordingly, let’s take it a step at a time to better illustrate the process and identify the issues. In many cases this simpler incremented code will be sufficient for getting the job done.
Sub FindAndReplaceFirstStoryOfEachType() Dim rngStory As Range For Each rngStory In ActiveDocument.StoryRanges With rngStory.Find .Text = "find text" .Replacement.Text = "I'm found" .Wrap = wdFindContinue .Execute Replace:=wdReplaceAll End With Next rngStory lbl_Exit: Exit Sub End Sub
Bonus Tip: If you are not aware, when use the Selection.Find method, you should specify all of the Find and Replace parameters, such as .Forward = True, because the values are otherwise taken from the Find and Replace dialog's current settings, which are “sticky. This is not necessary when using Range.Find, because the parameters are their default value if you don't specify a value in your code.
The basic macro above has a shortcoming. It only acts on the "first" StoryRange of each of the (eleven-seventeen) StoryTypes.
While a document only has one wdMainTextStory StoryRange, it can have one or more StoryRanges in several of the other StoryTypes. For example, a document may contain multiple text boxes, or multiple sections with un-linked headers or footers. Each of those multiple elements is a StoryRange in the StoryType. The basic code only process the first StoryRange in those StoryTypes.
To make sure that the code acts on every StoryRange in each each StoryType, you need to:
Public Sub FindReplaceAlmostAnywhere() Dim rngStory As Word.Range Dim lngValidate As Long 'Fix the skipped blank Header/Footer problem as provided by Peter Hewett. lngValidate = ActiveDocument.Sections(1).Headers(1).Range.StoryType 'Iterate through all story types in the current document. For Each rngStory In ActiveDocument.StoryRanges 'Iterate through all linked stories. Do With rngStory.Find .Text = "Test" .Replacement.Text = "I'm found" .Wrap = wdFindContinue .Execute Replace:=wdReplaceAll End With 'Get next linked story (if any). Set rngStory = rngStory.NextStoryRange Loop Until rngStory Is Nothing Next lbl_Exit: Exit Sub End Sub
The VBA "Trickery" mentioned above simply returns the value of the Section One Primary Header StoryType. This has proven to eliminate the problem VBA has of "jumping" empty unlinked header/footers and continuing to process sequent headers and footers.
There is one remaining shortcoming with the intermediate code above. Like the menu Find and Replace utility in earlier Word versions, this code will miss and fail to process textboxes and shapes anchored to a header/footer StoryRange.
The solution to the header/footer textbox and shape problem is found in the fact that textboxes and shapes are contained in the document’s ShapeRange collection. First we check the ShapeRange of each of the six header and footer StoryRanges in each document section for for the presence of a shape. If a shape is found, we then check shape's .TextFrame.TextRange for the .Find.Text parameter.
This final macro contains all of the code to find and replace text “anywhere” in a document. A few enhancements have been added to make it easier to apply the desired find and replace text strings:
Public Sub FindReplaceAnywhere() Dim rngStory As Word.Range Dim strFind As String, strRplc As String Dim lngValidate As Long Dim oShp As Shape strFind = InputBox("Enter the text that you want to find.", "FIND") If strFind = "" Then MsgBox "Cancelled by User" Exit Sub End If TryAgain: strRplc = InputBox("Enter the replacement.", "REPLACE") If strRplc = "" Then If MsgBox("Do you just want to delete the found text?", _ vbYesNoCancel) = vbNo Then GoTo TryAgain ElseIf vbCancel Then MsgBox "Cancelled by User." Exit Sub End If End If 'Fix the skipped blank Header/Footer problem. lngValidate = ActiveDocument.Sections(1).Headers(1).Range.StoryType 'Iterate through all story types in the current document. For Each rngStory In ActiveDocument.StoryRanges 'Iterate through all linked stories. Do SearchAndReplaceInStory rngStory, strFind, strRplc On Error Resume Next 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 SearchAndReplaceInStory oShp.TextFrame.TextRange, strFind, strRplc 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 lbl_Exit: Exit Sub End Sub Public Sub SearchAndReplaceInStory(ByVal rngStory As Word.Range, _ ByVal strSearch As String, _ ByVal strReplace As String) With rngStory.Find .ClearFormatting .Replacement.ClearFormatting .Text = strSearch .Replacement.Text = strReplace .Wrap = wdFindContinue .Execute Replace:=wdReplaceAll End With lbl_Exit: Exit Sub End Sub
Note: This tips page, illustrations and examples were developed using Word 2016. It is wholly functional with Word 2003-2016.
That's it! I hope you have found this tips page useful and informative.
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!