|
In this Microsoft Word Help & Tips page I will
show you how to create and employ a UserForm from a document template.
I have held off publishing this page for over a year because there is
already a page on this topic published on the MVP FAQ page
Create
a UserForm by friend and fellow MVP
Doug Robbins. This page is not intended to diminish the
helpfulness of that page. However Doug's article provides
only the bare bone basics where with this page I will attempt to enhance
some of the methods and provide examples of several available UserForm
controls. For this article I am
going to create a template and UserForm for a simple Student Survey
form. The template contains the boilerplate text in the Student Survey,
DocVariable fields placed at "most" locations in the template where variable data gathered
by the UserForm will be placed, a bookmark for one specific data
element (i.e., the address data), a VBA code module for creating document
variables and processing the
UserForm, and the UserForm object with its associated processing code.
An illustration of the template opened in Word (with field codes displayed)
is shown below:
On Doug's suggestion, with one exception I am departing from
an often used practice of using
bookmarks as placemarker\data stores in the document and will use document variables and DocVariables
fields instead.
Document variables and DocVariable fields are easier to code and you
have the advantage of using the \*CharFormat in the DocVariable fields.
This lets you add different formatting to the text at each DocVariable
field (note the DocVariable field located in the template header).
The data in that field will be formatted to match the "D" in
"DocVariable. The exception is that I will use a bookmark for the
"Address" information. At one time I thought I "needed" to
use a bookmark to process the multi-line input. That is not the case.
However, I still want to illustrate a method for putting data "in" a
bookmark instead of "at" a bookmark as was shown in the MVP FAQ
article. Having data in a bookmark allows you to reference
that data and repeat it using REF fields at other locations in the
document. For more on Repeating data and populating fields see my: Repeating
Data (or populating fields)
|
|

|
|
If you don't know what a template is or if you need help
creating one see the MVP FAQ:
Create a Template. For this exercise I named my template Student
Survey Form.dotm.
If you are using Word2007 you must use a .dotm (or macro enabled)
template.
|
|
DocVariable fields can be entered
using Insert Tab>QuickParts>Field>DocVariable ribbon sequence (Word2007)
or Insert>Field>DocVariable menu sequence (Word97-2003); or you can
simply type the field text e.g., DocVariable varName, select it, and then
press CTRL+F9. Bookmarks are entered using Insert
Tab>Link>Bookmark ribbon sequence (Word2007) or Insert>Bookmarks.. menu
sequence (Word97-2003) |
|
After you create your template and enter the
DocVariable fields and bookmark it is time to open the Visual Basic Editor (VBE) to create
a code module and UserForm object. The UserForm serves to collect and in certain
cases validate student responses to the survey questions. It is
presented to the user when a Student Survey document is created. The UserForm
I created for
this exercise is shown below: |
|

|
|
In this UserForm I have included most of the common UserForm
controls. The "Name, Address, Age, and
Phone Number" controls are Textbox controls. The "Address"
control is Textbox control with the MultiLine property set to true.
I associated a a Spinbutton control with the Age textbox. I used
OptionButton controls for "Gender" and Checkbox controls for "Personal
Items." The "Favorite Subject" control is a single item select ListBox.
The "Favorite Teacher" control is ComboBox control with the
MatchRequired property set to true. This
means that the entry must match one of the listed teachers. The
"Favorite Food" control is a single item ComboBox. Students can
select an item from the list or write in their own entry. The
"Sports Program" control is a multi-select ListBox. Students use
the Ctrl key and mouse to select multiple items in the list. I used a frame control to group the survey questions.
There are two command button controls used to process the form
or cancel.
I used one uncommon but very handy
DateAndTimePicker control for entering the birthday. |
|
Press ALT+F11 to open the VBE. If not
already showing, then show the "Project Explorer" and "Properties
Window." To show these item use the VBE menu View>Project Explorer and
View>Properties Window. In the Project Explorer, navigate to and
select the Project "Student Survey Form." Your VBE should
look like the illustration below. |
|

