Form Fields

B

Bob

I have created a MS Word Form that I want people to use
when placing an order. Is there a way that I can make
several of the form fields mandatory to fill out?

Thanks
 
G

Graham Mayor

You can test for content using a pair of macros run on exit from the field.
You'll need a similar pair of macros macro for each field you wish to test.
The following tests for content in the first field, which by default is
called Text1. Change Text1 for the name of the form field bookmark you wish
to test and the text prompt for whatever you want to say. Save the macros in
the document template - not normal.dot. The main macro uses the help
assistant to pop up the message. You could adapt to use a message box if you
prefer.

Sub ExitText1()
With ActiveDocument.FormFields("Text1")
If Len(.Result) = 0 Then
Beep
Application.OnTime When:=Now + TimeValue("00:00:01"),
Name:="GoBacktoText1"
Set Balloon = Assistant.NewBalloon
With Balloon
.Text = "No Data!" & vbCr & "You must fill in this
formfield"
.Button = msoButtonSetOK
.Animation = msoAnimationBeginSpeaking
.Show
End With
End If
End With
End Sub
Sub GoBacktoText1()
ActiveDocument.Bookmarks("Text1").Range.Fields(1).Result.Select
End Sub

See http://www.gmayor.com/installing_macro.htm

--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 
C

Charles Kenyon

Hi Graham,

I tried to adapt your procedures to a generic field checker and failed. The
code:

Sub ExitField()
Dim strName As String
Dim Balloon As Balloon
If Selection.FormFields.Count = 1 Then
'No textbox but a check- or listbox
strName = Selection.FormFields(1).Name
ElseIf Selection.FormFields.Count = 0 And Selection.Bookmarks.Count > 0
Then
strName = Selection.Bookmarks(Selection.Bookmarks.Count).Name
End If
With ActiveDocument.FormFields(strName)
If Len(.Result) = 0 Then
Beep
Application.OnTime When:=Now + TimeValue("00:00:11"), _
Name:="GoBacktoField(" & strName & ")"
Set Balloon = Assistant.NewBalloon
With Balloon
.Text = "No Data!" & vbCr & "You must fill in this FormField
"
.Button = msoButtonSetOK
.Animation = msoAnimationBeginSpeaking
.Show
End With
End If
End With
End Sub
Sub GoBacktoField(strName As String)
ActiveDocument.Bookmarks(strName).Range.Fields(1).Result.Select
End Sub

I also tried it with a direct command to select the field rather than
calling a subroutine.

In both cases it failed when used as an on-exit macro but worked fine when
run from the VBE or stepped-through. The failure was that even though the
message displayed, the selection was the next field rather than the one that
had been checked.

I've looked at http://word.mvps.org/FAQs/TblsFldsFms/ValidateFFields.htm
and http://word.mvps.org/FAQs/TblsFldsFms/GetCurFmFldName.htm.

This is the first time I've looked at the OnTime function and I'm not sure
that it allows the passing of parameters.

--

Charles Kenyon

See the MVP FAQ: <URL: http://www.mvps.org/word/> which is awesome!
--------- --------- --------- --------- --------- ---------
This message is posted to a newsgroup. Please post replies
and questions to the newsgroup so that others can learn
from my ignorance and your wisdom.
 
G

Graham Mayor

