How to modify a recorded macro

Article contributed by Jay Freedman

Lots of articles advise you to use the macro recorder to get started with Word macros. An example is our own Creating a macro with no programming experience using the recorder. Although it's good advice, the macros you record often need tweaking—and sometimes they don't work at all.

What's wrong with the recorder, anyway?

The macro recorder's job is to translate your actions into programming code, using a language called Visual Basic for Applications (VBA). That's easy to do when you simply type some words into a document, or when you give a simple command such as Edit > Copy. It's harder for the recorder to make good code for more complicated commands such as File > Open or Edit > Replace. Sometimes the recorded code doesn't do exactly what you want it to do, and in a few situations the code is incorrect.

Cleaning out unneeded dialog arguments

You can record a macro while you make a change in the document by using a dialog, such as Format > Font or Format > Paragraph. You might think the macro recorder should capture only the settings that you changed, but it doesn't—instead, it throws into the macro every setting in the whole dialog! Look at what you might get if you just change the font size to 10 pt:

Sub Macro1()

  With Selection.Font

    .Name = "Times New Roman"

    .Size = 10

    .Bold = False

    .Italic = False

    .Underline = wdUnderlineNone

    .UnderlineColor = wdColorAutomatic

    .StrikeThrough = False

    .DoubleStrikeThrough = False

    .Outline = False

    .Emboss = False

    .Shadow = False

    .Hidden = False

    .SmallCaps = False

    .AllCaps = False

    .Color = wdColorAutomatic

    .Engrave = False

    .Superscript = False

    .Subscript = False

    .Spacing = 0

    .Scaling = 100

    .Position = 0

    .Kerning = 0

    .Animation = wdAnimationNone

  End With

End Sub

 

The lines that say "With Selection.Font" and "End With" mean that the lines between them set the properties of the font of the selected text. This is a shorthand way to refer to many properties of the same item, instead of writing "Selection.Font" at the beginning of each property's name.

In this macro the one property that you changed during recording is buried among the many that you didn't change, which take extra storage and extra execution time. That's bad enough. But suppose you replay the macro after you select text that's in a different font or a different color, thinking that it will just change the size to 10 pt. In fact, the macro will also change the text to Times New Roman and Automatic color, because the recorder captured every setting in the dialog. That probably isn't what you intended the macro to do.

Whenever you record the result of a dialog, you should inspect the code and delete all the extra settings. In this case, to apply only the size change, all you need is this:

Sub Macro1A()

  With Selection.Font

    .Size = 10

  End With

End Sub

 

There's now only one property being changed, so the "With Selection.Font" and "End With" are also unnecessary. The macro can be further simplified to this:

Sub Macro1B()

  Selection.Font.Size = 10

End Sub

 

If you aren't sure of the name of the property you need to keep, record a macro while you set a property to one value, and then record another macro while you set the same property to a different value. Compare the two macros—the property you want to keep is the one whose value changes, and the others can be deleted from the revised macro.

Making a macro more general

Suppose you record a macro that opens a document and then does something to it, such as changing the view. The beginning of the macro may look like this: 1

Sub Macro2()

  Documents.Open FileName:= "Lorem.doc" , _

      ConfirmConversions:= False , _

      ReadOnly:= False , AddToRecentFiles:= False , _

      PasswordDocument:= "" , PasswordTemplate:= "" , _

      Revert:= False , WritePasswordDocument:= "" , _

      WritePasswordTemplate:= "" , _

      Format:=wdOpenFormatAuto, XMLTransform:= ""

  ' more code, for example ...

  ActiveWindow.View = wdPrintView

End Sub

 

The recorded macro for opening a document, like the recording of the Format > Font dialog, contains unneeded things. In this case, they are parameters that contain information about the file, such as a password. The only parameter that's necessary is the FileName. You can remove the other parameters from the command, and Word will use its default values for them.

A more important problem is that every time you run the recorded macro, it will open the same document. This may be what you intend, but more likely you want the macro to let you choose which document to open.

One way to get the file's name into the macro is to display an input box, where you can type it in. The InputBox function shows a message box with a text entry field, and its result is the name that you type into the field.

Sub Macro2A()

  Dim MyFileName As String

  MyFileName = InputBox( "Enter file name to open:" , _

      "Open a Document" )

  If MyFileName <> "" Then

    Documents.Open FileName:=MyFileName

    ' more code, for example ...

    ActiveWindow.View = wdPrintView

  End If

End Sub

 

But this isn't very friendly. There's a good chance of making a typing mistake. And if the document isn't in the currently active folder, you have to type the entire path to it. A better idea is to use the File > Open dialog that's already built into Word, which lets you browse to and select the proper document. When you click the OK button in the dialog, Word opens the selected document. The macro is simpler, too, because it doesn't need a separate Documents.Open statement—the dialog handles it all for you.

Sub Macro2B()

  If Dialogs(wdDialogFileOpen).Show = - 1 Then

    ' more code, for example ...

    ActiveWindow.View = wdPrintView

  End If

End Sub

 

The word "Dialogs" in this code refers to a list of all of Word's built-in dialogs. Each dialog has a name that starts with "wdDialog". In this case, wdDialogFileOpen is the name of the built-in File > Open dialog, and the expression "Dialogs(wdDialogFileOpen)" selects that particular dialog from the list. To see all of the possible names, press F2 in the VBA editor to display the Object Browser, type wdDialog into the search box, and press Enter.

The word ".Show" refers to a method of the dialog. A method is an action that can be done—the .Show method causes the dialog to appear and execute (carry out its function).

