I’ve been developing some “smart” forms a government department in the last few months. These forms had to be developed in good old MS Word. Now, Word is not really suited to creating tightly controlled forms, but, in my case it’s all I had to work with and I’ve had to work within the limitations presented to me. The only real solution available was to use Word Content Controls and then to protect portions of the document that users shouldn’t be able to edit. I’ve managed to get things working fairly well and thought it worth sharing a bit of code I’ve developed along the way that should help anyone working with Microsoft Word Content Controls and VBA.
Data Validation
If you’re doing forms then data validation is critical. It’s pretty straightforward if you’re working with content controls if you make use of the ContentControlOnExit event. For example, here’s a bit of basic validation that ensures the value entered into a field is currency:
Private Sub Document_ContentControlOnExit(ByVal CC As contentControl, Cancel As Boolean) Dim sngTotalCost As Single Dim oCC As contentControl Select Case CC.Tag Case "sTotalCost" If Not validateCurrency(CC.Range.Text) Then Cancel = True Beep CC.Range.Select Exit Sub Else CC.Range.Text = Format(parseCurrency(CC.Range.Text), "$#,###0.00") End If End Select End Sub Public Function validateCurrency(sValue As String) As Boolean Dim iLoop As Integer Dim bReturn As Boolean Dim iAsc As Integer On Error GoTo errorHandler bReturn = False validateCurrency = bReturn sValue = Trim(sValue) sValue = Replace(sValue, "$", "") sValue = Replace(sValue, ",", "") If Len(sValue) = 0 Then validateCurrency = True Exit Function End If For iLoop = 1 To Len(sValue) iAsc = Asc(Mid(sValue, iLoop)) If iAsc = Asc(".") Or (iAsc >= Asc("0") And iAsc <= Asc("9")) Then Else Exit Function End If Next iLoop validateCurrency = True Exit Function errorHandler: MsgBox "An error has occurred" & vbCrLf & "Module: ThisDocument" & vbCrLf & "Procedure: validateCurrency" & vbCrLf & "Error Number: " & Err.Number & vbCrLf & "Description: " & Err.Description, vbOKOnly Err.Clear End Function Public Function parseCurrency(sValue As String) As Single Dim iLoop As Integer Dim iAsc As Integer On Error GoTo errorHandler parseCurrency = 0 sValue = Trim(sValue) sValue = Replace(sValue, "$", "") sValue = Replace(sValue, ",", "") If Len(sValue) = 0 Then parseCurrency = 0 Exit Function End If For iLoop = 1 To Len(sValue) iAsc = Asc(Mid(sValue, iLoop)) If iAsc = Asc(".") Or (iAsc >= Asc("0") And iAsc <= Asc("9")) Then Else Exit Function End If Next iLoop parseCurrency = Round(CSng(sValue), 2) Exit Function errorHandler: MsgBox "An error has occurred" & vbCrLf & "Module: ThisDocument" & vbCrLf & "Procedure: parseCurrency" & vbCrLf & "Error Number: " & Err.Number & vbCrLf & "Description: " & Err.Description, vbOKOnly Err.Clear End Function
In this code we simply check the tag of each content control as users move to the next one. Content control tags are set on the Developer tab from within Word. If the control has a tag we’re interested in then the value (Range.Text) is run through the ValidateCurrency function. If it is valid then the parseCurrency function is used to format the value correctly and write it back to the content control contents. If the value entered isn’t valid then a user alert is raised and the focus is returned to the content control
Excel Style “Automatic” Calculations
If you’ve got data validation sorted out it’s a simple step to have read only fields in your forms whose values are derived from user entered fields. This bit of code takes the value of a currency field, multiplies it by 1.1 and writes the value into a second field.
Private Sub Document_ContentControlOnExit(ByVal CC As contentControl, Cancel As Boolean) Dim sngTotalCost As Single Dim oCC As contentControl Select Case CC.Tag Case "sTotalCost" If Not validateCurrency(CC.Range.Text) Then Cancel = True Beep CC.Range.Select Exit Sub Else CC.Range.Text = Format(parseCurrency(CC.Range.Text), "$#,###0.00") End If Set oCC = ActiveDocument.SelectContentControlsByTag("sTotalCost").Item(1) sngTotalCost = parseCurrency(oCC.Range.Text) Set oCC = ActiveDocument.SelectContentControlsByTag("sTotalCostGST").Item(1) With oCC .LockContents = False .Range.Text = Format(sngTotalCost * 1.1, "$#,###0.00") .LockContents = True End With End Select Set oCC = Nothing End Sub
It’s pretty simple to see what’s happening here. The value in the content control with the tag sTotalCost is validated for currency, and if a correct value the number has a calculation applied to it and the resultant value is written to a second content control with the tag sTotalCostGST. Note that I use the LockContents method to be able to write to the second content control and then use it again to make it read only.
Changing Document Format Based on Content Control Value
The last thing I’ll share is using the value in a content control to change some format in a Word document. In this case I simply set the font color of the content control based on the value selected in the control
Private Sub Document_ContentControlOnExit(ByVal CC As contentControl, Cancel As Boolean) Dim sngTotalCost As Single Dim oCC As contentControl Select Case CC.Tag Case "sTag1", "sTag2", "sTag3" If CC.Range.Text = "Yes" Then CC.Range.Font.ColorIndex = wdGreen End If If CC.Range.Text = "No" Then CC.Range.Font.ColorIndex = wdRed End If End Select Set oCC = Nothing End Sub
These content controls were of the dropdown list type. When “Yes” is selected the font color is set to green with the ColorIndex property. When set to “No” the Font.ColorIndex is set to Red. Pretty simple.