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 Photo Gallery 2009/10 UK Photo Gallery Ireland Photo Gallery Cats Photo Gallery 

 

 

Google
 

Many people access the material from this web site daily. Most just take what they want and run. That's OK, provided they are not selling on the material as their own; however if your productivity gains from the material you have used, a donation from the money you have saved, however small, would help to ensure the continued availability of this resource.

Click the appropriate button above 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, other Word 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 Paul Edstein, who uses the pseudonym Macropod in the forums, 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/2010) 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 Paul Edstein, 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.Range.InsertAfter rText & vbCr

            rText.Collapse wdCollapseEnd

        Loop

    End With

End With

With TDoc
     .Range.Sort ExcludeHeader:=False, _
        FieldNumber:="Paragraphs", _
        SortFieldType:=wdSortFieldAlphanumeric, _
        SortOrder:=wdSortOrderAscending
     .Paragraphs(1).Range.Delete
     .Activate

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. The following shows two techniques for selecting a digit from a found item.

 

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

or even simpler

Sub Subscript2_in_H2ORev1()
With Selection
    .HomeKey wdStory
       
With .Find
            .ClearFormatting
            .Replacement.ClearFormatting
           
Do While .Execute(findText:="H2O", _

                     MatchWildcards:=False, _

                     Wrap:=wdFindStop, _

                     Forward:=True) = True
              
'Do what you want with the found text (Selection.Range)

               'In this case format the second character as subscripted
                Selection.Range.Characters(2).Font.Subscript =
True
           
Loop 'and look for the next match
       
End With
End With
End Sub

 

Format part of a found text string in a list of items

 

Extending the above procedure, you may wish to format a number of similar items - eg chemical formulae. The following version defines the list of items as variants. Note that the subscripted numbers are located in different parts of the text string. The macro loops through the list searching for each variant throughout the document in turn. It then uses Case statements to process the range for each variant to achieve the results shown in red (when pasted to the vba editor the red colour will change to green and the format of the red coloured items will be lost).

In the cases where there are two or digits to format, they are processed separately. You could adapt this technique to format any character in the search strings in any manner you require.

 

Sub FormatChemicalFormulae()
Dim rText As Range
Dim vFindText(4) As Variant 'match the number in the brackets

'to the last number in the list below
Dim i As Long


vFindText(0) = "H2O"
vFindText(1) = "CO2"
vFindText(2) = "H2SO4"
vFindText(3) = "SO42-"
vFindText(4) = "[CO(NH3)6]3+"

'add more numbers as required

'increment the number 'n' in the brackets vFindText(n)


For i = 0 To UBound(vFindText)
    With Selection
        .HomeKey wdStory
        With .Find
            .ClearFormatting
            .Replacement.ClearFormatting
            Do While .Execute(findText:=vFindText(i), _
                MatchWildcards:=
False, _
                Wrap:=wdFindStop, Forward:=
True) = True
                Set rText = Selection.Range 'The found text
                With rText 'Do what you want with the found text
                    Select Case i
                    Case Is
= 0
'H2O - H2O
                       
.Characters(2).Font.Subscript = True
                    Case Is
= 1
'CO2 - CO2
                       
.Characters(3).Font.Subscript = True
                    Case Is
= 2
'H2SO4 - H2SO4
                       
.Characters(2).Font.Subscript = True
                       
.Characters(5).Font.Subscript = True
                    Case Is
= 3
'SO42- - SO42-
                        
.Characters(3).Font.Subscript = True
                         .
Characters(4).Font.Superscript = True
                        
.Characters(5).Font.Superscript = True
                    Case Is
= 4
'[CO(NH3)6]3+ - [Co(NH3)6]3+
                        
.Characters(3).Case = wdLowerCase
                        
.Characters(7).Font.Subscript = True
                        
.Characters(9).Font.Subscript = True
                        
.Characters(11).Font.Superscript = True
                        
.Characters(12).Font.Superscript = True
                    End Select

                
End With
            Loop
'and look for the next match
        End With
    End Wit
h
Next i
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

 

An alternative method of adding a row to a protected table

 

