Create and Employ a UserForm

Home Up Odds & Ends Photo Gallery Search Contact Me Privacy Notice

 

 

The information in this website is provided without risk or obligation and free of charge.  However, if you have benefitted from my efforts here and would like to make a contribution to help me continue and maintain this work then any donation will be greatly appreciated. Please click the adjacent button to access PayPal.  Thank you.
 

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.

:old: 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:

:oops: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
:old: 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

:old: 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

:old: 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

:old: 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


Looking for something else?

Google