MsgBox showing number of replacements for all affected story ranges, not just the main story

A

andreas

Hello everybody,

I recently wrote a macro that indicates the number of replacements of
a search and replace operation in a MsgBox.

It is working fine, but the trouble is that it covers only the number
of replacements of the main story range. Is it possible for somebody
to re-write the code to include ALL THE STORY RANGES of the document,
so that the MsgBox shows the TOTAL NUMBER OF REPLACEMENTS OF ALL STORY
RANGES?
I tried it several times, but to no avail.
Help is much appreciated. Thank you very much in advance. Regards,
Andreas


Sub ReplacementOperatonWithMsgBoxShowingNumberOfReplacements
Dim rng As Word.Range
Dim bfound As Boolean, i As Long
'start counter at -1
i = -1
Do

Set rng = ActiveDocument.Range

With rng.Find
.ClearFormatting
.Replacement.ClearFormatting
.MatchWildcards = True
'Check if word is found
bfound = .Execute(findtext:="find", replaceWith:="lost",
Replace:=wdReplaceOne)
'increment counter
i = i + 1
End With
Loop While bfound
MsgBox CStr(i) & " replacements made."

Set rng = Nothing
End Sub
 
B

Bear

Adreas and Shauna:

This example illustrates two of the most baffling features of VBA.

First, the example code Shauna refers to says:

myStoryRange.Find.Execute FindText:="Microsoft Word", Forward:=True
While myStoryRange.Find.Found
myStoryRange.Italic = True
myStoryRange.Find.Execute _
FindText:="Microsoft Word", Forward:=True
Wend

Before executing the first line of this code, myStoryRange is the first
story range in the document, say the main body story.

When there's a hit, after executing the first line myStoryRange becomes the
found text. So for the second and third lines, myStoryRange is the found text.

Now in the fourth line, without any action on our part, myStoryRange
suddenly no longer means the found text, but the original range -- or maybe
the original range with the start collpsed to the end of the found text. WHY?

Second, I know that code like:

For Each myStoryRange in ActiveDocument.StoryRanges
myStoryRange processing
Next myStoryRange

....will NOT work through every story range, and that the odd:

While Not (myStoryRange.NextStoryRange Is Nothing)

....is needed to progress through the collection. WHY?

I've accepted both behaviors, but I'd really welcome any insight that helps
me understand, rather than simply accept.

Bear
 
S

Shauna Kelly

Hi Bear

Good questions! And the answers, in the end are "because someone at
Microsoft just did it that way".

QUESTION 1

There are lots of different ways of laying out code, especially code to find
text in a Word document. So I've re-arranged this a bit to make it easier to
see what's going on. And I've added voluminous comments. Sadly, Microsoft's
example code never explicitly establishes its variables. It's worth knowing
that the myStoryRange in the following is a Range object. One really should
create the variable explicitly, as I've done here.

Sub FindStuff

'Declare our variable
Dim myStoryRange As Range

'Before the next bit of code would work, we have to set the myStoryRange to
something
'(In the original, it cycles through all the StoryRanges)
Set myStoryRange = ActiveDocument.StoryRanges.Item(wdMainTextStory)

'Use myStoryRange (which is the main text story) and do stuff with Finding
With myStoryRange.Find
'Identify what to find
.Text = "Microsoft Word"

'Identify whether to search top-to-bottom or bottom-to-top
.Forward = True

'Tell Word what to do when it's finished searching. Specifically:
'(from the help file) what happens if the search begins at a point
'other than the beginning of the document and the end of the document
'is reached (or vice versa if Forward is set to False) or if the search
'text isn't found in the specified selection or range.
'
'The wdFindContinue setting is the default. So in Microsoft's code
'example they could omit it. That's fine for computers, but we
'humans might like to know what's going on.
'
'The .Wrap property works like the prompt you get in the UI if
'you select a range and do a search-and-replace within the range.
'Word asks if you want to continue searching in the rest of the
document.
'
'For what it's worth, if you want to restrict your search to a specific
'range, choose .Wrap = wdFindStop.
'
'So, we tell Word to keep searching after this Find.
'
.Wrap = wdFindContinue

'Go see if you can find anything
.Execute

'If Word found the text, then the myStoryRange object (which is
'just a Range object) is now re-assigned to the range where
'Word found the text it was looking for.
'
'However, if Word did not find the text, then myStoryRange
'is the same as it ever was.
'
'That's just how it works. The VBA help file explains much of this.

'We haven't yet looked to see if Word found the text.
'We can detect that using myStoryRange.Find.Found.
'If that is true, then Word found our text.

'If Word reported that it found something, then do this stuff.
While .Found
'If we found our text, then myStoryRange has been re-assigned
'to the text we found. Make it Italic.
myStoryRange.Italic = True