The previous method created the new row in the table and added the fields to the cells. The following takes an alternative approach and copies the last row of the table, complete with its fields and associated macros, and pastes it at the end of the table. It then removes the on exit macro from what was previously the last row, so that there can be no false triggering of the macro if a user goes back to amend a field's content in the last column of a previous row.

The macro makes use of the wdDialogFormFieldOptions dialog, which has the following arguments: Entry, Exit, Name, Enable, TextType, TextWidth, TextDefault, TextFormat, CheckSize, CheckWidth, CheckDefault, Type, OwnHelp, HelpText, OwnStat, StatText, Calculate, to modify the field properties.

As executing this dialog box clears the user entered result of the field, if that content is later required, as seems probable, it is necessary to store the content and replace it after executing the dialog changes.

 

Sub AddRow()
'Run on exit from the last form field in
'the last row of the table

Dim oTable As Table
Dim oRng As Range
Dim oCell As Range
Dim oLastCell As Range
Dim sResult As String
Dim iRow As Integer
Dim iCol As Integer
Dim CurRow As Integer
Dim i As Long

Dim sPassword as String

sPassword = "" 'password to protect/unprotect form
With ActiveDocument
     .Unprotect Password:=sPassword 'Unprotect document
     Set oTable = .Tables(1) 'Select the appropriate table
     iRow = oTable.Rows.Count 'Record the last row number
     iCol = oTable.Columns.Count 'Record the last column number
     Set oLastCell = oTable.Cell(iRow, iCol).Range 'Record the last cell
     sResult = oLastCell.FormFields(1).Result 'Get the value in the last cell
     Set oRng = oTable.Rows(iRow).Range 'Add the last row to a range
     oRng.Copy 'Copy the row
     oRng.Collapse wdCollapseEnd 'collapse the range to its end.
     oRng.Select 'the end of the table
     Selection.Paste 'Paste the row at the end of the table
     CurRow = iRow + 1 'Record the new last row
     For i = 1 To iCol 'Repeat for each column
          Set oCell = oTable.Cell(CurRow, i).Range 'process each cell in the row
          oCell.FormFields(1).Select 'Select the first field in the cell
          With Dialogs(wdDialogFormFieldOptions) 'and name it
               .name = "Col" & i & "Row" & CurRow 'eg Col1Row2
               .Execute 'apply the changes
          End With
     Next
i
     'Select the formfield in the last cell of the previous row
     oLastCell.FormFields(1).Select
     With Dialogs(wdDialogFormFieldOptions)
          .Exit = "" 'and remove the exit macro
          .Execute 'apply the changes
          'but note that this clears the value from the cell
     End With
     oLastCell.FormFields(1).Result = sResult 'so restore the result of the cell
     .Protect NoReset:=True, Password:=sPassword, _
     Type:=wdAllowOnlyFormFields 'Reprotect the form
     .FormFields("Col1Row" & CurRow).Select 'and select the next field to be completed
End With
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

 

Repeat a block of formatted text and form fields based upon the content of another form field

 

In the following example a user wished to provide a variable number of fields and associated formatted text dependant on a figure input into a form field, as in the following illustration. For the purpose of the exercise, the control field 'Mortgages' was set as a number field, the following macro was run on exit from that field, and the section to be repeated was bookmarked with the name 'bMortgage'.

Note that some of the text is underlined, some is emboldened and some both underlined and emboldened. There are eight fields in the selection, each of which will be provided with a new bookmark name. The bookmark names of the fields in that marked section will be replaced by the macro, with the names "MortTextn" where 'i' is a numeric digit from 1 to 8. Subsequent iterations will be numbered from 8 - 16, 16 -24 etc.

The user may return to the Mortgages field and change the number and the number of entries will be readjusted to take account of that changed number.

Note:

In the illustration, bookmarks are displayed to aid understanding.

 

Sub Mortgages()
Dim sNum As Long
Dim oRng As Range
Dim bProtected As Boolean
Dim fCount As Long
Dim i As Long
Dim j As Long

'Unprotect the file
If ActiveDocument.ProtectionType <> wdNoProtection Then
     bProtected = True
     ActiveDocument.Unprotect Password:=""
End If
sNum = ActiveDocument.FormFields("Mortgages").Result
Selection.GoTo What:=wdGoToBookmark, Name:="bMortgage"
'Define the range object. This is where the cursor is at (start point)
Set oRng = Selection.Range
For i = 1 To sNum
     With Selection
     'Insert two blank lines
          .TypeParagraph
         
