You are on page 1of 49

http://www.gmayor.

com/
word_vba_examples.htm
Some useful macro examples - page 1
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 2} +{Tab 2}"
Dialogs(wdDialogInsertField).Show
lbl_Exit:
Exit Sub
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.

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 iSwitch As Long
Dim strChoice As String
SendKeys "{Tab 2} +{Tab 2}"
Dialogs(wdDialogInsertField).Show
On Error GoTo lbl_Exit 'User has cancelled
Set oRng = Selection.Range
oRng.Start = oRng.Start - 1
With oRng.Fields(1)
If InStr(1, .Code, "MERGEFORMAT") <> 0 Then
iSwitch = MsgBox("Use charformat in place of the mergeformat
switch?", _
vbYesNo, _
"Insert Field")
If iSwitch = vbYes Then
.Code.Text = Replace(.Code.Text, _
"MERGEFORMAT", _
"CHARFORMAT")
End If
End If
.Update
End With
oRng.End = oRng.End + 1
oRng.Collapse 0
oRng.Select
lbl_Exit:
Exit Sub
End Sub

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". 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

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

Sub InsertField()
Dim oRng As Range
Dim i As Variant
Dim iSwitch As Long
Dim strChoice As String
Dialogs(wdDialogInsertField).Show
On Error GoTo lbl_Exit 'User has cancelled
Set oRng = Selection.Range
oRng.Start = oRng.Start - 1
With oRng.Fields(1)
If InStr(1, .Code, "MERGEFORMAT") <> 0 Then
iSwitch = MsgBox("Use charformat in place of the mergeformat
switch?", _
vbYesNo, _
"Insert Field")
If iSwitch = vbYes Then
.Code.Text = Replace(.Code.Text, _
"MERGEFORMAT", _
"CHARFORMAT")
ElseIf iSwitch = vbNo Then
iSwitch = MsgBox("Remove switch?", _
vbYesNo, _
"Insert Field")
If iSwitch = vbYes Then
.Code.Text = Replace(.Code.Text, _
" \* MERGEFORMAT ", _
"")
End If
End If
End If
.Update
End With
oRng.End = oRng.End + 1
oRng.Collapse 0
oRng.Select
lbl_Exit:
Exit Sub
End Sub

The Vista version of the macro has two message boxes. One of them is
identical to the previous 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

Option Explicit

Sub AddNoFromINIFileToBookmark()
Dim SettingsFile As String
Dim Order As String
Dim rRecNo As Range
Dim strCount As String
Dim i As Long
On Error GoTo err_Handler
strCount = InputBox("Print how many copies?", _
"Print Numbered Copies", 1)
If strCount = "" Then GoTo lbl_Exit
SettingsFile = Options.DefaultFilePath(wdStartupPath) & "\
Settings.ini"
Order = System.PrivateProfileString(SettingsFile, "DocNumber",
"Order")
If Order = "" Then
Order = 1
End If
For i = 1 To Val(strCount)
With ActiveDocument
Set rRecNo = .Bookmarks("RecNo").Range
FillBM "RecNo", Format(Order, "00000")
.Fields.Update
.ActiveWindow.View.ShowFieldCodes = False
.PrintOut
End With
Order = Order + 1
Next i
System.PrivateProfileString(SettingsFile, "DocNumber", "Order") =
Order
lbl_Exit:
Exit Sub
err_Handler:
If Err.Number = 5941 Then
MsgBox "The bookmark 'RecNo' is not in the current document."
End If
GoTo lbl_Exit
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
lbl_Exit:
Exit Sub
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.

Option Explicit

Sub AutoNew()
Dim SettingsFile As String
Dim Order As String
Dim rRecNo As Range
On Error GoTo err_Handler
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
FillBM "RecNo", Format(Order, "00000")
Order = Order + 1
System.PrivateProfileString(SettingsFile, "DocNumber", "Order") =
Order
lbl_Exit:
Exit Sub
err_Handler:
If Err.Number = 5941 Then
MsgBox "The bookmark 'RecNo' is not in the current document."
End If
GoTo lbl_Exit
End Sub