I find that too :(
It needs a little more investigation and some clever heads than mine I
guess.
I'll play around with it.

--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 
G

Graham Mayor

I have crossposted this from docmanagement to see if any of you vba experts
can throw some light on what is going on here. The line
Name:="GoBacktoField(" & strName & ")" doesn't look right to me, but even
when changed the line is not carried to the second macro and instead of
focus falling back to the same field as would be the case in the original
hard coded example, the next field is selected. Anyone any ideas how to
handle this?

Please maintain the crosspost for the benefit of the thread on
docmanagement.

--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 
P

Peter Hewett

Hi Graham

In short you can't prevent a "tab-out" of a FormField from moving to the
next FormField. You can't stop Word moving you to the next FF by doing
anything on the OnExit macro! However, you can work around it. Here's the
code and I'll explain the methodology:

Public Sub AOnExit()
ActiveDocument.Variables("ForceFF").Value = "Text1"
End Sub

Public Sub AOnEntry()
Dim strCurrentFF As String

' MsgBox GetCurrentFF.Name

strCurrentFF = ActiveDocument.Variables("ForceFF").Value
If LenB(Trim$(strCurrentFF)) > 0 Then
ActiveDocument.Variables("ForceFF").Value = " "
GotoFF strCurrentFF
End If
End Sub

Private Sub GotoFF(ByVal strFFName As String)
Dim fldTemp As Word.Field
Dim rngBM As Word.Range
Dim rngField As Word.Range
Dim strOnEntry As String

With ActiveDocument

' This is really the Form Field we want
Set rngBM = .Bookmarks(strFFName).Range
For Each fldTemp In .Fields

' Some fields are collapsed so adjust start to ensure
' the field falls within the bookmarks range
Set rngField = fldTemp.Result
If rngField.Start = rngField.End Then
rngField.Start = rngField.Start - 1
End If

' Selection appropriate to Form Field type
If rngField.InRange(rngBM) Then
If fldTemp.Type = wdFieldFormTextInput Then
fldTemp.Result.Select
Else
fldTemp.Select
End If

' Run the on entry macro if present
strOnEntry = .FormFields(strFFName).EntryMacro
If LenB(strOnEntry) > 0 Then
Application.Run strOnEntry
End If
Exit Sub
End If
Next
End With
End Sub

Ok for a simple test create a document with 3 text FormFields, Text1,
Text2, Text3. Set all FFs to use the "AOnEntry" OnEntry macro. Set Text1 to
use the "AOnExit" OnExit macro.

Once you've protected the document for Forms, it looks like you can't tab
out of the Text1 FF. Of course you can display the appropriate message, so
that the user knows why they're "stuck" in a field, but I've left that bit
out.

The mechanism is simple the OnExit macro loads a Document Variable with
it's own name. The OnEntry macro checks to see if the DV is not blank, if
it's not blank it then GoTos that FF! So that Word does not delete the DV
by assigning a nullstring to it, assign a space to it. This keeps the code
simple by not having to handle a non existent DV.

If you want to display messages set another DV to the message in the OnExit
macro and display it in the OnEntry macro.

HTH + Cheers - Peter
 
G

Graham Mayor

Peter

That looks a tad complicated for an immediate reply :) I'll have a play
around and see where I get to. Thanks very much for supplying the code.

The puzzle arose because the original code, naming the field, does exactly
what I wanted, but doesn't when you thrown in the variable to allow the same
code to be used on all fields. It was this part I (and Charles) couldn't
figure out.


--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 
P

Peter Hewett

Hi Graham

I REALLY don't like the timer method as it just does not work reliably. If
you tab through another field that uses the OnTime in its exit macro it
cancels the first one, or if code is still running at the elapsed time the
macro does not fire!

Anyway, you can't pass a parameter to a macro you're running using
"OnTime". You can resolve your problem by using a Private scope variable in
the module containing all this code. Here's a trivial example:

Private mstrValue As String

Public Sub AOnExit()
mstrValue = GetCurrentFF.Name
Application.OnTime Now + TimeValue("00:00:01"), "TimerTest"
End Sub

Public Sub TimerTest()
MsgBox "The ForField you left was" & vbCr & mstrValue
End Sub

Private Function GetCurrentFF() As Word.FormField
Dim rngFF As Word.Range

Set rngFF = Selection.Range
rngFF.Expand wdParagraph
Set GetCurrentFF = rngFF.FormFields(1)
End Function

If you attach the "AOnExit" procedure to any field as its OnExit macro, the
TimerText will display the name of the FF you last exited!


Here's a simplified version of my previous post, just attach the on entry
macro to ALL FormFields an the OnExit macro to any FormField you don't want
to be "blank". It's all the code you need plus it's more reliable than
OnTime!!!

Private mstrFF As String

Public Sub AOnExit()
With GetCurrentFF
If Len(.Result) = 0 Then
MsgBox "You can't leave field: " & .Name & " blank"
mstrFF = GetCurrentFF.Name
End If
End With
End Sub

Public Sub AOnEntry()
Dim strCurrentFF As String

' Goto another FormField if we weren't supposed to leave it!
If LenB(mstrFF) > 0 Then
ActiveDocument.FormFields(mstrFF).Select
mstrFF = vbNullString
End If
End Sub

Private Function GetCurrentFF() As Word.FormField
Dim rngFF As Word.Range
Dim fldFF As Word.FormField

Set rngFF = Selection.Range
rngFF.Expand wdParagraph

