How do I write a loop statement to that will stop at the end of the doc?

J

Jeff Mitteer

I am doing a search on a document and need to perform some actions when the
search criteria is met. I am having a problem getting the loop to stop at
the end of the document. It just keeps looping. I am very new to Word VBA
and would appreciate any help.

Jeff
 
J

Jay Freedman

The usual construction is to start with a Range object that covers the
entire document body:

Dim myRange As Range
Set myRange = ActiveDocument.Range

Set the properties of the myRange.Find object (the .Text, .Replacement,
..MatchWildcards, and so forth). It's important to set myRange.Find.Wrap =
wdFindStop so the search stops at each occurrence of the target. Then write
the loop like this:

Do While myRange.Find.Execute
' do your work on myRange here
MyRange.Collapse wdCollapseEnd
Loop

The .Execute method returns True if it found the search target or False if
it didn't find it. That makes the loop stop when there are no more
occurrences between the current position of myRange and the end of the
document (you don't actually have to know whether you've "reached the end").

The .Collapse ensures that, if the target still exists within myRange after
you've worked on it, the next iteration won't find the same occurrence
again.

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
G

Greg Maxey

Jeff,

I helps to provide your code.

Try something like:

Sub Scratchmacro()
Dim oRng As Word.Range
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = "Your text"
.Wrap = wdFindStop
While .Execute
oRng.Font.Color = wdColorBlue
Wend
End With
End Sub
 
J

Jeff Mitteer

Jay said:
The usual construction is to start with a Range object that covers the
entire document body:

Dim myRange As Range
Set myRange = ActiveDocument.Range

Set the properties of the myRange.Find object (the .Text, .Replacement,
.MatchWildcards, and so forth). It's important to set myRange.Find.Wrap =
wdFindStop so the search stops at each occurrence of the target. Then write
the loop like this:

Do While myRange.Find.Execute
' do your work on myRange here
MyRange.Collapse wdCollapseEnd
Loop

The .Execute method returns True if it found the search target or False if
it didn't find it. That makes the loop stop when there are no more
occurrences between the current position of myRange and the end of the
document (you don't actually have to know whether you've "reached the end").

The .Collapse ensures that, if the target still exists within myRange after
you've worked on it, the next iteration won't find the same occurrence
again.
Thanks. I think that will work.

Jeff
 
G

Greg Maxey

Jay,

I have used a construction similiar to yours many times. That is one
reason I mentioned that the OP should provide his code.

In a new document I type the words "Your text"

and run this code:

Sub Scratchmacro()
Dim oRng As Word.Range
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = "Your text"
.Wrap = wdFindStop
While .Execute
oRng.Font.Color = wdColorBlue
Wend
End With
End Sub

In this simple test, it appears that .Wrap = wdFindStop is sufficient
to avoid that dreadful loop that often requires:

oRng.Collapse wdCollapseEnd ;-)
 
J

Jay Freedman

Hi Greg,

Of course, you're correct. I know I've been in situations where the
..Collapse was necessary, but I don't have an example handy. If I come across
one, I'll let you know what's different about it.

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
H

Helmut Weber

Hi Jay,

IMHO,
formatting does no harm,
however, replacing text seems to require
additional measures to control the range.

--
Greetings from Bavaria, Germany

Helmut Weber, MVP WordVBA

Win XP, Office 2003
"red.sys" & Chr$(64) & "t-online.de"
 
G

Greg Maxey

Jay/Helmut,

It seems that I have been tripped up before as well with a continous
loop without the .Collapse wdCollapseEnd statement, but despite trying
for the last hour, I had to resort to some non-standard code to produce
the situation. It will occur whenever you expand the found range
within the execute statement while leaving the find.text intact. In
this rather poor example, I am adding quotes to underlined text within
a document. With the .Collapse wdCollapseEnd statement a continous
loop occurs:

Sub ScratchMacro1()
'Adds quotes to underlined text
Dim oRng As Range
Set oRng = ActiveDocument.Content
With oRng.Find
.ClearFormatting
.Text = "<*>"
.Font.Underline = True
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
Do While .Execute
With oRng
.InsertBefore Chr(34)
.InsertAfter Chr(34)
.Characters.Last.Font.Underline = False
.Collapse wdCollapseEnd
End With
Loop
End With
End Sub

For me a more common problem is the job won't complete without that
statement. Take this example where I want to underline text in quotes:

Sub ScratchMacro2()
'Underlines text exclusive of the quotes marks
Dim oRng As Range
Set oRng = ActiveDocument.Content
With oRng.Find
.ClearFormatting
.Text = """<*>"""
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
Do While .Execute
With oRng
.MoveEnd Unit:=wdCharacter, Count:=-1
.MoveStart Unit:=wdCharacter, Count:=1
.Font.Underline = True
.Collapse wdCollapseEnd
End With
Loop
End With
End Sub

Without the collapse statement the code only formats the first found
instance.

I think I have read here somewhere before that the .execute won't find
..find.text that exactly matches the find.range.

So in a new document with Test text Test text

The following code:

Sub ScratchMacro3()
Dim oRng As Word.Range
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = "Test text"
.Wrap = wdFindStop
While .Execute
oRng.Text = "Test text"
Wend
End With
MsgBox oRn.Text
End Sub

Finds the first instances, the next .Execute collapses the range and
extends the range forward to the end of the document and finds the next
instance, at this point the oRng.Text and .Find.Text are identical and
..Execute terminates.

Here is another way of attempting to illustrate what I think happes.
You can't find the text of a document in the range of a document:

Sub ScratchMacro4()
Dim oRng As Word.Range
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = ActiveDocument.Range.Text
.Execute
If .Found Then
MsgBox "Found"
Else
MsgBox "Not found"
End If
End With
If oRng.Text = ActiveDocument.Range.Text Then
MsgBox "Doesn't is seem odd that Word doesnt' find the" _
& " ActiveDocument.Range.Text in the ActiveDocument.Range?"
End If
End Sub
 
T

Tony Jollans

I have just posted (in word.vba.customization) where a collapse was
necessary.

Each Find operation (each execution of the .Execute) is a stand-alone Find
and follows the same set of rules: if the range exactly satisfies the Find
criteria it is presumed to have been the result of a previous Find for the
same thing and searching starts after the range (the range is effectively
automatically collapsed); otherwise the Range is searched and if that
results in a not found condition, further searching depends on the Wrap
value.

If, after a find, you mess with the range - change its size - or its
contents or formatting - you will affect how the next iteration works. If
you have Wrap=Continue you can end up with an infinite loop in cases where
you don't change every found instance.
 
G

Greg Maxey

Tony,

Concur with your explanation in the Customization group message. That
is why I said:

"For me a more common problem is the job won't complete without that
statement."

This is a good explanation of what happens with .Execute:
Each Find operation (each execution of the .Execute) is a stand-alone Find
and follows the same set of rules: if the range exactly satisfies the Find
criteria it is presumed to have been the result of a previous Find for the
same thing and searching starts after the range (the range is effectively
automatically collapsed); otherwise the Range is searched and if that
results in a not found condition, further searching depends on the Wrap
value.

This might give anyone following this string a good example of how the
collapse statement ensures the code procedes when the range is reduced
and ensures it doesn't get in a continous loop if the range is expanded
in the .Execute portion of the code:

Sub Test1()
Dim oRng As Word.Range
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = "<*>"
.MatchWildcards = True
While .Execute
MsgBox oRng.Text
oRng.MoveEnd wdCharacter, -1
oRng.MoveStart wdCharacter, 1
MsgBox oRng.Text
oRng.Collapse wdCollapseEnd
Wend
End With
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = "<*>"
.MatchWildcards = True
While .Execute
MsgBox oRng.Text
oRng.MoveEnd wdCharacter, 1
oRng.MoveStart wdCharacter, -1
oRng.Collapse wdCollapseEnd
MsgBox oRng.Text
Wend
End With
End Sub
 
Joined
Mar 15, 2017
Messages
1
Reaction score
0
I am doing a search on a document and need to perform some actions when the
search criteria is met. I am having a problem getting the loop to stop at
the end of the document. It just keeps looping. I am very new to Word VBA
and would appreciate any help.

Jeff

Hi Jeff,
I had the same need, more than once. Finally I found a simple way, from word.tips.net. Here it is:

Selection.HomeKey Unit:=wdStory
[set up for your search]
Selection.Find.Execute
While Selection.Find.Found
[take some action]
Selection.Find.Execute
Wend

The first line just goes to the beginning of the document. I left that out and went to the beginning manually before starting the macro, and it worked perfectly.

Jazz
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top