The two previous macros use the following sub to fill the bookmark with the
value. This sub may be used to fill any named bookmark with a supplied
value.

Public Sub FillBM(strBMName As String, strValue As String)


Dim oRng As Range
With ActiveDocument
On Error GoTo lbl_Exit
Set oRng = .Bookmarks(strBMName).Range
oRng.Text = strValue
oRng.Bookmarks.Add strBMName
End With
lbl_Exit:
Exit Sub
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 err_Handler
Selection.PasteSpecial _
DataType:=wdPasteText, _
Placement:=wdInLine
lbl_Exit:
Exit Sub
err_Handler:
Beep
GoTo lbl_Exit
End Sub
 

Copy footnotes c/w formatting to a new document


Sub CopyFootnotes()
Dim oSourceDoc As Document
Dim oTargetDoc As Document
Dim strID As String
Dim i As Long
Dim oFNote As Range
Dim oRng As Range
Set oSourceDoc = ActiveDocument
Set oTargetDoc = Documents.Add
For i = 1 To oSourceDoc.Footnotes.Count
strID = oSourceDoc.Footnotes(i).Index
Set oFNote = oSourceDoc.Footnotes(i).Range
Set oRng = oTargetDoc.Range
oRng.Collapse 0
With oRng
.Collapse 0
.Text = strID & Chr(32)
.Style = "Footnote Text"
.Font.Superscript = True
.Collapse 0
.Font.Superscript = False
.FormattedText = oFNote.FormattedText
End With
Next i
lbl_Exit:
Exit Sub
End Sub

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

Sub Send_As_Mail_Attachment()
' Send the document as an attachment
' in an Outlook Email message
Dim oOutlookApp As Object
Dim olInsp As Object
Dim wdDoc As Object
Dim oRng As Object
Dim oItem As Object

Const strTo As String = "someone@somewhere.com"


Const strSubject As String = "This is the subject"
Const strMsg As String = "This is the message body. Default
signature will be retained."

On Error Resume Next


'Prompt the user to save the document
ActiveDocument.Save
'Get Outlook if it's running
Set oOutlookApp = GetObject(, "Outlook.Application")
'Outlook wasn't running, start it from code
If Err <> 0 Then
Set oOutlookApp = CreateObject("Outlook.Application")
End If
On Error GoTo err_Handler
'Create a new mailitem
Set oItem = oOutlookApp.CreateItem(0)
With oItem
.to = strTo
.Subject = strSubject
.Attachments.Add ActiveDocument.FullName
.BodyFormat = 2 'html format
Set olInsp = .GetInspector
Set wdDoc = olInsp.WordEditor 'The message body editor
Set oRng = wdDoc.Range(0, 0) 'The start of the message body
oRng.Text = strMsg 'The accompanying message text
.Display 'This line must be retained
'.Send 'Remove leading apostrophe after testing
End With
'Clean up
Set oItem = Nothing
Set olInsp = Nothing
Set wdDoc = Nothing
Set oRng = Nothing
Set oOutlookApp = Nothing
lbl_Exit:
Exit Sub
err_Handler:
GoTo lbl_Exit
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 - https://support.microsoft.com/en-us/help/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.

Option Explicit

Sub ToggleSQLSecurity()
Dim WSHShell As Object
Dim RegKey As String
Dim rKeyWord As String
Dim wVer As String
If Val(Application.Version) < 10 Then
'The security issue relates to
'Word versions from 10.0 (Word 2002)
MsgBox "This macro is for Word 2002 and later!", vbOKOnly, "Wrong
Word Version"
Exit Sub
End If
wVer = Val(Application.Version) & ".0"
Set WSHShell = CreateObject("WScript.Shell")
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
lbl_Exit:
Exit Sub
End Sub

http://www.gmayor.com/word_vba_examples_2.htm

Word macros - page 2


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 acorresponding 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.

Option Explicit

Sub TrueTitleCase()
'Graham Mayor - https://www.gmayor.com - Last updated - 18 Mar
2022
Dim rSel As Range, oRng As Range
Dim vFindText As Variant
Dim vReplText As Variant
Dim i As Long
Dim k As Long

Set oRng = Selection.Range


