Thrown against the wall (Will it stick?)

G

Greg Maxey

Hi All,

This is a bit long winded, so please stay with me. I
recently became interested in making the return key
funtion to advance between protected formfields. I found
KB article 211219 which offered a plausible solution.
However, the article specifically state that if other
bookmarks or if fillin disabled formfields were used that
additional code may be required.

Things continued to work reasonably well during testing
even after I inserted a few additional bookmarks and a few
fillin disabled formfields. The enter key would cause the
insertion point to jump to the next form field or if in
the last form field, it would jump back to the first
formfield. The only clich was that the first field
following any fillin disabled field would not
be "highlighted." The TAB key will cycle to and highlight
each field, the Enter key would cycle each field and and
highlight each field expect for fields following a fillin
disbled field.

I could figure out why these fields wouldn't highlight. I
have very limited VBA knowledge, but I tried everything I
could think of to reevaluate that field and expand the
selection over the field range. Nothing would work. I
even created another macro to run on entry to these
specific fields and then select it. I was really baffled
when the on entry macro had no effect but would work like
a charm if ran manually. Finally it dawne on me that it
could be a timing issue.

I had previosly stashed away a snippet of code for causing
a macor to "doze." for a bit. I think it was either Peter
Hewett that posted it here earlier. I applied his
Declaration statement, the Doze macro and called it in my
code below. It appears a simple 1 ms pause was all I
needed to identify the current field and select it. Now
all formfields are selected and highlighted regardless of
their location or the presence of fillin disabled fields.

Here is my code. Thrown against the wall for your
critical comments or suggestions for improvement. Thanks.

Sub EnterKeyMacro()
' Include the following declaration in the Macro module:
'Private Declare Sub Sleep Lib "kernel32" (ByVal
dwMilliseconds As Long)

Dim myFF, oFrm, oSelBkm

Set oFrm = ActiveDocument.FormFields
Set oSelBkm = Selection.Bookmarks

If ActiveDocument.ProtectionType = wdAllowOnlyFormFields
And _
Selection.Sections(1).ProtectedForForms = True Then
myFF = oSelBkm(1).Name

If oFrm(myFF).Name <> oFrm(oFrm.Count).Name Then
oFrm(myFF).Next.Select
'I added these three lines.
Doze 1
myFF = oSelBkm(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
Else
oFrm(1).Select
End If
Else
Selection.TypeText Chr(13)
End If

End Sub

Sub Doze(ByVal lngPeriod As Long)

DoEvents
Sleep lngPeriod
' Call it in desired location to sleep for 1 second like
this:
' Doze 1000
End Sub
 
W

Word Heretic

G'day "Greg Maxey" <[email protected]>,

As coded, Doze is pointless, just call sleep directly where you have
doze. You should also type your variables.

Yeah, I often get strange problems whenever there is a bookmark
involved, that are fixed through OnTime's or Sleeping. Word needs to
play catchup with its dynamic collections more often than would seem
on the surface.

Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg Maxey reckoned:
 
G

Greg

Steve,

Thanks. I cleaned up the sleep/doze issue.

What do you mean when you say, "You should also type your
varialbles."?

Do you mean, Dim myFF AS ...? I normally do, but in this
case I don't know what the AS should be. I guess that I
was just lucky and got by with it :)
 
W

Word Heretic

G'day "Greg" <[email protected]>,

Correct,

Dim myFF as String

Dim oFrm as FormFields (not that I would even bother with this, I'd
just ref the collection direct)

Dim oSelBkm as Bookmarks (ditto)


, oFrm, oSelBkm

Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg reckoned:
 
W

Word Heretic

G'day "Greg Maxey" <[email protected]>,

You are displaying a std novice problem with understanding object
usage. I will try to tersely explain.

There is only one of each of these objects:

Selection (although there are as many Document.Selections as there are
Documents)

ActiveDocument.Bookmarks collection
Application.Documents collection

and so on.

