Some Useful Word Macro Examples

Home Up Search This Site What's New? Audio On CDR Favourites Downloadable files Photo Gallery 2002 Photo Gallery 2003 Photo Gallery 2004/5 Photo Gallery 2006/7 Photo Gallery 2008 UK Photo Gallery Ireland Photo Gallery Cats Photo Gallery 

 

Google
 

 

There is no charge for using any of the material (for personal use) on this web site, but if you wish to make a contribution to the ever growing running costs, any donation would be much appreciated. Click the adjacent button to access PayPal

Some Useful Macro Examples

 

Frequently the Word forums throw up some interesting exercises in macro programming. Some of the better examples that my fellow MVPs and I have come up with appear elsewhere in the Word pages on this site. This page offers a place to present some code examples that may have wider use, which you can adapt to your own requirements. I will add to the examples as they come up, but for the moment we will start with the InsertField dialog:

Insert Field formatting switch

 

The InsertField dialog (the illustrations are from Word 2003 (top) and 2007, earlier versions are similar) has the Preserve formatting during updates check box checked by default, with no obvious way of resetting that default. This adds a MERGEFORMAT switch to the inserted field. Frankly I have never found any real use for the switch and so I always uncheck it .... when of course I remember, so the first macro I created simply intercepts the InsertFile command and uses the SendKeys command to physically uncheck the box i.e.

 

Sub InsertField()
SendKeys "{Tab}{Tab} +{Tab}+{Tab}"
Dialogs(wdDialogInsertField).Show
End Sub

 

This worked fine, until fellow MVP, who uses the pseudonym Macropod, baited me to produce a version which gave the user the opportunity to add a CHARFORMAT switch as an alternative to the MERGEFORMAT switch. The result was the following.

Inserting a field from the Insert > Field menu option (Insert > Quick Parts > Field in Word 2007) opens the dialog with the check box unchecked, using the same method as above, but if you check the box, you are presented with a message box which offers the opportunity to choose the type of formatting switch, then adds the appropriate switch to the field.

Check the box and you will see the further dialog

 

The result is that the field may be inserted with either switch as appropriate e.g.

{ CREATEDATE \@ "dddd, dd MMMM yyyy" \* CHARFORMAT }

by selecting YES

{ CREATEDATE \@ "dddd, dd MMMM yyyy" \* MERGEFORMAT }

by selecting No

or none if the Insert Field Dialog box is left unchecked.

{ CREATEDATE \@ "dddd, dd MMMM yyyy" }

 

Sub InsertField()
Dim oRng As Range
Dim i As Variant
Dim sSwitch As String
Dim strChoice As String
SendKeys "{Tab}{Tab} +{Tab}+{Tab}"
Dialogs(wdDialogInsertField).Show

On Error Goto Finish 'User has cancelled
Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
Set oRng = Selection.Range
For i = 1 To oRng.Fields.Count
    With oRng.Fields(i)
        If InStr(1, .Code, "MERGEFORMAT") <> 0 Then
             sSwitch = MsgBox("Use charformat in place of the mergeformat switch?", _
             vbYesNo, _
             "Insert Field")
             If sSwitch = vbYes Then
                  .Code.Text = Replace(.Code.Text, _
                  "MERGEFORMAT", _
                  "CHARFORMAT")
             End If
        End If

    .Update
    End With
Next
i
Selection.MoveRight Unit:=wdCharacter, Count:=1

Finish:
End Sub

Note:

I am informed, by the aforementioned Macropod that the SendKeys approach will not work under the Windows Vista operating system, returning error code 70: "Permission denied" - so that's another reason why I will be keeping Windows XP for the foreseeable future.

This then negates the use of the above macro for Vista users, but I have included a modified version below with the SendKeys line removed and a separate step to remove the switch. It is not as elegant as un-checking the check box, but it does the job.

Windows Vista Version

Note:

The following will work in Windows XP also, but requires an extra step to overcome the SendKeys issue.

 

Sub InsertField()
Dim oRng As Range
Dim i As Variant
Dim sSwitch As String
Dim strChoice As String
Dialogs(wdDialogInsertField).Show