Set rSel = Selection.Range
'count the characters in the selected string
k = Len(rSel)
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
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 rSel
.Case = wdTitleWord
'omit the first word
.MoveStart unit:=wdWord, Count:=1
'list the exceptions to look for in an array
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
End With
'Optionally capitalize the first character of any word following
a colon
FixColon oRng
lbl_Exit:
Set rSel = Nothing
Set oRng = Nothing
Exit Sub
End Sub

Private Sub FixColon(oRng As Range)


Const sList As String =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim rSel As Range
oRng.Select
Set rSel = Selection.Range
With rSel.Find
.Text = ":"
Do While .Execute
If rSel.InRange(oRng) = False Then Exit Do
rSel.MoveStartUntil sList
rSel.Characters(1).Case = wdUpperCase
rSel.Collapse 0
Loop
End With
oRng.Select
lbl_Exit:
Exit Sub
End Sub

 
 

Extract acronyms to a new document

A forum 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.

Option Explicit

Sub ExtractAcronyms()
Dim rText As Range
Dim oSourceDoc As Document
Dim oTargetDoc As Document
Set oSourceDoc = ActiveDocument
Set oTargetDoc = Documents.Add
oSourceDoc.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
oTargetDoc.Range.InsertAfter rText & vbCr
rText.Collapse wdCollapseEnd
Loop
End With
End With
With oTargetDoc
.Range.Sort ExcludeHeader:=False, _
FieldNumber:="Paragraphs", _
SortFieldType:=wdSortFieldAlphanumeric, _
SortOrder:=wdSortOrderAscending
.Paragraphs(1).Range.Delete
.Activate
End With
lbl_Exit:
Exit Sub
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 H 2O 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.

Option Explicit

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
lbl_Exit:
Exit Sub
End Sub
 

or even simpler
Option Explicit

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
lbl_Exit:
Exit Sub
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 required results.

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.

Option Explicit

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
.Characters(2).Font.Subscript = True
Case Is = 1
'CO2
.Characters(3).Font.Subscript = True
Case Is = 2
'H2SO4
.Characters(2).Font.Subscript = True
.Characters(5).Font.Subscript = True
Case Is = 3
'SO42
.Characters(3).Font.Subscript = True
.Characters(4).Font.Superscript = True
.Characters(5).Font.Superscript = True
Case Is = 4
'[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 With
Next i
lbl_Exit:
Exit Sub
End Sub
 

Add a row to a table in a protected form

The following macro when run on exit from the last formfield in the last row of
the table 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 code here can be applied to multiple tables in the same form.

Option Explicit

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 oNewRow As Range
Dim oCell As Range
Dim oLastCell As Range
Dim sResult As String
Dim iRow As Long
Dim iCol As Long
Dim CurRow As Long
Dim i As Long, j As Long
Dim sPassword As String
sPassword = ""
'password to protect/unprotect form
With ActiveDocument
.Unprotect Password:=sPassword
'Unprotect document
Set oTable = Selection.Tables(1)
'Establish which table the cursor is in
For j = 1 To .Tables.Count
If oTable.Range.Start = .Tables(j).Range.Start Then
'Table is j
Exit For 'Stop checking
End If
Next j
'Select the appropriate table
iCol = oTable.Columns.Count 'Record the last column number
'Set a range to the last cell0
Set oLastCell = oTable.Cell(iRow, iCol).Range
'Record the last cell field value
Select Case oLastCell.FormFields(1).Type
Case 70 'wdFieldFormTextInput
sResult = oLastCell.FormFields(1).Result
Case 71 'wdFieldFormCheckBox
sResult = oLastCell.FormFields(1).CheckBox.Value
Case 83 'wdFieldFormDropDown
sResult = oLastCell.FormFields(1).DropDown.Value
End Select
'Get the value in the last cell
Set oRng = oTable.Rows.Last.Range
'Add the last row to a range
Set oNewRow = oTable.Rows.Last.Range 'Add the last row to another
range
oNewRow.Collapse wdCollapseEnd 'Collapse the second range to the
end of the table
oNewRow.FormattedText = oRng
'insert the content of the last row into the new range
'thereby adding a new row with the same content as the last row
CurRow = oTable.Rows.Count 'Determine the new last row of the
table
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 = "Tab" & j & "Col" & i & "Row" & CurRow 'eg Tab1Col1Row2
.Execute 'apply the changes
End With
Next i
'Select the formfield in the last cell of the previous row
oLastCell.FormFields(1).Select
'Remove the macro
oLastCell.FormFields(1).ExitMacro = ""
'Restore the field value according to type
Select Case oLastCell.FormFields(1).Type
Case 70
oLastCell.FormFields(1).Result = sResult
Case 71
oLastCell.FormFields(1).CheckBox.Value = sResult
Case 83
oLastCell.FormFields(1).DropDown.Value = sResult
End Select
.Protect NoReset:=True, _
Password:=sPassword, _
Type:=wdAllowOnlyFormFields
'Reprotect the form
.FormFields("Tab" & j & "Col1Row" _
& CurRow).Select 'and select the next field to be completed
End With
lbl_Exit:
Set oTable = Nothing
Set oRng = Nothing
Set oNewRow = Nothing
Set oCell = Nothing
Set oLastCell = Nothing
Exit Sub
End Sub