So it is pointless assigning an object to them, all this does is give
you a coders shorthand for that object whilst making your code more
unreadable.

Now, inside a document, there are MANY MANY ranges and paragraphs, so
there is some call for saying ThisPara = this and ThatPara = that and
referring to both of them. These point to a single instance of
multiple objects that you are interested in.


So thus, a std piece of code would look like

Dim MyBookMark as Bookmark

For each MyBookMark in ActiveDocument.Bookmarks

There is no point assigning ActiveDocument.Bookmarks, so we don't.


Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg Maxey reckoned:
 
G

Greg Maxey

Steve.

I am a novice. I thought I followed you up to this point. Now I don't.
Thanks for trying though. Perhaps with time and after I see enough examples
of completed macros coded in the manner you describe then the idea will
click. Right now, I just can't connect the dots and follow your track in the
specific macro at hand.
 
W

Word Heretic

G'day "Greg Maxey" <[email protected]>,

Ok, to draw it specifically to your macro, see how you used Selection?
Why didn't you Dim Sel and Set Sel = Selection? Pointless right? Same
as Dim Ad and Set Ad = ActiveDocument

Yet you did exactly this with ActiveDocument.FormFields and
Selection.Bookmarks. They are single instanced objects, in both these
specific cases they are a COLLECTION, which is never-the-less an
OBJECT.

How many Selection.Bookmarks collections can we have? 1.

How many collections of Formfields can our ActiveDoc have? 1

So when I read your code, I go OFrm(what the?) - hang on, did I miss
an array definition somewhere. Whereas if I was to read
ActiveDoctor.FormFields I would know it was time to take my pills
again without having to be told :)


Sub EnterKeyMacro()
' Include the following declaration in the Macro module:
'Private Declare Sub Sleep Lib "kernel32" (ByVal
dwMilliseconds As Long)

Dim myFF, oFrm, oSelBkm

Set oFrm = ActiveDocument.FormFields
Set oSelBkm = Selection.Bookmarks

If ActiveDocument.ProtectionType = wdAllowOnlyFormFields
And _
Selection.Sections(1).ProtectedForForms = True Then
myFF = oSelBkm(1).Name