.TypeParagraph
          'Set the underline option
          .Font.Underline = wdUnderlineSingle
          'Set the bold option
          .Font.Bold = False
          'Ensure that the paragraphs stay together on the same page
          .ParagraphFormat.KeepWithNext = True
          .TypeText "Plaintiff: "
          .Font.Underline = wdUnderlineNone
          'Add a form field
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          .TypeParagraph
          .TypeParagraph
          .Font.Underline = wdUnderlineSingle
          .Font.Bold = True
          .TypeText "Mortgage:"
          'Turn off the underline
          .Font.Underline = wdUnderlineNone
          'Turn off the bold option
          .Font.Bold = False
          .TypeParagraph
          .TypeParagraph
          .TypeText "From: "
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          .TypeParagraph
          .TypeParagraph
          .TypeText "To: "
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          .TypeParagraph
          .TypeParagraph
          .Font.Bold = True
          .TypeText "Dated: "
          .Font.Bold = False
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          .TypeText Chr(32) 'Add a Space
          .Font.Bold = True
          .TypeText "Recorded: "
          .Font.Bold = False
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          .TypeText Chr(32)
          .Font.Bold = True
          .TypeText "OR Book: "
          .Font.Bold = False
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          .TypeText Chr(32)
          .Font.Bold = True
          .TypeText "Page: "
          .Font.Bold = False
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          .TypeText Chr(32)
          .Font.Bold = True
          .TypeText "Original Amount: $"
          .Font.Bold = False
          .FormFields.Add Range:=Selection.Range, Type:=wdFieldFormTextInput
          'Turn off the Keep with next option
          .ParagraphFormat.KeepWithNext = False
          .TypeParagraph
          .TypeParagraph
     End With
Next i
'The selection has moved down the page. Redefine the end of the range object.
oRng.End = Selection.Range.End
'Recreate the bookmark.
ActiveDocument.Bookmarks.Add "bMortgage", oRng
'Count the form fields added to the range
fCount = oRng.FormFields.Count
'Give each of the added fields a unique bookmark name
For j = 1 To fCount
     With oRng.FormFields(j)
          .Name = "MortText" & j 'Add a unique bookmark name
          .Enabled = True 'Enable the field for user entry
          .CalculateOnExit = False 'Uncheck the calculate on exit check box
     End With
Next j
'Reprotect the document.
If bProtected = True Then
     ActiveDocument.Protect _
     Type:=wdAllowOnlyFormFields, _
     NoReset:=True, _
     Password:=""
End If
'Select the first of the fields in the range
ActiveDocument.FormFields("MortText1").Select
End Sub
 

Colour a form field check box with a contrasting colour when it is checked.

 

In the following illustration the checked form field check box is coloured red. This can be achieved by checking the value of the check box and formatting the check box when the value is 'True'. The code uses the function Private Function GetCurrentFF() As Word.FormField used in the Bar Chart example above to get the current field name, so that the same macro can be applied to the On Exit property of each check box field.

 

 

Private mstrFF As String
Sub EmphasiseCheckedBox()
Dim oFld As FormFields
Dim sCount 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 field is current
     mstrFF = GetCurrentFF.name
End With

Set oFld = ActiveDocument.FormFields
sCount = oFld(mstrFF).CheckBox.Value 'Get the Checkbox field value
'Check if the document is protected and if so unprotect it

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

With oFld(mstrFF).Range
     If sCount = True Then
          .Font.Color = wdColorRed 'Set the colour of the checked box
     Else
          .Font.Color = wdColorAutomatic 'Set the colour of the unchecked box
     End If
End With

 

'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
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

 

Count times entered into a document

 

A user in a Word newsgroup asked how to total the number of times associated with documented sound bites grouped in sections, similar to that shown in the illustration below. The macro below will count all the times in the format HH:MM:SS in the section where the cursor is located.

 

Sub CountTimes()
'Totals times in the current section
'Times should be in the format HH:MM:SS

Dim sNum As Long
Dim oRng As Range
Dim sText As String
Dim sHr As Long
Dim sMin As Long
Dim sSec As Long