http://www.gmayor.com/word_vba_examples_3.htm

Word macros - page 3


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.

Option Explicit
Private mstrFF As String

Sub ColTable1Row1()
Dim oFld As FormFields
Dim i As Long
Dim sCount As Long
Dim sRow As Long
Dim oCol As Long
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.Backg
roundPatternColor = wdColorWhite
Next i
If sCount = 0 Then GoTo lbl_Exit '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.Backg
roundPatternColor = oCol
Next i
lbl_Exit: 'Re-protect the form and apply the password (if
any).
If bProtected = True Then
ActiveDocument.Protect _
Type:=wdAllowOnlyFormFields, _
NoReset:=True, Password:=sPassword
End If
Exit Sub
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
lbl_Exit:
Exit Function
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.

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
lbl_Exit:
Exit Sub
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 following 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.

Option Explicit

Private mstrFF As String


Sub EmphasiseCheckedBox()
Dim oFld As FormFields
Dim sCount As Long
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
lbl_Exit:
Exit Sub
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
lbl_Exit:
Exit Function
End Function

Count times entered into a document

A user in a Word forum 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.

Option Explicit

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"
lbl_Exit:
Exit Sub
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.

Option Explicit

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 err_Handler
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
lbl_Exit:
Exit Sub
err_Handler:
If Err.Number = 4248 Then
MsgBox Msg3, vbCritical, MsgTitle
End If
GoTo lbl_Exit
End Sub

Insert Autotext Entry with VBA

When you request an autotext entry, 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.

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 first 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 second macro 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 the macro below should
do the trick wherever they are.

Option Explicit

Sub InsertMyAutotext()
Dim oAT As AutoTextEntry
Dim oTemplate As Template
Dim oAddin As AddIn
Dim bFound As Boolean
Const strAutoTextName As String = "AutoText Name" 'Define the
required autotext entry

'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 = strAutoTextName Then
'if found insert it
oTemplate.AutoTextEntries(strAutoTextName).Insert _
Where:=Selection.Range
'Set the found flag to true
bFound = True
'Clean up and stop looking
Set oTemplate = Nothing
GoTo lbl_Exit
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 GoTo Skip
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 = strAutoTextName Then
'if found insert it
oTemplate.AutoTextEntries(strAutoTextName).Insert _
Where:=Selection.Range
'Set the found flag to true
bFound = True
'Clean up and stop looking
Set oTemplate = Nothing
GoTo lbl_Exit
End If
Next oAT
Skip:
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 = strAutoTextName Then
NormalTemplate.AutoTextEntries(strAutoTextName).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
'so tell the user.
If bFound = False Then
MsgBox "Entry not found", _
vbInformation, _
"Autotext " _
& Chr(145) & strAutoTextName & Chr(146)
End If
lbl_Exit:
Exit Sub
End Sub

Insert Autotext Entry with VBA - Word 2007/2010


Option Explicit