If oFrm(myFF).Name <> oFrm(oFrm.Count).Name Then
oFrm(myFF).Next.Select
'I added these three lines.
Doze 1
myFF = oSelBkm(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
Else
oFrm(1).Select
End If
Else
Selection.TypeText Chr(13)
End If

End Sub

Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg Maxey reckoned:
 
G

Greg Maxey

Steve,

I am a Sailor. Some would probably say a dumb sailor. If I ever met you, I
would probably like you and get on with you just fine. However, when it
comes to macros, you are a Greek. Or at least you speak Greek. What you
say here almost makes sense, but not quite. Obviously I have done something
that didn't need to be done and I will go back and try to figure it out.
Still, I am about ready to call "Uncle" man.

Thanks for the tease.
 
G

Greg Maxey

Steve,

I used oFrm to keep from having to type out ActiveDocument.Formfields all
over the place. I dont' know what the crime is in that :)
 
W

Word Heretic

G'day "Greg Maxey" <[email protected]>,

Yes, I am being fairly pedantic - but that is what the difference is
between clean, optimised code and hacked macros. It's no such much a
crime as a waste of object usage whilst reducing code clarity.

The main reason I am concerned is that I don't want you to end up
thinking of object declaration merely as convenient shortcuts to
existing objects.

Additionally, that extra pointer lookup (via your dummy object) slows
your code execution down marginally.

If you use the autocomplete it is lot easier to type out the big
stuff. Using your untyped variables you wont get this effect, but for
example, if your VBE options have autocompleting turned on,

Type ActiveD and press ctrl+space, a dot, FOR then ctrl+space again.

As objects are tokenised by object id, by using
ActiveDocument.Formfields instead of oFrm we achieve all of the
following:

* Smaller code size (no dim or set)
* Smaller memory footprint (no extra variables)
* Faster code execution (direct object lookup)

This HAS to be a good thing when repeated many times in a single code
module.

Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg Maxey reckoned:
 
G

Greg Maxey

Steve,

Sorry for the comment about your language. You were correct in your
assessment that I did what I did as a method of shorthand. Is this
correct?:

Sub EnterKeyMacro()
' Include the following declaration in the Macro module:
'Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Dim myFF As String

If ActiveDocument.ProtectionType = wdAllowOnlyFormFields And _
Selection.Sections(1).ProtectedForForms = True Then
myFF = Selection.Bookmarks(1).Name

If ActiveDocument.FormFields(myFF).Name <> ActiveDocument.FormFields _
(ActiveDocument.FormFields.Count).Name Then
ActiveDocument.FormFields(myFF).Next.Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
Else
ActiveDocument.FormFields(1).Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
End If
Else
Selection.TypeText Chr(13)
End If

End Sub
 
W

Word Heretic

G'day "Greg Maxey" <[email protected]>,

Getting much bettererer! Now we need to use With's to reduce object
instancing. First through, another classic beginner's mistake:

BAD:

If Something = True then

GOOD:

If Something then

Sub EnterKeyMacro()
' Include the following declaration in the Macro module:
'Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Dim myFF As String

If ActiveDocument.ProtectionType = wdAllowOnlyFormFields And _
Selection.Sections(1).ProtectedForForms Then
myFF = Selection.Bookmarks(1).Name

with activedocument.formfields
If .Item(myFF).Name said:
.item(myFF).Next.Select
Sleep 2

'Are these next two lines really necc? Isnt this done just above
sleep? Try commenting them out for a test
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
Else
.Item(1).Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
End If
end with
Else
Selection.TypeText Chr(13)
End If

End Sub


Now, we start seeing that the stuff in the the inner IF block is a bit
repetitive. I want you to work out how to set a variable inside that
IF block and use it to perform the required actions. There is an
elegant way to do it, hopefully you can find it.

Post back your next version and if you haven't worked it out I'll show
you :)

Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg Maxey reckoned:
 
J

Jonathan West

Word Heretic said:
G'day "Greg Maxey" <[email protected]>,

Getting much bettererer! Now we need to use With's to reduce object
instancing. First through, another classic beginner's mistake:

BAD:

If Something = True then

GOOD:

If Something then

Steve,

People will generally find it much more helpful if you avoid ex cathedra
statements like that, and explain *why* you think that one way of writing
code is better than another.

For instance, it would be useful to explain that the line following an
If-Then statement is executed if the expression in the statement evaluates
to True, and if it is already boolean, comparing True with True simply takes
extra time.

But comparing a non-boolean value to True doesn't necessarily give the same
results as an implicit or explicit conversion to boolean.

(The next bit is more for Greg's benefit, as I'm sure you realise this
already)
For instance, run the following macro

Sub TestTruth()
Dim Something As Integer
Something = 3

If Something = True then
MsgBox "'Something = True' is True"
Else
MsgBox "'Something = True' is False"
End If

If Something = True then
MsgBox "'Something' is True"
Else
MsgBox "'Something' is False"
End If

End Sub

Then try running that macro again with Something set to -1.
 
G

Greg

Steve,

Sorry but it escapes me. I read Jonathan's posting as
well and thought the elusive varialbe might be boolean,
but I can find a way to apply it.

Here is my latest code:

Sub EnterKeyMacro()
' Include the following declaration in the Macro module:
'Private Declare Sub Sleep Lib "kernel32" (ByVal
dwMilliseconds As Long)

Dim myFF As String

If ActiveDocument.ProtectionType = wdAllowOnlyFormFields
And _
Selection.Sections(1).ProtectedForForms Then
myFF = Selection.Bookmarks(1).Name