sHr = 0
sMin = 0
sSec = 0
sNum = Selection.Information(wdActiveEndSectionNumber)
Set oRng = ActiveDocument.Range

With oRng.Find
     .ClearFormatting
     .Replacement.ClearFormatting
     .Text = "[0-9]{2}:[0-9]{2}:[0-9]{2}"
     'Look for times in the format 00:00:00
     .Wrap = wdFindStop
     .MatchWildcards = True
     Do While .Execute = True
          sText = oRng.Text
          'To count the whole document, omit the next line
          If oRng.Information(wdActiveEndSectionNumber) = sNum Then
          'Split the found time into three separate numbers
          'representing hours minutes and seconds and add to
          'the previously recorded hours, minutes and seconds

               sHr = sHr + Left(sText, 2)
               sMin = sMin + Mid(sText, 4, 2)
               sSec = sSec + Right(sText, 2)
          'To count the whole document, omit the next line
          End If
     Loop
End With


If sSec > 60 Then 'Divide by 60 and add to the minutes total
     sMin = sMin + Int(sSec / 60)
     sSec = sSec Mod 60 'The remainder is the seconds
End If
If
sMin > 60 Then 'Divide by 60 and add to the hours total
     sHr = sHr + Int(sMin / 60)
     sMin = sMin Mod 60 'The remainder is the minutes
End If
MsgBox sHr & " hours" & vbCr & _
format(sMin, "00") & " Minutes" & vbCr & _
format(sSec, "00") & " Seconds"
End Sub
 

Transpose Characters

 

There is no function in Word to transpose the order of two characters - a function that has been available in some word processing software since before Windows found its way onto the home computer. The following macro attached to some suitable keyboard shortcut will correct that omission. The macro works with either two selected characters or the characters either side of the cursor. The macro also takes account of the case of the transposed characters. If the first character to be transposed is upper case and the second not, then after transposition the first character will be upper case and the second lower case. Where both or neither characters are upper case, the case of the characters is retained.

 

Sub Transpose()
Dim oRng As Range
Dim sText As String
Dim
Msg1 As String
Dim
Msg2 As String
Dim
Msg3 As String
Dim
MsgTitle As String
Msg1 = "You must place the cursor between " & _
"the 2 characters to be transposed!"
Msg2 = "There are no characters to transpose?"
Msg3 = "There is no document open!"
MsgTitle = "Transpose Characters"
On Error GoTo ErrorHandler
If ActiveDocument.Characters.Count > 2 Then
     Set oRng = Selection.Range
     Select Case Len(oRng)
     Case Is = 0
          If oRng.Start = oRng.Paragraphs(1).Range.Start Then
               MsgBox Msg1, vbCritical, MsgTitle
               Exit Sub
          End If
          If
oRng.End = oRng.Paragraphs(1).Range.End - 1 Then
               MsgBox Msg1, vbCritical, MsgTitle
               Exit Sub
          End If
          With
oRng
               .Start = .Start - 1
               .End = .End + 1
               .Select
               sText = .Text
          End With
     Case Is
= 1
          MsgBox Msg1, vbCritical, MsgTitle
          Exit Sub
     Case Is
= 2
          sText = Selection.Range.Text
     Case Else
          MsgBox Msg1, vbCritical, MsgTitle
          Exit Sub
     End Select
     With
Selection
          If .Range.Characters(1).Case = 1 _
          And .Range.Characters(2).Case = 0 Then
               .Text = UCase(Mid(sText, 2, 1)) & _
               LCase(Mid(sText, 1, 1))
          Else
               .Text = Mid(sText, 2, 1) & _
               Mid(sText, 1, 1)
          End If
          .Collapse wdCollapseEnd
          .Move wdCharacter, -1
     End With
Else

     MsgBox Msg2, vbCritical, MsgTitle
End If
ErrorHandler:
If Err.Number = 4248 Then
MsgBox Msg3, vbCritical, MsgTitle
End If
End Sub

Insert Autotext Entry with VBA - Word to 2003

 

When you request an autotext entry, Word looks into the active template first, then in add in templates and finally in the normal template. This appears rather complex to arrange with vba.