Sub InsertMyBuildingBlock()
Dim oTemplate As Template
Dim oAddin As AddIn
Dim bFound As Boolean
Dim i As Long
'Define the required building block entry
Const strBuildingBlockName As String = "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 = strBuildingBlockName
Then
oTemplate.BuildingBlockEntries(strBuildingBlockName).Insert _
Where:=Selection.Range
'Set the found flag to true
bFound = True
'Clean up and stop looking
Set oTemplate = Nothing
GoTo lbl_Exit
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 = strBuildingBlockName
Then
'Look for the building block name
'and if found, insert it.
oTemplate.BuildingBlockEntries(strBuildingBlockName).Insert _
Where:=Selection.Range
'Set the found flag to true
bFound = True
'Clean up and stop looking
Set oTemplate = Nothing
GoTo lbl_Exit
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 =
strBuildingBlockName Then
NormalTemplate.BuildingBlockEntries(strBuildingBlockName).Insert
_
Where:=Selection.Range
'set the found flag to true
bFound = True
GoTo lbl_Exit
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 =
strBuildingBlockName Then
Templates(oTemplate.FullName).BuildingBlockEntries(strBuildingBlo
ckName).Insert _
Where:=Selection.Range
'set the found flag to true
bFound = True
'Clean up and stop looking
Set oTemplate = Nothing
GoTo lbl_Exit
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) & strBuildingBlockName & Chr(146)
End If
lbl_Exit:
Exit Sub
End Sub

http://www.gmayor.com/word_vba_examples_4.htm

Word macros - page 4


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.

Option Explicit

Sub ReplaceQuotes()
Dim vFindText As Variant
Dim vReplText As Variant
Dim sFormat As Boolean
Dim sQuotes As String
Dim oRng As Range
Dim i As Long
sQuotes = MsgBox("Click 'Yes' to convert smart quotes to straight
quotes." & vbCr & _
"Click 'No' to convert straight quotes to smart quotes.", _
vbYesNo, "Convert quotes")
sFormat = Options.AutoFormatAsYouTypeReplaceQuotes
If sQuotes = vbYes Then
vFindText = Array(Chr(132), Chr(147), Chr(148), ChrW(171) &
ChrW(160), ChrW(160) & ChrW(187), Chr(145), Chr(146))
vReplText = Array(Chr(34), Chr(34), Chr(34), Chr(34), Chr(34),
Chr(39), Chr(39))
Options.AutoFormatAsYouTypeReplaceQuotes = False
For i = LBound(vFindText) To UBound(vFindText)
Set oRng = ActiveDocument.Range
With oRng.Find
Do While .Execute(vFindText(i))
oRng.Text = vReplText(i)
oRng.Collapse wdCollapseEnd
Loop
End With
Next i
Else
Options.AutoFormatReplaceQuotes = True
ActiveDocument.Range.AutoFormat
End If
Options.AutoFormatAsYouTypeReplaceQuotes = sFormat
lbl_Exit:
Exit Sub
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.

Option Explicit

Sub ReplaceFromTableList()
Dim oChanges As Document, oDoc As Document
Dim oTable As Table
Dim oRng As Range
Dim oStory As Range
Dim rFindText As Range, rReplacement As Range
Dim i As Long
Dim sFname As String
Dim sAsk As String
sFname = "C:\Path\Changes.docx"
Set oDoc = ActiveDocument
Set oChanges = Documents.Open(Filename:=sFname, Visible:=False)
Set oTable = oChanges.Tables(1)
For i = 1 To oTable.Rows.Count
For Each oStory In oDoc.StoryRanges
Set oRng = oStory
Set rFindText = oTable.Cell(i, 1).Range
rFindText.End = rFindText.End - 1
Set rReplacement = oTable.Cell(i, 2).Range
rReplacement.End = rReplacement.End - 1
With oRng.Find
.ClearFormatting
.Replacement.ClearFormatting
Do While .Execute(findText:=rFindText, _
MatchWholeWord:=True, _
MatchWildcards:=False, _
Forward:=True, _
Wrap:=wdFindStop) = True
oRng.Select
sAsk = MsgBox("Replace - " & vbCr & oRng & vbCr + vbCr & _
"with - " & vbCr & rReplacement, vbYesNo, _
"Replace from Table")