|
|
Using the Tools>References menu verify or
establish a reference to the Microsoft Forms 2.0 Object Library and
Microsoft Windows Common Controls-2.6.0 (SP3) as shown below:
Unfortunately
Microsoft no longer includes the Microsoft Windows Common Control-2-6-0
(SP3) in the resource library. You need this reference for the
DateAndTimePicker control to work. If you can't find it in the
list of available resources then you will need to download
MSCOMCT2.OCX (see
Office Help &
Support KB 297381). Once you have saved MSCOMCT2.OCX in your
system directory (e.g., C:\Windows\System32 for Windows XP
Professional) you will need to register it. Click "Start" then
"Run," type in
regsvr32
c:\windows\system32\mscomct2.ocx, then click OK. If you don't want
to go the trouble of doing this, I have included a second UserForm in
the .dot version of the Student Survey Form template that
substitutes a Textbox control for birthday and omits the
DateAndTimePicker control.
|
|

|
|
Use the VBE Insert menu to insert a
"Module" and a "UserForm" object. This creates a new Module1 and
UserForm1 object in the project. Use the "Properties Window"
"Name" property to rename the code module and UserForm to "Main" and
"frmSurvey" respectively. Double click on "frmSurvey." This
will display the UserForm object and the Control Toolbox. Click
and drag two command button controls onto the UserForm object. |
|

|
Select the CommandButton1 and use the "Properties Window" "Name" property to
rename the control "cmdOK." Use the
"Caption" property and change the caption to "OK." Select the
other control, name it "cmdCancel" and change its caption to "Cancel."
Position and adjust the size of the controls to suit. |
|

|
|
Right click the frmSurvey object in the Project
Explorer and select "View code." At the top of the code window
type in Option Explicit and then
click the "General" dropdown on the left hand side. Select "cmdOK." |
|

|
|
This inserts the default event "Click" procedure
for the control. In the procedure type in "Me.Hide" (without quotes).
Select the cmdCancel control in the dropdown and insert a click
procedure, type in "Me.Hide.
Your code window should look like the illustration below: |
|