While you can insert an autotext entry using vba from the attached (active) template or from the normal template, if it is present relatively simply, provided you know its location and it is present. The problems arise when the location is not known or may not be present. The following macro first checks whether the document template is the normal template. If it is not, then the document template is checked for the entry. If it is or if the entry has not been found, the macro then checks all installed add-ins. Finally, if the normal template was not checked in the first step, it is now checked. If the entry is found in any of these locations the entry is inserted and the macro quits. If not the user is given a message to that effect.

 

Sub InsertMyAutotext()
Dim oAT As AutoTextEntry
Dim oTemplate As Template
Dim oAddin As AddIn
Dim strText As String
Dim
bFound As Boolean

'Define the required autotext entry
strText = "AutoText Name"
'Set the found flag default to False
bFound = False
'Ignore the attached template for now if the
'document is based on the normal template

If ActiveDocument.AttachedTemplate <> NormalTemplate Then
     Set oTemplate = ActiveDocument.AttachedTemplate
     'Check each autotext entry in the attached template
     For Each oAT In oTemplate.AutoTextEntries
          'Look for the autotext name
          If oAT.name = strText Then 'if found insert it
               oTemplate.AutoTextEntries(strText).Insert _
                 Where:=Selection.Range
               'Set the found flag to true
               bFound = True
               'Clean up and stop looking
               Set oTemplate = Nothing
               Exit Sub
          End If
     Next
oAT
End If
 

'Autotext entry was not found
If bFound = False Then
     For Each oAddin In AddIns
          'Check currently loaded add-ins
          If oAddin.Installed = False Then Exit For
          Set oTemplate = Templates(oAddin.Path & _
            Application.PathSeparator & oAddin.name)
          'Check each autotext entry in the current attached template
          For Each oAT In oTemplate.AutoTextEntries
               If oAT.name = strText Then 'if found insert it
                    oTemplate.AutoTextEntries(strText).Insert _
                      Where:=Selection.Range
                    'Set the found flag to true
                    bFound = True
                    'Clean up and stop looking
                    Set oTemplate = Nothing
                    Exit Sub
               End If
          Next
oAT
     Next oAddin
End If

'The entry has not been found check the normal template
If bFound = False Then
     For
Each oAT In NormalTemplate.AutoTextEntries
          If oAT.name = strText Then
               NormalTemplate.AutoTextEntries(strText).Insert _
                 Where:=Selection.Range
               bFound = True
               Exit For
          End If
     Next
oAT
End If
 

'All sources have been checked and the entry is still not found
If bFound = False Then 'so tell the user.
     MsgBox "Entry not found", vbInformation, "Autotext " _
       & Chr(145) & strText & Chr(146)
End If
End Su
b

Insert Autotext Entry with VBA - Word 2007/2010

 