On Error Goto Finish 'User has cancelled
Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
Set oRng = Selection.Range
For i = 1 To oRng.Fields.Count
    With oRng.Fields(i)
        If InStr(1, .Code, "MERGEFORMAT") <> 0 Then
             sSwitch = MsgBox("Use charformat in place of the mergeformat switch?", _
             vbYesNo, _
             "Insert Field")
             If sSwitch = vbYes Then
                  .Code.Text = Replace(.Code.Text, _
                  "MERGEFORMAT", _
                  "CHARFORMAT")
             End If

             If sSwitch = vbNo Then
                  sSwitch = MsgBox("Remove switch?", _
                  vbYesNo, _
                  "Insert Field")
                  If sSwitch = vbYes Then
                       .Code.Text = Replace(.Code.Text, _
                       " \* MERGEFORMAT ", _
                       "")

                  End If
             End If
        End If
    .Update
    End With
Next
i
Selection.MoveRight Unit:=wdCharacter, Count:=1

Finish:
End Sub

 

The Vista version of the macro has two message boxes. One of them is identical to the Windows XP version, the other is displayed when the user responds to the first box with 'No'

Number documents

 

There is a page on this site dedicated to numbering documents but on a number of occasions I have been asked for a variation of this to place an incrementing number at a bookmarked location in a series of documents. The example used here was created to print a batch of numbered receipts, and includes a second macro to reset the stored start number.

The macro uses a bookmark in the document template named RecNo

 

Sub AddNoFromINIFileToBookmark()
Dim SettingsFile As String
Dim Order As String
Dim iCount As String
Dim rRecNo As Range
Dim i As Long
iCount = InputBox("Print how many copies?", _
"Print Numbered Copies", 1)
If iCount = "" Then Exit Sub
SettingsFile = Options.DefaultFilePath(wdStartupPath) & "\Settings.ini"
Order = System.PrivateProfileString(SettingsFile, _
"DocNumber", "Order")
If Order = "" Then
    Order = 1
End If
For i = 1 To iCount
    Set rRecNo = ActiveDocument.Bookmarks("RecNo").Range
    rRecNo.Text = Format(Order, "00000")
    With ActiveDocument
        .Bookmarks.Add "RecNo", rRecNo
        .Fields.Update
        .ActiveWindow.View.ShowFieldCodes = False
        .PrintOut
    End With
    Order = Order + 1
Next i
System.PrivateProfileString(SettingsFile, "DocNumber", _
"Order") = Order
End Sub


Sub ResetStartNo()
Dim SettingsFile As String
Dim Order As String
Dim sQuery As String
SettingsFile = Options.DefaultFilePath(wdStartupPath) & "\Settings.ini"
Order = System.PrivateProfileString(SettingsFile, _
"DocNumber", "Order")
sQuery = InputBox("Reset start number?", "Reset", Order)
If sQuery = "" Then Exit Sub
Order = sQuery
System.PrivateProfileString(SettingsFile, "DocNumber", _
"Order") = Order
End Sub
 

 

Instead of printing a batch of similar numbered documents, the following variation simply adds the incremented number to each new document created from the template at the bookmarked location named RecNo. The reset macro above will reset this version equally as the following uses the same stored number data.

 

Sub AutoNew()
Dim SettingsFile As String
Dim Order As String
Dim rRecNo As Range
Dim i As Long

SettingsFile = Options.DefaultFilePath(wdStartupPath) & "\Settings.ini"
Order = System.PrivateProfileString(SettingsFile, _
        "DocNumber", "Order")
If Order = "" Then
    Order = 1
End If
   
Set rRecNo = ActiveDocument.Bookmarks("RecNo").Range
    rRecNo.Text = Format(Order, "00000")
   
With ActiveDocument
        .Bookmarks.Add "RecNo", rRecNo
        .Fields.Update
        .ActiveWindow.View.ShowFieldCodes =
False
   
End With
    Order = Order + 1
System.PrivateProfileString(SettingsFile, "DocNumber", _
        "Order") = Order
End Sub

Paste unformatted text

 