Many methods also have a value after they execute, which tells the macro something about what just happened (this is called "returning" the value). In this case, if you click the OK button in the dialog then the .Show method returns the value –1, but if you click the Cancel button or the X in the title bar then .Show returns the value 0. The VBA help topic for each method tells you what values that method can return and what they mean. You can use the returned value in an If statement, as in Macro2B, to decide what to do.

Similar changes to recorded code let you make macros that save files to variable locations, search for variable strings, and many other unrecordable variations. You can find out more at Getting help with calling Word's built-in dialogs using VBA.

Making toggle macros

The Italic, Bold, and Underline buttons on the toolbar are toggles—click the button once to turn it on, and again to turn it off. If you want to make your own toggle for something else, you can record separate macros for turning it on and off, but how do you combine them into one?

As an example, let's make a macro to toggle the font's outline property on and off. If you record the change to turn it on, and remove the unnecessary properties, you get this:

Sub Macro3()

  With Selection.Font

    .Outline = True

  End With

End Sub

 

One way to make a toggle macro from this code is to use an If statement. You test the current value and then assign the opposite value to the property:

Sub Macro3A()

  With Selection.Font

    If .Outline = False Then

      .Outline = True

    Else

      .Outline = False

    End If

  End With

End Sub

 

A more efficient way is to use the Not operator. If the value is False, then applying Not to the value returns True, and vice versa. With this operator the macro can be written as

Sub Macro3B()

  With Selection.Font

    .Outline = Not .Outline

  End With

End Sub

 

Fixing broken Replace macros

One of the most common actions to record is a Replace operation. A macro can be a great time-saver, since setting up the same Replace over and over can be time-consuming and it's easy to make a mistake. In one circumstance, though, the recorder creates a macro that simply doesn't work.

Suppose you record the replacement of all italic text with the same text in bold italic. While you're recording this operation, it works perfectly well. If you replay the macro on another document, though, nothing happens! What's the matter?

A look at the recorded code reveals the problem:

Sub Macro4()

  Selection.Find.ClearFormatting

  Selection.Find.Replacement.ClearFormatting

  With Selection.Find

    .Text = ""

    .Replacement.Text = ""

    .Forward = True

    .Wrap = wdFindContinue

    .Format = True

    .MatchCase = False

    .MatchWholeWord = False

    .MatchWildcards = False

    .MatchSoundsLike = False

    .MatchAllWordForms = False

  End With

  Selection.Find.Execute Replace:=wdReplaceAll

End Sub

 

There's no mention of italic or bold italic anywhere in this code. Except for the notation .Format = True (which tells Word to use formatting information about the .Text or .Replacement properties while searching or replacing), the recorder has completely missed the fact that you were replacing one format with another. To make this macro work as intended, you have to add these lines:

.Font.Italic = True

.Replacement.Font.Bold = True

 

The first of these lines tells Word to search for italic text. The second line tells it to make the replacement text bold—because it's already italic, it will become bold italic.

Besides making the macro correct, I like to make it consistent. The "With" and "End With" statements are meant to replace the references to Selection.Find; that both speeds up the macro and makes it easier to read. You can pull the ClearFormatting and Execute statements inside the With clause as well, to get this code:

Sub Macro4A()

  With Selection.Find

    .ClearFormatting

    .Replacement.ClearFormatting

    .Text = ""

    .Replacement.Text = ""

    .Font.Italic = True

    .Replacement.Font.Bold = True

    .Forward = True

    .Wrap = wdFindContinue

    .Format = True

    .MatchCase = False

    .MatchWholeWord = False

    .MatchWildcards = False

    .MatchSoundsLike = False

    .MatchAllWordForms = False

    .Execute Replace:=wdReplaceAll

  End With

End Sub

 

Naming and storing macros

When you record a macro, Word suggests a name like Macro1. By default, it puts the macro in a module named NewMacros in the Normal.dot template. You should make it a habit to rename your macros to give them descriptive names, and macros that are useful enough to keep should be stored in a more organized manner.

To rename a macro, all you need to do is change the word that follows "Sub" in the first line. For example, you could change the first line of Macro4A to

Sub ItalicToBoldItalic()

That name will appear in the Tools > Macro > Macros dialog, and you can tell what it does without having to look at the code..

You can organize macros into modules, just as you organize files into folders. In the VBA editor, use the View menu to display the Project Explorer and the Properties pane. On the Insert menu, click Module and notice that a folder named Module1 appears in the Project Explorer. Click that folder, and change its name in the Properties pane.

To move a macro from one module to another, cut its code from the editing pane, double-click the destination module in the Project Explorer, and paste the code into the destination's editing pane. Unfortunately, the Project Explorer doesn't support drag-and-drop movement of macros.

You can move an entire module from one template to another by using the Organizer (Tools > Macro > Macros > Organizer). Macros that are useful for a  specific type of document should be stored in modules in the template used to create that type of document. Macros that are generally useful should be stored in a global template, as explained in What do Templates and Add-ins store?

More fun to come

There are lots of other situations in which the macro recorder gives you code that's inefficient or doesn't do what you want or expect, and you should practice by improving it.

As you learn more about macros, sometimes you'll find it useful to record an action just to discover what statements in VBA are involved. Then you can throw away the recorded macro and write good code of your own.

There are times when you can't get the recorder to record anything useful because the commands you want to use are grayed out. Then you can usually find out more by looking at articles here, or asking questions in the VBA newsgroups.