'Quick reminder:
'The .Forward setting is still True.
'The .Wrap setting is still wdFindContinue
'So, if we haven't hit the end of the document, Word will
'keep looking in the document.

'Go see if you can find another instance of our text
.Execute

'And if Word finds another instance of our text, then
'.Found will be true
'and myStoryRange will have been re-assigned to the
'newly-found text.

'So, if .Found is true, we'll loop here.
'If .Found is false, Word will kick us out of the loop
'and we can go home.

Wend


End With

EndSub


QUESTION 2

Here's an illustrated version of code to cycle through StoryRanges. I've
used a separate variable here for the 'inner' story ranges, where Microsoft
only uses one. And I've tried to make some things more explicit - it's not
necessarily good programming practice, it's just designed for illustration.
And I will count myself as having succeeded if, after working through this,
you no longer think that "While Not (myStoryRange.NextStoryRange Is
Nothing)" is Odd<g>.

Sub CycleThroughStoryRanges()

Dim myStoryRange As Range
Dim mySubRange As Range

'Go through every StoryRange (there are exactly 17 of them
'in Word 2003)
For Each myStoryRange In ActiveDocument.StoryRanges
'Do some stuff to this myStoryRange object
If myStoryRange.Paragraphs.Count > 0 Then
MsgBox myStoryRange.Paragraphs(1).Range.Text
End If


'But some stories have 'followers'. Eg if the document has more than
'one section, there may be many even page headers in the document.
'And we want to cycle through them all.

'Trap an error if the .NextStoryRange doesn't exist.
'For example, if myStoryRange is the even headers story, then there
might
'not be a Next even header. And the VBA Help for NextStoryRange explains
'that when myStoryRange is (eg) the footnotes story, then
..NextStoryRagne
'will always return Nothing.
On Error Resume Next
Set mySubRange = myStoryRange.NextStoryRange
On Error GoTo 0

If mySubRange Is Nothing Then
'There is no 'next' story - so if myStoryRange were, say,
'an even pages header, then there is no next even pages
'header. Or maybe, eg, myStoryRange is the footnotes, and
'it has no .NextStoryRange. So we can give up and go on.
Else
'We're here because mySubRange is *not* a Nothing

'While we continue to have more than Nothing, do this stuff
While Not (mySubRange Is Nothing)
'Do some stuff to this 'follower' range
If myStoryRange.Paragraphs.Count > 0 Then
MsgBox mySubRange.Paragraphs(1).Range.Text
End If

'See if we can find yet another 'follower' range
'(eg the next even header)
On Error Resume Next
Set mySubRange = mySubRange.NextStoryRange
On Error GoTo 0

'If we've found another 'follower' range (eg another
'even pages header), then we'll loop here.
'Otherwise, Word will kick us out of this inner loop
'and we'll go on to the next (top-level) myStoryRange.

Wend
End If
Next myStoryRange

End Sub
 
B

Bear

Shauna:

Thanks for your efforts. I'm not sure you've done more than beautifully
illuminate the odd nature of the DOM and VBA's interaction with it. But I'll
reread your code a few times to make sure I'm getting your points.

I DID get a new perspective on story ranges. Tell me if any of this is
wrong...

When you say of story ranges "there are exactly 17 of them in Word 2003" I
think you mean to say that there are 17 types of story range. The might be
any number of story ranges in a given document?

For whatever reason, "For Eaching" through the story range collection only
yields the first story range of each type, so the "inner" loop with the test
for Is Nothing is needed to process subsequent story ranges within a given
type.

Bear
 
S

Shauna Kelly

Hi

You have it exactly right--at least as I understand it.

You can see the 17 types if you look in VBA help under the Item Method for
the StoryRanges object. That lists 11 of them. But if you type
ActiveDocument.StoryRanges.Item( into the VBE, the intellisense lists 17 of
them. And the object browser lists 17.

I think that the apparently convoluted nature of the StoryRanges is because
(for example) a document has exactly one main story, it might or might not
have comments, and it might have any number of different headers and
footers.

Hope this helps.

Shauna Kelly. Microsoft MVP.
http://www.shaunakelly.com/word
 
A

andreas

Hi

You have it exactly right--at least as I understand it.

You can see the 17 types if you look in VBA help under the Item Method for
the StoryRanges object. That lists 11 of them. But if you type
ActiveDocument.StoryRanges.Item( into the VBE, the intellisense lists 17 of
them. And the object browser lists 17.

I think that the apparently convoluted nature of the StoryRanges is because
(for example) a document has exactly one main story, it might or might not
have comments, and it might have any number of different headers and
footers.

Hope this helps.

Shauna Kelly. Microsoft MVP.http://www.shaunakelly.com/word











- Zitierten Text anzeigen -

Shauna and Bear,

great comments from both of you. Very helpful. Thank you so much.

Regards,

Andreas
 

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