If you paste text from the internet for example, the paste will bring across all the formatting of the web page, whereas users frequently require the pasted text to adopt the formatting of the document into which it is pasted. This can be achieved with Paste Special > Unformatted text, but the macro recorder will not accurately record that action so....

 

Sub PasteUnfText()
   
On Error GoTo oops
    Selection.PasteSpecial _
    DataType:=wdPasteText, _
    Placement:=wdInLine
   
End
oops:
Beep
End Sub

Copy footnotes c/w formatting to a new document

 

Sub CopyFootnotes()
Dim sDoc As Document
Dim tDoc As Document
Dim sId As String
Set sDoc = ActiveDocument
Set tDoc = Documents.Add
For i = 1 To sDoc.Footnotes.Count
    sId = sDoc.Footnotes(i).Index
    sDoc.Footnotes(i).Range.Select
    Selection.Copy
    tDoc.Activate
   
With Selection
        .Style = "Footnote Text"
        .Font.Superscript =
True
        .TypeText sId & " "
        .Font.Superscript =
False
        .Paste
        .TypeParagraph
   
End With
    sDoc.Activate
Next i
tDoc.Activate
End Sub
 

Send the current document from Word by e-mail as an attachment, with the header details pre-completed, e.g. for the return of a completed form document.

 

Sub SendDocumentAsAttachment()

'This macro requires the Outlook Object library to be checked

'in the vba editor Tools > References
Dim
bStarted As Boolean
Dim oOutlookApp As Outlook.Application
Dim oItem As Outlook.MailItem
On Error Resume Next
If Len(ActiveDocument.Path) = 0 Then 'Document has not been saved
     ActiveDocument.Save 'so save it
End If
'see if Outlook is running and if so turn your attention there
Set oOutlookApp = GetObject(, "Outlook.Application")
If Err <> 0 Then 'Outlook isn't running
     'So fire it up
     Set oOutlookApp = CreateObject("Outlook.Application")
     bStarted = True
End If
'Open a new e-mail message
Set oItem = oOutlookApp.CreateItem(olMailItem)
With oItem 'and add the detail to it
     .To = "someone@somewhere.com" 'send to this address
     .Subject = "New subject" 'This is the message subject
     .Body = "See attached document" ' This is the message body text
     .Attachments.Add Source:=ActiveDocument.FullName, Type:=olByValue
     .Send
     '**********************************
     'If you want to view the message before it goes
     'change the line above from .Send to .Display
     'Otherwise the message is sent straight to the Outbox
     'and if you have Outlook set to send mail immediately,
     'it will simply be Sent
     'with no obvious sign that Outlook has operated.
     'Apart from the copy in the Outlook Sent folder
'**********************************

End With
If bStarted Then 'If the macro started Outlook, stop it again.
     oOutlookApp.Quit
End If
'Clean up
Set oItem = Nothing
Set oOutlookApp = Nothing
End Sub

Toggle the SQL security entry in the registry through vba

 

You receive the "Opening this will run the following SQL command" message when you open a Word mail merge main document that is linked to a data source - http://support.microsoft.com/?kbid=825765

This linked page explains how to create registry entries to turn off the security message. Some users have been concerned about the security implications of turning off this warning message. The following code was conceived with that issue in mind. The macro creates the registry entry if it is not present and then toggles the setting between 0 and 1 each time the macro is run. It could therefore be adapted for use in a mail merge macro to switch off the warning while the particular merge was run, then switch it back on again on completion.

 

Sub ToggleSQLSecurity()
Dim WSHShell, RegKey, rKeyWord, wVer
Set WSHShell = CreateObject("WScript.Shell")
wVer = Application.Version
If wVer < 10 Then 'The security issue relates to

'Word versions greater than 10.0 (Word 2002)
     MsgBox "This macro is for Word 2002 and later!", vbOKOnly, "Wrong Word Version"
     Exit Sub
End If
Start:
RegKey = "HKEY_CURRENT_USER\Software\Microsoft\Office\" & wVer & "\Word\Options\"
On Error Resume Next 'The registry key does not exist
rKeyWord = WSHShell.RegRead(RegKey & "SQLSecurityCheck")
If rKeyWord = "" Then
     WSHShell.regwrite RegKey & "SQLSecurityCheck", 1, "REG_DWORD" 'set it at zero
     GoTo Start: 'and read it again