If sAsk = vbYes Then


oRng.FormattedText = rReplacement.FormattedText
End If
oRng.Collapse wdCollapseEnd
Loop
End With
If oStory.StoryType <> wdMainTextStory Then
While Not (oStory.NextStoryRange Is Nothing)
Set oStory = oStory.NextStoryRange
Set oRng = oStory
Set rFindText = oTable.Cell(i, 1).Range
rFindText.End = rFindText.End - 1
Set rReplacement = oTable.Cell(i, 2).Range
rReplacement.End = rReplacement.End - 1
With oRng.Find
.ClearFormatting
.Replacement.ClearFormatting
Do While .Execute(findText:=rFindText, _
MatchWholeWord:=True, _
MatchWildcards:=False, _
Forward:=True, _
Wrap:=wdFindStop) = True
oRng.Select
sAsk = MsgBox("Replace - " & vbCr & oRng & vbCr + vbCr & _
"with - " & vbCr & rReplacement, vbYesNo, _
"Replace from Table")

If sAsk = vbYes Then


oRng.FormattedText = rReplacement.FormattedText
End If
oRng.Collapse wdCollapseEnd
Loop
End With
Wend
End If
Next oStory
Next i
If ActiveWindow.View.SplitSpecial <> wdPaneNone Then
ActiveWindow.Panes(2).Close
End If
If ActiveWindow.ActivePane.View.Type = wdNormalView Or
ActiveWindow. _
ActivePane.View.Type = wdOutlineView Then
ActiveWindow.ActivePane.View.Type = wdPrintView
End If
ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
If ActiveWindow.View.SplitSpecial = wdPaneNone Then
ActiveWindow.ActivePane.View.Type = wdPrintView
Else
ActiveWindow.View.Type = wdPrintView
End If
ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument
If ActiveWindow.View.SplitSpecial = wdPaneNone Then
ActiveWindow.ActivePane.View.Type = wdPrintView
Else
ActiveWindow.View.Type = wdPrintView
End If
oChanges.Close wdDoNotSaveChanges
lbl_Exit:
Set oStory = Nothing
Set oRng = Nothing
Set rFindText = Nothing
Set rReplacement = Nothing
Exit Sub
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
Word forum 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!

Option Explicit

Sub ReplaceFromTableChoices()
Dim oChangesDoc As Document
Dim oDoc As Document
Dim oTable As Table
Dim oRng As Range
Dim oldPart As Range
Dim newPart As Range
Dim oFound As Range
Dim i As Long, j As Long, iCol As Long
Dim iAsk As Long
Dim sReplaceText As String, sNum As String
Const sFname As String = "C:\Path\Changes.docx"

'Identify the document to be processed