' You can't reliably use "rngFF.FormFields(1)" if you
' hit enter and tab out of the FF the code fails
For Each fldFF In rngFF.FormFields
Set GetCurrentFF = fldFF
Exit For
Next
End Function

HTH + Cheers - Peter
 
C

Charles Kenyon

I was wondering where those document variables were coming into the picture.
Now I understand, I think. I'll work on an update and post it soon because I
think a generic on-exit macro would be useful, although not a cure-all.
--

Charles Kenyon

See the MVP FAQ: <URL: http://www.mvps.org/word/> which is awesome!
--------- --------- --------- --------- --------- ---------
This message is posted to a newsgroup. Please post replies
and questions to the newsgroup so that others can learn
from my ignorance and your wisdom.
 
C

Charles Kenyon

Sorry, I didn't read far enough. You've done the work for me. Thank you!
--

Charles Kenyon

See the MVP FAQ: <URL: http://www.mvps.org/word/> which is awesome!
--------- --------- --------- --------- --------- ---------
This message is posted to a newsgroup. Please post replies
and questions to the newsgroup so that others can learn
from my ignorance and your wisdom.
 
C

Charles Kenyon

I tested it and ran into problems when first running it. It skipped a field
on me. Once I had been through all fields, though. It worked fine then. I
assume it requires the OnEntryA macro to initialize the appropriate
variables.

Anyway, it still doesn't cure the problem of the person who simply saves /
exits from a form without filling in a field, but it is a step in the right
direction. If I really need this, I will write some intercepts for Save and
SaveAs to catch those. I don't really care about the user who exits without
saving.
--

Charles Kenyon

See the MVP FAQ: <URL: http://www.mvps.org/word/> which is awesome!
--------- --------- --------- --------- --------- ---------
This message is posted to a newsgroup. Please post replies
and questions to the newsgroup so that others can learn
from my ignorance and your wisdom.
 
P

Peter Hewett

Hi Charles

I don't see why it should skip a FF on you? ALL FF's need the OnEntry macro
but only those you want validated need the OnExit macro. As soon as you
leave a FF it set the Private variable mstrFF based on the contents of the
current FF. So when you move to the next FF and fire its OnEntry macro the
variables already set.

No, you're right the OnExit macro does not fire when you close or save your
document. To do what you want you have to use apllication events. The
Document_Save event is no good to you as you can't cancel it if the current
FF is invalid. As you know you can hook the DocumentBeforeSave event. You
can then run a validation procedure. You can't use the OnExit macros
because there is no way of stoping the document from being saved! What you
need is separate validation function. Here's how you would might set up the
code in the ThisDocument module:


Public WithEvents mappWord As Word.Application
Private mstrFF As String

Private Sub Document_New()
' Hook the event handler
Set mappWord = Word.Application
End Sub

Private Sub Document_Open()
' Hook the event handler
Set mappWord = Word.Application
End Sub

Private Sub mappWord_DocumentBeforeClose(ByVal Doc As Document, Cancel As
Boolean)
Cancel = Not ValidateAllFFs
End Sub

Private Function GetCurrentFF() As Word.FormField
Dim rngFF As Word.Range
Dim fldFF As Word.FormField

Set rngFF = Selection.Range
rngFF.Expand wdParagraph

' You can't reliably use "rngFF.FormFields(1)" if you
' hit enter and tab out of the FF the code fails!!
For Each fldFF In rngFF.FormFields
Set GetCurrentFF = fldFF
Exit For
Next
End Function

Private Function ValidateAllFFs() As Boolean

If LenB(GetFF("Text1").Result) = 0 Then
MsgBox "Cant leave field: Text1 blank"
ElseIf LenB(GetFF("Text2").Result) = 0 Then
MsgBox "Cant leave field: Text2 blank"
ElseIf LenB(GetFF("Text3").Result) = 0 Then
MsgBox "Cant leave field: Text3 blank"
Else
ValidateAllFFs = True
End If
End Function

Private Function GetFF(ByVal strFFName As String) As Word.FormField
ActiveDocument.FormFields(strFFName).Select
Set GetFF = GetCurrentFF
End Function

Public Sub AOnExit()
With GetCurrentFF
If Len(.Result) = 0 Then
MsgBox "You cant leave field: " & .Name & " blank"
mstrFF = .Name
End If
End With
End Sub

Public Sub AOnEntry()
Dim strCurrentFF As String

If LenB(mstrFF) > 0 Then
ActiveDocument.FormFields(mstrFF).Select
mstrFF = vbNullString
End If
End Sub