Word 2007 introduced building blocks which added a whole lot of other parameters and a separate building blocks template where autotext entries could be stored. Provided the autotext entry that you wish to insert is defined in the autotext gallery (or it is included in a Word 97-2003 format template or add-in, then the above macro will work as it stands. If you want to check all the galleries, then you will need some extra code. In addition to checking the active template, add-in templates and the normal template, the following finally looks in the building blocks.dotx template.

It is to be hoped that if you are using vba to insert entries, you might have a better idea of where they are stored beforehand, but this macro should do the trick wherever they are.

 

Sub InsertMyBuildingBlock()
Dim strText As String
Dim oTemplate As Template
Dim oAddin As AddIn
Dim bFound As Boolean
Dim
i As Long

'Define the required building block entry
strText = "Building Block Name"

'Set the found flag default to False
bFound = False
'Ignore the attached template for now if the
'document is based on the normal template

If ActiveDocument.AttachedTemplate <> NormalTemplate Then
     Set oTemplate = ActiveDocument.AttachedTemplate
     'Check each building block entry in the attached template
     For i = 1 To oTemplate.BuildingBlockEntries.Count
          'Look for the building block name
          'and if found, insert it.

          If oTemplate.BuildingBlockEntries(i).name = strText Then
               oTemplate.BuildingBlockEntries(strText).Insert _
                 Where:=Selection.Range
               'Set the found flag to true
               bFound = True
               'Clean up and stop looking
               Set oTemplate = Nothing
               Exit Sub
          End If
     Next
i
End If
 

'The entry has not been found
If bFound = False Then
     For Each
oAddin In AddIns
          'Check currently loaded add-ins
          If oAddin.Installed = False Then Exit For
          Set
oTemplate = Templates(oAddin.Path & _
            Application.PathSeparator & oAddin.name)
          'Check each building block entry in the each add in
          For i = 1 To oTemplate.BuildingBlockEntries.Count
               If oTemplate.BuildingBlockEntries(i).name = strText Then
                    'Look for the building block name
                    'and if found, insert it.

                    oTemplate.BuildingBlockEntries(strText).Insert _
                      Where:=Selection.Range
                    'Set the found flag to true
                    bFound = True
                    'Clean up and stop looking
                    Set oTemplate = Nothing
                    Exit Sub
               End If
          Next
i
     Next oAddin
End If
 

'The entry has not been found. Check the normal template
If bFound = False Then
     For
i = 1 To NormalTemplate.BuildingBlockEntries.Count
          If NormalTemplate.BuildingBlockEntries(i).name = strText Then
               NormalTemplate.BuildingBlockEntries(strText).Insert _
                 Where:=Selection.Range
               'set the found flag to true
               bFound = True
               Exit Sub
          End If
     Next
i
End If
 

'If the entry has still not been found
'finally check the Building Blocks.dotx template

If bFound = False Then

     Templates.LoadBuildingBlocks
     For Each
oTemplate In Templates
          If oTemplate.name = "Building Blocks.dotx" Then Exit For
     Next
     For
i = 1 To Templates(oTemplate.FullName).BuildingBlockEntries.Count
          If Templates(oTemplate.FullName).BuildingBlockEntries(i).name = strText Then
               Templates(oTemplate.FullName).BuildingBlockEntries(strText).Insert _
                 Where:=Selection.Range
               'set the found flag to true
               bFound = True
               'Clean up and stop looking
               Set oTemplate = Nothing
               Exit Sub
          End If
     Next
i
End If

'All sources have been checked and the entry is still not found
If bFound = False Then 'so tell the user.
     MsgBox "Entry not found", vbInformation, "Building Block " _
       & Chr(145) & strText & Chr(146)
End If
End Sub

 

Replace various words or phrases in a document from a table list, with a choice of replacements

 

It is fairly straightforward to use vba to search for a series of words or phrases from an array or from a document containing a table (or a list comprising each item in a separate paragraph) and then either processing the found words or replacing them with a corresponding word in the adjacent column of the same table. The following examples will do each of those things:

Replace a list of words from an array

 

The following example uses a pair of arrays to hold corresponding lists of words, characters or phrases separated by commas. Items from the first list are replaced with the corresponding item from the second list

vFindText = Array(Chr(147), Chr(148), Chr(145), Chr(146))
vReplText = Array(Chr(34), Chr(34), Chr(39), Chr(39))

In a practical use of the technique, the above example as shown in the macro below, is used to replace smart quotes with straight quotes (and vice versa), however the list and macro could be modified to be used to replace or process any sequence of words or phrases.

 

Sub ReplaceQuotes()
Dim vFindText As Variant
Dim vReplText As Variant
Dim sFormat As Boolean
Dim sQuotes As String
Dim
i As Long

'Ask the user whether to format with smart or straight quotes
sQuotes = MsgBox("Click 'Yes' to convert smart quotes to straight quotes." & vbCr & _
"Click 'No' to convert straight quotes to smart quotes.", _
vbYesNo, "Convert quotes")

'Record the current setting of the autoformat option to replace straight quotes with smart quotes
sFormat = Options.AutoFormatAsYouTypeReplaceQuotes


If sQuotes = vbYes Then 'The user has clicked 'Yes'

     'Define the lists of smart quotes and their replacements
     vFindText = Array(Chr(147), Chr(148), Chr(145), Chr(146))
     vReplText = Array(Chr(34), Chr(34), Chr(39), Chr(39))

     'Set the autoformat option to replace straight quotes with smart quotes to off
     Options.AutoFormatAsYouTypeReplaceQuotes = False

     'Start from the top of the document
     Selection.HomeKey wdStory
     With Selection.Find
          .Forward = True
          .Wrap = wdFindContinue
          .MatchWholeWord = True
          .MatchWildcards = False
          .MatchSoundsLike = False
          .MatchAllWordForms = False
          .Format = True
          .MatchCase = True

          'replace each item from the first array with the corresponding item in the second array
          For i = LBound(vFindText) To UBound(vFindText)
               .Text = vFindText(i)
               .Replacement.Text = vReplText(i)
               .Execute Replace:=wdReplaceAll
          Next i
     End With
Else
'User clicked 'No'

     'Use autoformat to replace straight quotes with smart quotes

     Options.AutoFormatReplaceQuotes = True
     Selection.Range.AutoFormat
End If

'Finally reset the autoformat setting to its start configuration
Options.AutoFormatAsYouTypeReplaceQuotes = sFormat
End Sub

Replace a list of words from a table

 

In the following example, the words and their replacements are stored in adjacent columns of a two column table stored in a document - here called "changes.doc". The name is unimportant and Word 2007/2010 users could use docx format. The table could also have more than two columns, but only the first two columns are used.

 

Sub ReplaceFromTableList()
Dim ChangeDoc, RefDoc As Document
Dim cTable As Table
Dim oFind, oReplace As Range
Dim i As Long
Dim sFname As String
'Define the document containing the table of words/phrases and their replacements
sFname = "D:\My Documents\Test\changes.doc"

'Define the document to be processed
Set RefDoc = ActiveDocument

'Open the document with the changes
Set ChangeDoc = Documents.Open(sFname)

'Define the table to be used
Set cTable = ChangeDoc.Tables(1)

'Activate the document to be processed
RefDoc.Activate
For i = 1 To cTable.Rows.Count

     'Define the cell containing the word/phrase to be replaced
     Set oFind = cTable.Cell(i, 1).Range
     oFind.End = oFind.End - 1

     'Define the cell containing the replacement word/phrase
     Set oReplace = cTable.Cell(i, 2).Range
     oReplace.End = oReplace.End - 1
     With Selection

          'Start at the top of the document
          .HomeKey wdStory

          'Replace the words/phrases
          With .Find
               .ClearFormatting
               .Replacement.ClearFormatting
               .Execute findText:=oFind, _
                 ReplaceWith:=oReplace, _
                 Replace:=wdReplaceAll, _
                 MatchWholeWord:=True, _
                 MatchWildcards:=False, _
                 MatchCase:=True, _

                 Forward:=True, _
                 Wrap:=wdFindContinue
          End With
     End With
Next
i

'Close the document with the table
ChangeDoc.Close wdDoNotSaveChanges

End Sub

Replace a list of words from a table and offer a choice of replacements

 

The final example in this trilogy of replacements using lists was prompted by a newsgroup question. The user wanted initially to highlight words and phrases in a document from a list, which was easily achieved using a variation of one of the above macros, and the he ventured the possibility of the user choosing from a number of possible replacements. How practical this is in a real life situation I cannot say, but the principles involved I felt were interesting enough to repeat them here.

In this instance the macro uses a multi-column table. The first column contains the words to be located, the subsequent columns contain the replacement choices. The columns should be filled from left to right. Not all the columns (except the first) need contain any data, but the columns must be filled from left to right with no gaps.

If only the second column has data, the found item is replaced with the content of the second column

If more columns to the right of the second column have data, the choices from the second and subsequent columns are presented as numbered choices in a list.

If none of the columns, except the first, contains data, then the found word is merely highlighted.

There must be no empty cells in the first column!

 

Sub ReplaceFromTableChoices()
Dim ChangeDoc, RefDoc As Document
Dim cTable As Table
Dim oldPart, newPart, oFound As Range
Dim i, j, iCol As Long
Dim sFname, sReplaceText, sNum As String
'Define the document containing the table of changes.

'The table must have at least 3 columns.
sFname = "D:\My Documents\Test\changes2.doc"

'Define the document to be processed
Set RefDoc = ActiveDocument
Set ChangeDoc = Documents.Open(sFname)

'Define the table to be used
Set cTable = ChangeDoc.Tables(1)

'Activate the document to be processed
RefDoc.Activate

'Process each row of the table in turn

For i = 1 To cTable.Rows.Count

     'Set the search item to the content of the first cell
     Set oldPart = cTable.Cell(i, 1).Range

     'Remove the cell end character from the range
     oldPart.End = oldPart.End - 1

     'Start from the beginning of the document
     With Selection
          .HomeKey wdStory
          With .Find
               .ClearFormatting
               .Replacement.ClearFormatting
               .MatchWholeWord = True
               .MatchCase = True

               'Look for the search item
               Do While .Execute(findText:=oldPart)

                    'And assign the found item to a range variable
                    Set oFound = Selection.Range

                    'Set the start number of a counter
                    iCol = 1

                    'Set a temporary replacement text string to zero length
                    sReplaceText = ""

                    'Look into the remaining columns for replacement choices
                    For j = 2 To cTable.Columns.Count

                         'And assign the replacement choices to a range variable in turn
                         Set newPart = cTable.Cell(i, j).Range

                         'Remove the cell end character from the range
                         newPart.End = newPart.End - 1

                         'If the current cell has no content, ignore the remainder
                         If Len(newPart) = 0 Then Exit For

                         'Add the range content to the temporary replacement text string
                         sReplaceText = sReplaceText & iCol & ". " & _
                         newPart.Text & vbCr

                         'Increment the counter
                         iCol = iCol + 1
                    Next j

                     'If there is a replacement available
                    If Len(sReplaceText) <> 0 Then

                         'If there is only one such replacement
                         If Len(cTable.Cell(i, 2).Range) <> 2 And _
                         Len(cTable.Cell(i, 3).Range) = 2 Then
                              'Set the number of that replacement to 1

                              sNum = "1"
                         Else
Again: 'Add a label to mark the start of the user input

                              'If there is more than one choice,

                              'ask the user to pick the preferred replacement
                              sNum = InputBox(sReplaceText & vbCr & vbCr & _
                              "Enter the number of the replacement for '" _
                              & oldPart.Text & "'")
                              If sNum = "" Then Exit Sub 'The user has cancelled

                              'Error trap inappropriate user choices

                              'Check if the user has entered something other than a number
                              If IsNumeric(sNum) = False Then

                                   'Tell the user
                                   MsgBox "Invalid entry! Try again.", _
                                   vbInformation, "Error"

                                   'and go round again
                                   GoTo Again
                              End If

                              'Check if the user has entered a number

                              'higher than the number of columns in the table
                              If sNum > cTable.Columns.Count Then

                                   'Tell the user
                                   MsgBox "Invalid entry! Try again.", _
                                   vbInformation, "Error"

                                   'and go round again
                                   GoTo Again
                              End If

                              'Check if a user has picked a valid number

                              'higher than the available choices
                              If Len(cTable.Cell(i, sNum + 1).Range) = 2 Then
                                  
'Tell the user

                                   MsgBox "Invalid entry! Try again.", _
                                   vbInformation, "Error"

                                   'and go round again
                                   GoTo Again
                              End If
                         End If

                         'Set the replacement according to the user input
                         Set newPart = cTable.Cell(i, sNum + 1).Range
                         newPart.End = newPart.End - 1
                         oFound.Text = newPart.Text
                    Else

                         'There are no replacements so highlight the found item
                         oFound.HighlightColorIndex = wdYellow
                    End If
               Loop
          End With
     End With
Next
i

'Close the document containing the table
ChangeDoc.Close wdDoNotSaveChanges
End Sub
 

Add tabs to centre of text area and right margin

 

The following macro sets a centre aligned tab, centred between the current margins, and a right aligned tab at the right margin.

 

Sub AddTabs()
Dim iLeft As Long
Dim
iRight As Long
Dim
iCentre As Long
Dim
iWidth As Long
With
Selection
     iLeft = .Sections(1).PageSetup.LeftMargin
     iRight = .Sections(1).PageSetup.RightMargin
     iWidth = .Sections(1).PageSetup.PageWidth
     iCentre = (iWidth - iLeft - iRight) / 2
     .ParagraphFormat.TabStops.Add Position:=iCentre, _
        Alignment:=wdAlignTabCenter, _

        Leader:=wdTabLeaderSpaces
     .ParagraphFormat.TabStops.Add Position:=iWidth - (iRight + iLeft), _
        Alignment:=wdAlignTabRight, _

        Leader:=wdTabLeaderSpaces
End With
End Sub