|
|
Now we will enter the basic code in the new code
module "Main" that creates and instance of and displays the UserForm.
Double click the project module "Main" and type in or copy and paste the
following code: |
Sub CallUF()
Dim oFrm As frmSurvey
Set oFrm = New frmSurvey
oFrm.Show
Unload oFrm
Set oFrm = Nothing
End Sub |
You can step through a VBA procedure from the VBE using the F8 key.
Give it a try. As you step through the code you will see your
UserForm be created and displayed. Click either "OK" or "Cancel"
and you will see it disappear. |
|
You will now need to enter the remaining controls
and set their respective properties (i.e., name, caption, background color,
foreground color, font, MultiSelect, MatchEntry, etc.) using the
Properties Window, create the event procedures, and expand
the processing code to complete the form. I am not going to step
through the construction of each control or the event procedures.
You can download the complete template (Word2007 and Word97-2003
versions) with all the code here:
Student Survey Forms.
I will review some of the event procedures and the processing code in
the discussion that follows.
You may have noticed that the ListBox and
multi-select ListBox in the example form has items displayed and one of
the Gender option buttons is selected. This is accomplished using
the UserForm "Initialize" event. This event establishes the
conditions in the UserForm when it is initially displayed to the user.
The code for the Student Survey Form is: |
Private Sub UserForm_Initialize()
Dim myArray() As String
With Me
.obUndecided.Value = True
With .LBFavSub
.AddItem
"Math"
.AddItem
"English"
.AddItem
"Science"
.AddItem
"Social Studies"
.AddItem
"Home Room"
End With
.CBFavTeach.List = Array("Mr.
Hardnose", "Ms Toad", "Mrs. Shickleburger", "Mr. Badger")
myArray = Split("Beans and
Franks|Pizza|Grinders|Cold Gruel|Grubs", "|")
.CBFavFood.List = myArray
.LBmultisel.List =
Split("Football,Basketball,Baseball,Soccer,Tennis,Golf," _
& "Hockey,Gymnastics,Water
Polo,Swimming", ",")
End With
End Sub |
The code above illustrates how to set the state of
an OptionButton control and illustrates several methods of loading
entries into Listboxes and Comboboxes. The .AddItem method is
handy for small lists while the .List method makes quick work of larger
lists loaded from an array. For a whole lot more on loading
ListBoxes or ComboBoxes, see my:
Populate UserForm ListBox |
|
The Spinbuttons are used to increment the value in
the age field by 1 each time the Spinbutton control is
pressed up or down. The code for this control is shown below.
You should see that the upper limit is 120 and the
lower limit is 0. |
Private Sub
SpinButton1_SpinUp()
Dim lngAge as Long 'See
note
On Error GoTo Err_Age
lngAge = Me.txtAge
If lngAge < 120 Then
Me.txtAge = lngAge + 1
End If
Exit Sub
Err_Age:
Me.txtAge = 0
Resume
End Sub
Private Sub SpinButton1_SpinDown()
Dim lngAge as Long 'See
note
On Error GoTo Err_Age
lngAge = Me.txtAge
If lngAge > 1 Then
Me.txtAge = lngAge - 1
End If
Exit Sub
Err_Age:
Me.txtAge = 0
Resume
End Sub
Variables with the same name and type used in multiple procedures can be
declared once at the object level (i.e., right at the top under the
Option Explicit statement) |
|
Students being students, you might want to
validate some of the UserForm inputs. While the Spinbuttons are used
to increment the value in the age field, we want to ensure that only a
numeric value less than 120 is entered. We are also going to use
the Age field input to set the year displayed in the DateAndTimePicker
control to a value near the the students birth year.
|
|
Private Sub
txtAge_Change()
Dim pSpan
If Not IsNumeric(Me.txtAge) Then
If Len(Me.txtAge) > 1 Then
Me.txtAge.Text =
Left(Me.txtAge.Text, Len(Me.txtAge.Text) - 1)
Else
Me.txtAge.Text = ""
End If
Exit Sub
End If
If Len(Me.txtAge) > 3 Then
Me.txtAge.Text = Left(Me.txtAge.Text, 3)
End If
If Val(Me.txtAge) > 120 Then
MsgBox "Contact Guiness World Records!!"
Me.txtAge.Text = Left(Me.txtAge.Text, 2)
End If
pSpan = Format(DateAdd("yyyy", -(CDbl(Me.txtAge)), Now), "yyyy")
Me.DTPicker1 = CDate("1/1/" & pSpan)
End Sub |
Or you might want to validate and format the data
entered. The phone number field formats the number entered as:
(###)-###-####. |
Private Sub txtPhone_Exit(ByVal
Cancel As MSForms.ReturnBoolean)
Dim pStr As String
pStr = Me.txtPhone.Value
If (pStr Like "(###) ###-####") Then Exit Sub
If (pStr Like "##########") Then
pStr = "(" & Left(pStr, 3) & ") " &
Mid(pStr, 4, 3) & "-" & Right(pStr, 4)
Me.txtPhone.Value = pStr
ElseIf (pStr Like "###-###-####") Then
pStr = "(" & Left(pStr, 3) & ") " &
Right(pStr, 8)
Me.txtPhone.Value = pStr
ElseIf (pStr Like "### ### ####") Then
pStr = Replace(pStr, " ", "-")
pStr = "(" & Left(pStr, 3) & ") " &
Right(pStr, 8)
Me.txtPhone.Value = pStr
ElseIf (pStr Like "(###)###-####") Then
pStr = Left(pStr, 5) & " " & Right(pStr, 8)
Me.txtPhone.Value = pStr
Else
If MsgBox("Your entry does not convert to a
standard U.S. phone number format. " _
& "Do you want to try again?", vbQuestion + vbYesNo, "Invalid Format") _
= vbYes Then
With Me.txtPhone
.SetFocus
.SelStart = 0
.SelLength = Len(.Text)
End With
Cancel = True
Else
Me.txtPhone.Value = pStr
End If
End If
End Sub |
|
You might want to ensure that
fields are not left blank or comment on a particular entry. You
can do this with the "OK" click event as follows: |
Option Explicit
'This public variable is shared
with the CallUF procedure and is used to determine if the student _
processed or canceled the form.
Public boolProceed As Boolean
Private Sub cmdBtnOK_Click()
Select Case ""
Case Me.txtName.Value
MsgBox "Please fill-in
your name."
Me.txtName.SetFocus
Exit Sub
Case Me.txtAge.Value
MsgBox "Please fill-in
your age."
Me.txtAge.SetFocus
Exit Sub
Case Me.txtAddress.Value
MsgBox "Please fill-in
your address."
Me.txtAddress.SetFocus
Exit Sub
Case Me.txtPhone.Value
MsgBox "Please fill-in
your phone number."
Me.txtPhone.SetFocus
Exit Sub
End Select
If Me.obUndecided.Value = True Then
MsgBox "You should schedule an appointment
with the school counselor", _
vbExclamation + vbOKOnly, "Don't know your gender?"
Exit Sub
End If
'Set value of a public variable
declared at the forms level." This is used to determine if _
form was processed or if the student canceled.
Me.boolProceed = True
Me.Hide
End Sub |
|
Now let's shift our attention to the code needed to
process the information entered in the UserForm and put it in the
document. To do this we will add additional processing code to the
CallUF procedure created earlier. To process the data
and put it in the bookmark and DocVariable fields created earlier we are going to need to declare a few more
variables. These new variables and the processing code is
shown below: |
|
Option Explicit
Sub CallUF()
Dim oFrm As frmSurvey
Dim oVars As Word.Variables
Dim pStr As String
Dim oRng As Word.Range
Dim i As Long
Dim pMulSel As String
Set oVars = ActiveDocument.Variables
Set oFrm = New frmSurvey
With oFrm
.Show
If .boolProceed Then
oVars("varName").Value = .txtName
oVars("varAge").Value = .txtAge
'Replace the line
breaks entered by the user with line breaks and tabs _
to ensure address entry is properly indented.
See notes below.
pStr = Replace(.txtAddress.Value, Chr(10),
Chr(10) + Chr(9))
Set oRng =
ActiveDocument.Bookmarks("bmAddress").Range
oRng.Text = pStr
ActiveDocument.Bookmarks.Add "bmAddress", oRng
oVars("varBirthDay").Value = .DTPicker1
'Define the Gender
text
Select Case True
Case .obMale
pStr =
"Male"
Case .obFemale
pStr =
"Female"
Case .obUndecided
pStr =
"Undecided"
End Select
oVars("varGender").Value = pStr
oVars("varPhone").Value = .txtPhone
'Process student
responses (including no response)
If Not IsNull(.LBFavSub.Value) And
(.LBFavSub.Value) <> "" Then
oVars("varFavSub").Value =
.LBFavSub.Value
Else
oVars("varFavSub").Value = "Not
provided."
End If
If .CBFavTeach.Value <> "" Then
oVars("varFavTeach").Value =
.CBFavTeach.Value
Else: oVars("varFavTeach").Value = "No response"
End If
If .CBFavFood.Value <> "" Then
oVars("varFavFood").Value =
.CBFavFood.Value
Else: oVars("varFavFood").Value = "No response"
End If
'Define the PersItems
text string
If .CheckBox1.Value = True Then pStr = "Cell
phone, "
If .CheckBox2.Value = True Then pStr = pStr &
"Car, "
If .CheckBox3.Value = True Then pStr = pStr &
"MP3 Player, "
If .CheckBox4.Value = True Then pStr = pStr &
"Bullwhip."
'Clean up the string
text
If Right(pStr, 2) = ", " Then pStr = Left(pStr,
Len(pStr) - 2) & "."
On Error Resume Next
pStr = Left(pStr,
InStrRev(pStr, ",") - 1) & " and" & Mid(pStr, InStrRev(pStr, ",") + 1)
On Error GoTo 0
If pStr = "" Then pStr =
"No response"
oVars("varPersItems").Value = pStr
'Build
the multi-select string
pMulSel = ""
With .LBmultisel
For i =
0 To .ListCount - 1
If .Selected(i) Then
pMulSel = pMulSel & .List(i) & ", "
End If
Next i
End With
'Clean up the string text.
If Right(pMulSel, 2) = ",
" Then pMulSel = Left(pMulSel, Len(pMulSel) - 2) & "."
On Error Resume Next
pMulSel = Left(pMulSel,
InStrRev(pMulSel, ",") - 1) & " and" & Mid(pMulSel, InStrRev(pMulSel,
",") + 1)
On Error GoTo 0
If pMulSel = "" Then
pMulSel = "No Response"
oVars("varFavSports").Value = pMulSel
UpdateThisFormsFields
Else
MsgBox "Form cancelled by user"
End If
End With
Unload oFrm
Set oFrm = Nothing
Set oVars = Nothing
Set oRng = Nothing
End Sub |
|
Notes:
1. Notice that it takes two additional steps and lines of code to process the bookmark
for the address data. Whenever you place data "in" a bookmark
instead of "at" a bookmark you must redefine the bookmark range.
This destroys the bookmark and another one with the same name must be
added at the redefined range. This may seem like a lot of trouble
but it is worth the extra effort.
2. If you elect to use a DocVariable for a
multi=line textbox and want to indent the subsequent lines to tab stop
you would use the following code:
oVar("varAddress").Value =
Replace(Me.TextBox1.Text, Chr(10), Chr(9))
In the procedure above we have set the value of several
document variables to the value that the user enter entered in the
UserForm. We entered the DocVariable fields when we created the
template. Those fields need to be updated to the new variable valuse. We accomplished this by calling a
separate procedure
"myUpdateFields" that cycles through each storyrange in the document and
updates the fields. This procedure is shown below:
|
|
Sub
myUpdateFields()
Dim pRange As Word.Range
Dim iLink As Long
iLink = ActiveDocument.Sections(1).Headers(1).Range.StoryType
For Each pRange In ActiveDocument.StoryRanges
Do
pRange.Fields.Update
Set pRange = pRange.NextStoryRange
Loop Until pRange Is Nothing
Next
End Sub |
DocVariable fields that point to a variable that doesn't exist can
look pretty gnarly when the field result is displayed.
To keep our template looking
nice we can set initial values in each variable with a simple procedure:
|
|

Field Codes displayed |

Field result displayed |
|
Sub
Create_Reset_Variables()
With ActiveDocument.Variables
.Item("varName").Value = " "
.Item("varAge").Value = " "
.Item("varAddress").Value = " "
.Item("varBirthday").Value = " "
.Item("varGender").Value = " "
.Item("varPhone").Value = " "
.Item("varFavSub").Value = " "
.Item("varFavTeach").Value = " "
.Item("varFavFood").Value = " "
.Item("varFavSports").Value = " "
.Item("varPersItems").Value = " "
End With
UpdateThisFormsFields
End Sub |
|
Now all we need to do is deploy the UserForm when
a student creates a new Student Survey Form from the template.
This easily accomplished by adding an AutoNew event procedure to the
project module "Main." With this procedure we will create the
document variables and display the form each time a new document is
created from the template. |
|
Sub AutoNew()
Create_Reset_Variables
CallUF
End Sub |
This concludes this Tips Page. I hope
that it gives you a better understanding and appreciation for UserForms.
There are several other helpful articles on UserForms in the Word MVP
FAQ pages
MVP FAQ UserForms |