With ActiveDocument.FormFields
'Item fills in as ActiveDocument.Formfields
If LastFF Then
If .Item(myFF).Name <> .Item(.Count).Name Then
.Item(myFF).Next.Select
Sleep 2

'Are these next two lines really necc? I think so. It is
the only
'way I can get fields following any field that is not
fillin enabled
'to be highlighted.

myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
Else
.Item(1).Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
End If
End With

Else
Selection.TypeText Chr(13)
End If
End Sub
 
G

Greg

Steve,

I may be completely off mark here, but from what your said
earlier and from reading Jonathan's post, I suspected I
needed to put a Boolean expression in somewhere. Well
here it is, but I don't see how it improves matters much.
That is my best shot.
Thanks

Sub EnterKeyMacro()
' Include the following declaration in the Macro module:
'Private Declare Sub Sleep Lib "kernel32" (ByVal
dwMilliseconds As Long)

Dim myFF As String
Dim NotLastFF As Boolean

If ActiveDocument.ProtectionType = wdAllowOnlyFormFields
And _
Selection.Sections(1).ProtectedForForms Then
myFF = Selection.Bookmarks(1).Name

With ActiveDocument.FormFields
'Item fills in as ActiveDocument.Formfields
NotLastFF = .Item(myFF).Name <> .Item(.Count).Name
If NotLastFF Then
' If .Item(myFF).Name <> .Item(.Count).Name Then
.Item(myFF).Next.Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
Else
.Item(1).Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF
End If
End With

Else
Selection.TypeText Chr(13)
End If
End Sub
 
W

Word Heretic

G'day "Greg" <[email protected]>,

Ok, lets compare the two parts of the if
' If .Item(myFF).Name <> .Item(.Count).Name Then

PART ONE
.Item(myFF).Next.Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF

PART TWO
.Item(1).Select
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF

They look very similar. Matter of fact, the last three lines of each
are IDENTICAL!

So we can write this as

If .Item(myFF).Name = .Item(.Count).Name Then
.item(1).select
else
.Item(myFF).Next.Select
end if
Sleep 2
myFF = Selection.Bookmarks(1).Name
Selection.GoTo What:=wdGoToBookmark, Name:=myFF

No, you don't need that boolean variable. We are using the publicly
available Selection object as our variable. After we select, it now
holds the value we are interested in. So we apply the generic code to
the selection. These last three lines were repeated in both parts of
the if block. Now they are stated once outside the if block. This way
it reads much clearer, yes?

Now, there is also the issue of
Selection.TypeText Chr(13)

Always used the typed function where possible for speed: Chr$(13)
returns a string, Chr(13) returns a variant with the contents of a
string that needs to be validated as a string when assigning it to a
string variable.

Surely we must be getting close now :)


Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg reckoned:
 
W

Word Heretic

G'day "Jonathan West said:
But comparing a non-boolean value to True doesn't necessarily give the same
results as an implicit or explicit conversion to boolean.

Absolutely, and unfort MS lists that property as boolean. Yes, ex
cathedra's not really suitable, but I'm already bending over backwards
to help Greg improve his coding so I'm sure he'll forgive me this
time! Sorry Greg, and nice explanation too Johnathon :)

Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Jonathan West reckoned:
 
G

Greg Maxey

Steve,

Sorry for taking so long to get back to you. I see you posted last night.
For some reason my news server is not very responisve of late. I checked
this morning an your reply had not posted and the reply still doesn't appear
on the Microsoft Pulic Newsgroup page either.

Well. You tried to teach me to fish and ended up giving me the fish anyway.
Still I learned a bit and hopefully part of it will stick.

Thanks for sticking with this one.
 
W

Word Heretic

G'day "Greg Maxey" <[email protected]>,

So long as you learned something, that's all that matters. Next
project you will have a few more tricks up your sleeve :) Until then!

Steve Hudson - Word Heretic
Want a hyperlinked index? S/W R&D? See WordHeretic.com

steve from wordheretic.com (Email replies require payment)


Greg Maxey reckoned:
 

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