Set oDoc = ActiveDocument
'Identify the document containing the table of changes.
'The table must have at least 3 columns.
Set oChangesDoc = Documents.Open(sFname)
'Identify the table to be used
Set oTable = oChangesDoc.Tables(1)
'Activate the document to be processed
oDoc.Activate
'Process each row of the table in turn
For i = 1 To oTable.Rows.Count
Set oRng = oDoc.Range
'Set the search item to the content of the first cell
Set oldPart = oTable.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 oRng.Find
.MatchWholeWord = True
'Look for the search item
Do While .Execute(findText:=oldPart)
'And assign the found item to a range variable
Set oFound = oRng
oFound.Select
'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 oTable.Columns.Count
'And assign the replacement choices to a range variable in turn
Set newPart = oTable.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(oTable.Cell(i, 2).Range) <> 2 And _
Len(oTable.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(Prompt:=sReplaceText & vbCr & vbCr & _
"Enter the number of the replacement for '" _
& oldPart.Text & "'", Default:=1)
If sNum = "" Then GoTo lbl_Exit 'User has click Cancel

'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 "Input must be a number between 1 & " &
oTable.Columns.Count - 1 & ". 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 > oTable.Columns.Count - 1 Then
'Tell the user
MsgBox "Input must be a number between 1 & " &
oTable.Columns.Count - 1 & ". 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(oTable.Cell(i, sNum + 1).Range) = 2 Then
'Tell the user
MsgBox "You have entered a number higher than the choices
available! Try again.", _
vbInformation, "Error"
'and go round again
GoTo Again
End If
End If
'Set the replacement according to the user input
Set newPart = oTable.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
Skip:
oRng.Collapse 0
Loop
End With
Next i
'Close the document containing the table
oChangesDoc.Close wdDoNotSaveChanges
lbl_Exit:
Set oChangesDoc = Nothing
Set oDoc = Nothing
Set oTable = Nothing
Set oRng = Nothing
Set oldPart = Nothing
Set newPart = Nothing
Set oFound = Nothing
Exit Sub
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.

Option Explicit

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
lbl_Exit:
Exit Sub
End Sub

Extract e-mail addresses from a document to a list

The following macro will extract all the e-mail addresses from a document to a
new document. The macro works on the principle that autocorrect is capable of
converting e-mail addresses and web links to hyperlinks. The macro then
examines those hyperlinks and extracts any that contain e-mail addresses to a
new document. the original document is unaffected.

Sub ExtractEMailAddresses()
Dim oSource As Document, oTarget As Document
Dim strPath As String
Dim oHLink As Hyperlink
Dim oPara As Paragraph
'Ensure there is a document to process
If Documents.Count = 0 Then
MsgBox "Open the document
with the e-mail addresses first!", _
vbCritical, _
"Extract e-mail addresses"
Exit Sub
End If
Set oSource = ActiveDocument
oSource.Save
'Store the document name and path
strPath = oSource.FullName
'Create a new document to receive the e-mail addresses
Set oTarget = Documents.Add
'Autoformat the original document to set hyperlinks
With Options
.AutoFormatReplaceHyperlinks = True
oSource.Range.AutoFormat
End With
'Ascertain which hyperlinks are to e-mail addresses
For Each oHLink In oSource.Range.Hyperlinks
If InStr(1, oHLink.Address, "mailto:") > 0 Then
'and copy them to the target document, each to a new line
With oTarget.Range
.InsertAfter oHLink.Address
.InsertAfter vbCr
End With
End If
Next oHLink
'Remove the unwanted text from the hyperlink address
oTarget.Range = Replace(oTarget.Range, "mailto:", "")
'Sort the list alphabetically
oTarget.Range.SortAscending
'Remove any blank lines
For Each oPara In oTarget.Range.Paragraphs
If Len(oPara.Range) = 1 Then oPara.Range.Delete
Next oPara
'Format the paragraphs to remove unwanted space
oTarget.Range.ParagraphFormat.SpaceAfter = 0
oTarget.Range.ParagraphFormat.SpaceBefore = 0
'Close the original document without saving to preserve the
original format
oSource.Close wdDoNotSaveChanges
'Ask whether the user wants to re-open the source document
If MsgBox("Re-open the source document?", _
vbYesNo, _
"Extract e-mail addresses") = vbYes Then
Documents.Open FileName:=strPath
End If
lbl_Exit:
Exit Sub
End Sub

Fix scrambled view of some open type fonts in Word 2010

It has been observed that occasionally some open type fonts display
incorrectly in Word 2010, as in the following illustration, or the display becomes
completely scrambled with the words all stacked together. It has also been
observed that switching to an alternative printer will fix the issue. The following
macro does just that, using the One Note driver that comes with all versions of
Office 2010, then restores the original printer driver.
Sub FixDisplay()
Dim sPrinter As String
sPrinter = ActivePrinter
ActivePrinter = "Send To OneNote 2010"
ActivePrinter = sPrinter
lbl_Exit:
Exit Sub
End Sub

Word 2010 Print Preview

2010, the print preview was changed to part of the print dialog on the File Tab
of the ribbon. The familiar print preview from earlier versions can be added to
the QAT (Quick Access Toolbar) with Print Preview Edit Mode from the All
Commands group of the QAT editor. The following macro intercepts that
function and sets the zoom level from the default of full page to 100%. Change
the 100 to reflect any personally preferred zoom level.

Sub PrintPreviewEditMode()
ActiveDocument.PrintPreview
ActiveWindow.View.Zoom = 100
lbl_Exit:
Exit Sub
End Sub

You might also like