End If
If rKeyWord = 1 Then
     WSHShell.regwrite RegKey & "SQLSecurityCheck", 0, "REG_DWORD"

     MsgBox "SQL Security checking is switched off", vbInformation, "SQL Check"

Else
     WSHShell.regwrite RegKey & "SQLSecurityCheck", 1, "REG_DWORD"

     MsgBox "SQL Security checking is switched on", vbInformation, "SQL Check"
End If
End Sub

 

True title case

 

Word has the ability to set a block of text in title case, i.e. each of the words formatted so that its first letter is capitalized, thus:

A Tale Of Two Cities but formatting styles often dictate that articles, prepositions, and conjunctions should be in lower case, thus: A Tale of Two Cities.

Word has no built-in function to perform this type of formatting, but it can be achieved with a macro. The following sets the selected text in Word's title case and then sets all the words in the first array:

vFindText = Array("A", "An", "And", "As", "At", "But", "By", "For", _
"If", "In", "Of", "On", "Or", "The", "To", "With")

to their lower case equivalents with a corresponding array

vReplText = Array("a", "an", "and", "as", "at", "but", "by", "for", _
"if", "in", "of", "on", "or", "the", "to", "with")

The list of corrections can be expanded as required by simply adding the word in its alternative forms to both lists.

If one of the listed words was the first word in the selected text, then it too would be set in lower case, so that too needs to be corrected, and similarly if there is a colon in the selected text, the word following the colon would need to be corrected. The macro forces capitalization on the first letters of all words that appear in either position.

 

Sub TrueTitleCase()
Dim sText As Range
Dim vFindText As Variant
Dim vReplText As Variant
Dim i As Long
Dim k As Long
Dim m As Long
Set sText = Selection.Range
'count the characters in the selected string
k = Len(sText)
If k < 1 Then 'If none, then no string is selected
'so warn the user
     MsgBox "Select the text first!", vbOKOnly, "No text selected"
     Exit Sub 'and quit the macro
End If
'format the selected string as title case
sText.Case = wdTitleWord
'list the exceptions to look for in an array
vFindText = Array("A", "An", "And", "As", "At", "But", "By", "For", _
"If", "In", "Of", "On", "Or", "The", "To", "With")
'list their replacements in a matching array
vReplText = Array("a", "an", "and", "as", "at", "but", "by", "for", _
"if", "in", "of", "on", "or", "the", "to", "with")
With sText
     With .Find
     'replace items in the first list
     'with the corresponding items from the second

          .ClearFormatting
          .Replacement.ClearFormatting
          .Forward = True
          .Wrap = wdFindStop
          .MatchWholeWord = True
          .MatchWildcards = False
          .MatchSoundsLike = False
          .MatchAllWordForms = False
          .Format = True
          .MatchCase = True
          For i = LBound(vFindText) To UBound(vFindText)
               .Text = vFindText(i)
               .Replacement.Text = vReplText(i)
               .Execute Replace:=wdReplaceAll
          Next i
     End With
     'Reduce the range of the selected text
     'to encompass only the first character

     .MoveEnd Unit:=wdCharacter, Count:=-Len(sText) + 1
     'format that character as upper case
     .Case = wdUpperCase
     'restore the selected text to its original length
     .MoveEnd Unit:=wdCharacter, Count:=k
     'and check to see if the string contains a colon
     If InStr(1, sText, ":") > 0 Then
     'If it does note the position of the character
     'after the first colon

          m = InStr(1, sText, ":") + 1
          'and set that as the new start of the selected text
          .MoveStart wdCharacter, m
          'set the end of the selected text to include
          'one extra character

          .MoveEnd Unit:=wdCharacter, Count:=-Len(sText) + 1
          'format that character as upper case
          .Case = wdUpperCase
     End If
End With
End Sub

Extract Acronyms to a new document

 