HTH + Cheers - Peter
 
P

Peter Hewett

Hi Charles

Hooked the wrong event, I should have choosen:
Private Sub mappWord_DocumentBeforeSave(ByVal Doc As Document, _
SaveAsUI As Boolean, Cancel As Boolean)
Cancel = Not ValidateAllFFs
End Sub

and not:
mappWord_DocumentBeforeClose

Cheers - Peter
 
G

Graham Mayor

Peter
I have now had the opportunity to play around with the code you posted, but
following your instructions, I cannot get it work for me. Whether or not the
first formfield has content, the cursor always returns to that field.

Of the three batches of code, only the original one, which calls the
bookmarked fields by name responds correctly, thus the following works for
the same three fields Text1-3. Unfortunately this means you have to
duplicate the code for each field, which produces a lot of code for a form
with lots of fields, and plenty of opportunities to screw up the field names
when anything more complicated than Text1-3 is desired, hence the desire to
simplify it. Your own code also requires separate (albeit shorter) onexit
macros for each field.

I have changed the messages to use MsgBox rather than the help assistant for
the sake of clarity. Any further insights you have would be appreciated.

Sub ExitText1()
With ActiveDocument.FormFields("Text1")
If Len(.Result) = 0 Then
Beep
Application.OnTime When:=Now + TimeValue("00:00:01"),
Name:="GotoText1"
MsgBox ("Field must be completed")
End If
End With
End Sub
Sub ExitText2()
With ActiveDocument.FormFields("Text2")
If Len(.Result) = 0 Then
Beep
Application.OnTime When:=Now + TimeValue("00:00:01"),
Name:="GotoText2"
MsgBox ("Field must be completed")
End If
End With
End Sub
Sub ExitText3()
With ActiveDocument.FormFields("Text3")
If Len(.Result) = 0 Then
Beep
Application.OnTime When:=Now + TimeValue("00:00:01"),
Name:="GotoText3"
MsgBox ("Field must be completed")
End If
End With
End Sub
Sub GotoText1()
ActiveDocument.Bookmarks("Text1").Range.Fields(1).Result.Select
End Sub
Sub GotoText2()
ActiveDocument.Bookmarks("Text2").Range.Fields(1).Result.Select
End Sub
Sub GotoText3()
ActiveDocument.Bookmarks("Text3").Range.Fields(1).Result.Select
End Sub


--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 
P

Peter Hewett

Hi Graham

I'm not sure what's happening (or not for you) but rather than persevere
with that code I've posted more concise and document variableless versions.
Check the follow up posts for a more viable and complete code samples. Lack
of conciseness is a problem when you post code at 2.00am!!

If you have further problems, post again and I'll email you something that
works.

Sorry for any confusion + Cheers - Peter
 
G

Graham Mayor

I prepared my last post (which has not yet appeared on the server) before
going on line, so when it pops up, ignore it until I have had time to
evaluate the further contributions from yourself and Charles.

--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 
G

Graham Mayor

Excellent, though your earlier simpler contribution will work for me :)
Thanks for this.

--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 
C

Charles Kenyon

I don't see why it should skip a FF on you? ALL FF's need the OnEntry
macro
but only those you want validated need the OnExit macro. As soon as you
leave a FF it set the Private variable mstrFF based on the contents of the
current FF. So when you move to the next FF and fire its OnEntry macro the
variables already set.

I suspect that the OnEntry macro did not fire upon the protection of the
document. The focus was, nevertheless, in the field. All I need to do with
templates is cycle through the fields manually once and then reset them
before saving.

Thank you for the coding.
--

Charles Kenyon

See the MVP FAQ: <URL: http://www.mvps.org/word/> which is awesome!
--------- --------- --------- --------- --------- ---------
This message is posted to a newsgroup. Please post replies
and questions to the newsgroup so that others can learn
from my ignorance and your wisdom.
 
G

Graham Mayor

Peter
I have added some of this to my web site with appropriate credit to you. I
do not have your e-mail address to contact you off group. If you have any
comments or objections, let me know and I will change or remove the page.
http://www.gmayor.com/Whats_New.htm

The new page should be active by the time you see this. If not try again in
a couple of minutes.

--
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
Graham Mayor - Word MVP

Web site www.gmayor.com
Word MVP web site www.mvps.org/word
<>>< ><<> ><<> <>>< ><<> <>>< <>>< ><<>
 

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