Hi Eric:
On 2/9/06 1:12 PM, in article
(e-mail address removed), "Eric55"
Wow, many thanks! This was a much more helpful reponse than I had hoped for.
I have only a basic knowledge of programming, and virtually none in using
VBA, so I have just tried to put a few macros together by piecing together
and modifying things I see on-line, but with only a weak understanding of
what's going on!
That's the *approved* procedure for obtaining success in VBA
How do you
think *we* learned it. Oh, OK, there are those extremists who went out and
bought books on the subject and attended university and stuff. But they're
geeks...
Let me ask a follow-up question to help advance my understanding a bit.
Related to my problem of not executing the Find operation before testing the
Found variable: in the simplified code that you suggested at the end of your
post, where does the Find operation execute? The "execute" seems to be
associated with the Replace operation. Thanks for your help.
It's not, but you have not understood "dot-languages", "properties",
"inheritance", or "context".
You could thinks of the "dots" in the statement as "apostrophe esses". So
now we can read " Selection.Find.Execute " as "The Selection's Find's
Execute command".
VBA and AppleScript are both "object-oriented" languages. This means that
they are based upon "things" (objects). These objects are often physical
things we could see in a document if we looked (although they don't have to
be).
A document is an object, which contains objects, some of which contain
objects and so on. Like Chinese Eggs: big objects contain small objects
which contain smaller objects.
Objects have "properties" and "methods". Properties are like
"characteristics". My shirt is an object, it has a property of Size and a
property of Colour. The Colour property has a property of "Yellow" and (to
you) the size is unknown.
A "method" is like an "ability", literally "the ability to do something."
Let's look at the original statement:
Selection.Find.Execute Replace:=wdReplaceAll
To be strictly semantically correct, we should really write that as:
World.computer.OperatingSystem.Application.Documents.ActiveDocument.
Selection.Find.Execute
However, we can indulge in a little assumption in the interests of brevity.
We can assume that the whole world will not let us play with its computers,
so the computer in question can be implied to be "the computer upon which
our program is executing. Similarly, we can assume that there can be only
operating system controlling this computer.
"Application" is going to be tricky: there could be several. We should
really name it, but even that wouldn't do us much good because there may be
more than one copy running.
Instead, we rely on the rules of "Context". "Context" tells us to "use the
closest one we find to where we are standing." If we are standing in the
room in which I work, the context is "my office" and "the door" thus needs
to further information because there is only one door to my office. If we
were standing in the corridor, we would have to say "which" door.
Since, in VBA, we are running within the context of a single application, we
do not have to say which one we mean unless we mean one that is outside of
where we are running. So in this case, VBA knows that the "Application" in
question is "this copy of Word".
Now pay attention: this bit gets a little obtuse...
Word can have multiple documents open. So in VBA we need to say which
document we mean. However, because we're lazy, we can allow that to default
to "the active document" (which is ALWAYS the document containing the
selection). We are relying on the fact that Word can have only ONE
selection, and that the document that contains it must be the active
document, and that these two properties will default, so we do not have to
say "which" document or which selection because there can be only one
selection, and if we find that we automatically know which document we're
talking about.
The "Selection" is an object. It has a method: "Find" (it knows how to find
things). So once we have narrowed the context down to the selection, we can
then refer to "Find" unambiguously, because there can be only one selection
and thus only one Find.
So we're now several layers deep into this puzzle. However, because there
is no chance of ambiguity, we do not have to qualify anything yet. Because
there is no chance of any alternatives, we can use some smarts built in to
the VBA compiler to save ourselves a lot of typing.
The first thing we can do is explicitly nominate our context. Because our
whole code block refers to the same context, we can nominate it up front:
"With Selection.Find" All the following statements up to the "End With"
will now operate only within the explicit context described above -- the
selection in the active document.
So now we can remove the qualifying context from each statement: instead of
having to write "Selection.Find" in front of each statement, we can write it
once and have it apply to the entire block.
Once we do that, we can use a leading dot to IMPLY the context. That's what
I am doing when I write:
Do While .Found
.Execute Replace:=wdReplaceAll
Loop
The dot in front of the word .Found implies the current context so that bit
means "Do while Selection's Find's Found property (exists)." I could
equally have written "Do While Selection.Find.Found = True". But I know I
can rely on another behaviour of VBA: if I do not qualify a "While"
statement with an operator and an operand, the compiler's default is to
check for "True", and since I know the built-in control variable "Found" is
Boolean, I know it can only be one or the other, true or false.
The dot in front of Execute simply implies the context again: "Selection's
Find's Execute method".
There's nothing in the rules to say that an Object can not contain other
objects. So the Find object can (and does...) contain a Replacement object.
As soon as you name the Find object as being in your context, the compiler
expects you to do something with the Replacement object. We could, of
course, choose not to use it, but we don't.
Instead, we fire the "Execute" method of the Find object, and set its
"Replace" property to "wdReplaceAll" (change each instance that Find finds).
Now we have to avoid getting confused by the "replace all". It means
"replace all the instances that THIS iteration of the Find method found. As
we know, if you have four spaces, and you search for two spaces and change
to one, the first two spaces will be turned into one space, but then the
find will inspect the text FOLLOWING the replacement. It will change those
two into one also. But you will then be left with two spaces where you
previously had four. So we need to run the Find at least one more time to
find them.
In our case, we choose to keep running the Find command until it doesn't
find anything. Each time we run it, it will inspect all of the text in the
document (because we set Wrap to 'wdFindContinue'). When it doesn't find
anything, Find will return with the control variable Found set to False and
our macro then stops trying and exits.
So: the implied question is "should we use shorthand in code?" There is no
"correct" answer. Each individual case is an artistic, not scientific,
choice.
So long as our code is unambiguous, the compiler will produce exactly the
same binary each time. So the question becomes "who else needs to read this
text." If the answer is "Only Me", and you have a good memory, you can be
as cryptic as you like. If you have lots of experience with VBA and know
the language well, you can save yourself a great deal of typing and reading.
However, if someone else has to try to work out how to maintain the code
some years later when you're not around or can't remember how it works, they
will really thank you for letting your text run on a little, leaving a few
more words in there to make it self-explanatory.
Here's an example: I use this as an "exam" for job applicants. If someone
claims to be expert in Word VBA, I show them this and ask them what it does:
With myRange.Paragraphs
.Style = "Table Number"
With .First.Range.ListFormat
.ApplyListTemplate .ListTemplate, False
End With
With .Last
.KeepWithNext = False
.SpaceAfter = 4
End With
End With
There's a prize for the most correct guess. Those who've seen it before, be
quiet: let the new students have some fun!
Cheers
--Eric
:
Hi Erik:
You have a "First iteration" problem. *My* code is full of them
Do While Selection.Find.Found
Selection.Find.Execute Replace:=wdReplaceAll
Loop
This tests the control variable (Found) *before* you have executed the Find
statement. It will always be False... So your Execute will never be
executed.
You need to kludge it...
Selection.Find.Execute Replace:=wdReplaceAll
Do While Selection.Find.Found
Selection.Find.Execute Replace:=wdReplaceAll
Loop