A newsgroup contributor asked if it was possible to extract acronyms to a new document. The following will extract all words in the format NATO or N.A.T.O. consisting of more than two characters to a new document. The list is then sorted and duplicate entries removed. The macro does not extract acronyms that are adopted as proper nouns e.g. Unesco.

 

Sub ExtractAcronyms()

Dim rText As Range

Dim SDoc As Document

Dim TDoc As Document

Set SDoc = ActiveDocument

Set TDoc = Documents.Add

SDoc.Activate

With Selection

    .HomeKey wdStory

    With .Find

        .ClearFormatting

        .Replacement.ClearFormatting

        .Replacement.Text = ""

        Do While .Execute(findText:="[A-Z.]{2,}", _

            MatchWildcards:=True, _

            Wrap:=wdFindStop, Forward:=True) = True

            Set rText = Selection.Range

            TDoc.Activate

            Selection.TypeText rText & vbCr

            SDoc.Activate

            rText.Collapse wdCollapseEnd

        Loop

    End With

End With

TDoc.Activate

With Selection

    .WholeStory

    .Sort ExcludeHeader:=False, FieldNumber:="Paragraphs", _

        SortFieldType:=wdSortFieldAlphanumeric, _

        SortOrder:=wdSortOrderAscending

    With .Find

        .ClearFormatting

        .Replacement.ClearFormatting

        .Text = "(*^13)@"

        .Replacement.Text = "\1"

        .Wrap = wdFindContinue

        .Execute Replace:=wdReplaceAll

    End With

    .HomeKey wdStory

    .Delete

End With

End Sub

Format part of a found text string

 

Word's replace function is capable of applying formatting to found strings, but only the whole of a found string. Sometimes it is desirable to format only part of the string e.g. you may search for the chemical symbol for water H2O in order to format the 2 as subscript. The replace function cannot do that. With this example the simplest solution is to copy the correctly formatted version to the clipboard and replace the find string H2O with the clipboard contents H2O i.e. put ^c  in the replace with box.

OR

You could create a macro to do so.

 

Sub Subscript2_in_H2O()

Dim rText As Range

With Selection

    .HomeKey wdStory

    With .Find

        .ClearFormatting

        .Replacement.ClearFormatting

        Do While .Execute(findText:="H2O", _

            MatchWildcards:=False, _

            Wrap:=wdFindStop, Forward:=True) = True

            Set rText = Selection.Range 'The found text

            With rText 'Do what you want with the found text

                'move the start position of the found text one character right

                .MoveStart Unit:=wdCharacter, Count:=1

                'move the end position of the found text one character to the left

                .MoveEnd Unit:=wdCharacter, Count:=-1

                'the text string is now one character long i.e. the "2"

                .Font.Subscript = True 'apply subscript to the remaining text string

            End With

        Loop 'and look for the next match

    End With

End With

End Sub

Add a row to a table in a protected form

 

In a recent Word forum question, a user wanted to automatically provide the function to add a row to a table in a protected form and to fill the new row with form fields to match those in the previous row, but with new field bookmark names and a calculation in the final cell to add the other fields in the row. The user had done most of the work, which I have borrowed for this example, but was having problems with the calculation.

The modified solution works in a table with seven columns. The content of the fields in the first six columns is added in the final column.

 

Sub addrow()
'Works with a seven column table
Dim oTable As Table
Dim Response As String
Dim CurRow As Long
Dim i As Long
Dim fCount As Long

Dim sPassword as String

Set oTable = ActiveDocument.Tables(1)

sPassword = "" 'Define the password used to protect the form (if any)
Response = MsgBox("Add new row?", vbQuestion + vbYesNo)
If Response = vbYes Then
     ActiveDocument.Unprotect Password:="sPassword" 'Unprotect document
     Selection.InsertRowsBelow 1 'Add a row to the bottom of the table
     Selection.Collapse (wdCollapseStart) 'Put the cursor in the first cell of the new row
     CurRow = Selection.Information(wdStartOfRangeRowNumber) 'Read the number of the new row

     For i = 1 To oTable.Columns.Count
          oTable.Cell(CurRow, i).Select 'Select the next cell for processing
          Selection.FormFields.Add Range:=Selection.Range, _
          Type:=wdFieldFormTextInput 'And add a form field
          fCount = ActiveDocument.Range.FormFields.Count
          With ActiveDocument.FormFields(fCount)
               .Name = "col" & i & "row" & CurRow 'Add a unique bookmark name
               .Enabled = True 'Enable the field for user entry
               .CalculateOnExit = True 'set the calculate on exit check box
               If i = 6 Then .ExitMacro = "addrow" 'add this macro to the cell in column 6
               If i = 7 Then 'add a calculation to add the field results in cols 1 to 6
                    .Enabled = False
                    .TextInput.EditType Type:=wdCalculationText, _
                    Default:="=Col1Row" & CurRow _
                    & " + Col2Row" & CurRow _
                    & " + Col3Row" & CurRow _
                    & " + Col4Row" & CurRow _
                    & " + Col5Row" & CurRow _
                    & " + Col6Row" & CurRow, _
                    Format:=""
               End If
          End With
     Next
i
 

    With ActiveDocument
          .Protect NoReset:=True, Password:="sPassword", _
          Type:=wdAllowOnlyFormFields 'Reprotect the form
          .Range.FormFields("col1row" & CurRow).Select 'Select the first field in the new row
     End With
End If
End Sub

 

Create a bar chart based on the content of a dropdown form field

 

The aim of this procedure is to create a visual indication, by means of a bar chart, the value of a dropdown form field as in the animated illustration below. The animation will run indefinitely:

 

Each of the dropdown fields in column 1 of the table is configured similarly (create one and copy/paste the remainder). The drop down entries are numbers 0 to 10, and the macro ColTable1Row1 is run on exit from each. The illustration shows the default field bookmark name of Dropdown1. The bookmark names of the fields are immaterial as long as the final character is a number from 1 to 4 to reflect the row of the table. This number is used by the macro to fill the correct row of the table.

 

 

Private mstrFF As String
Sub ColTable1Row1()
Dim oFld As FormFields
Dim i As Long
Dim sCount As Integer
Dim sRow As Integer
Dim bProtected As Boolean

Dim sPassword as String

 

sPassword = "" 'Insert the password (if any), used to protect the form between the quotes
With GetCurrentFF 'Establish which dropdown field is current
     mstrFF = GetCurrentFF.Name
End With
Set oFld = ActiveDocument.FormFields
sCount = oFld(mstrFF).Result 'Get the dropdown field value
sRow = Right(mstrFF, 1) 'Get the number at the end of the bookmark name
 

'Check if the document is protected and if so unprotect it

If ActiveDocument.ProtectionType <> wdNoProtection Then
     bProtected = True
     ActiveDocument.Unprotect Password:=sPassword
End If
 

For i = 2 To 11 'Select each column of the active row in turn and colour the cell white
     ActiveDocument.Tables(1).Rows(sRow).Cells(i).Shading.BackgroundPatternColor = wdColorWhite
Next i


If sCount = 0 Then GoTo Quit 'If user enters 0, the row is already white so quit

Select Case sRow
Case 1 'Row 1 colour is red
     oCol = wdColorRed
Case 2 'Row 2 colour is blue
     oCol = wdColorBlue
Case 3 'Row 3 colour is gold
     oCol = wdColorGold
Case 4 'Row 4 colour is green
     oCol = wdColorGreen
Case Else
End Select

For i = 2 To sCount + 1 'Colour the cells in the row from column 2 to the number entered
     ActiveDocument.Tables(1).Rows(sRow).Cells(i).Shading.BackgroundPatternColor = oCol
Next i

Quit: 'Re-protect the form and apply the password (if any).
If bProtected = True Then
     ActiveDocument.Protect _
     Type:=wdAllowOnlyFormFields, NoReset:=True, Password:=sPassword
End If
End Sub

Private Function GetCurrentFF() As Word.FormField 'Get the dropdown field name
Dim rngFF As Word.Range
Dim fldFF As Word.FormField

Set rngFF = Selection.Range
rngFF.Expand wdParagraph

For Each fldFF In rngFF.FormFields
     Set GetCurrentFF = fldFF
Exit For
Next
End Function