In this chapter
Checking Security Settings Before Using Macros 970
Case Study: Macro for Formatting for a Mail Merge 972
Everyday-Use Macro Example: Formatting an Invoice Register 979
Understanding VBA Code—An Analogy 982
Using Simple Variables and Object Variables 986
Customizing the Everyday-Use Macro Example: GetOpenFileName and GetSaveAsFileName 990
From-Scratch Macro Example: Loops, Flow Control, and Referring to Ranges 991
Combination Macro Example: Creating a Report for Each Customer 998
Every copy of Excel shipped since 1995 has included the powerful Visual Basic for Applications (VBA) lurking behind the grid. With VBA, you can do anything that you can do in the regular interface, and you can do it much faster. VBA shines when you have many repetitive tasks to undertake.
Learning to use macros is a good news/bad news proposition.
The good news is that Microsoft Office provides a macro recorder that can write a macro as you work. The bads news is that it is not easy to record a macro that will work consistently with any dataset. To really unleash the power of macros, you need to understand how to edit recorded macro code. You can then record a macro that is close to what you want and then edit that macro to create something that will run the way you want it to work.
On March 26, 1999, a hacker named Kwyjibo launched the Melissa virus. This particular virus used VBA macros in Word to propagate itself. Microsoft took a lot of heat over the fact that macros were able to run without the knowledge of the person running the computer. In response, Microsoft has made it more difficult to run macros in subsequent versions of Excel. At one point, there was even some concern that Microsoft would remove support for VBA macros, but Microsoft has committed to support VBA macros for another 10 to 15 years.
Before you can use macros, you have to take some positive steps to affirm that you want to record or run a macro.
To enable VBA security, follow these steps:
Before you start recording a macro, you need to think about how to break the task into easily repeatable steps. The macro recorder is great at recording navigation done using arrow keys. Therefore, you want to use only the keyboard for navigation while using the macro recorder.
Plan out your macro before recording it. Think about the steps you need to perform. If you need to fix many items in a worksheet, you might want to select the first item first. This way, the macro can perform an action on cells relative to the original selection.
To record a macro, follow these steps:
Macro Name
, use MacroName
).A co-worker has some names and addresses in Excel. She needs to do a mail merge in Word. Rather than teach her how to do a mail merge, you offer to do the mail merge for her. In theory, this should take you a couple minutes. However, when the list of names arrives in the Excel worksheet, you realize that the data is in the wrong format. In the Excel worksheet, the names are going down Column A, as shown in Figure 36.1.
In order to successfully do a mail merge, the Excel worksheet should have fields for name, street address, and city+state+zip code, as shown in Figure 36.2.
Before you start recording a macro, you need to think about how to break the task into easily repeatable steps. The macro recorder is great at recording navigation done using arrow keys. Therefore, ideally, you want to use only the keyboard for navigation while using the macro recorder.
It would be good to record a macro that can fix one name in the list. Assume that you start with the cell pointer on a person’s name at the beginning of the macro, as shown in Figure 36.3. The macro would need to perform these steps to fix one record and end up on the name of the second person in the list:
When you run a macro that goes through these steps, Excel deletes the three blank rows, but the selection now contains the three cells that encompass the next record, as shown in Figure 36.7. Ideally, the macro should end with only the name selected. Therefore, the macro now needs to press the Up Arrow key and the Down Arrow key. Moving the cell pointer up a cell and then back to the name causes only a single cell to be selected, as shown in Figure 36.8.
If the macro correctly performs all these steps, the first name and address are properly formatted. The blank rows left between the first and second names are deleted.
By making sure that the macro starts on a name and ends up on the next name, you allow the macro to be run repeatedly. If you assign this macro to the keyboard shortcut Ctrl+J, you can then hold down Ctrl+J and quickly fix records, one after the other.
The default state of the macro recorder is a very stupid state. If you recorded the preceding steps in the macro recorder, the macro recorder would take your actions very literally. The English pseudocode for recording these steps would say this:
This macro will work, but it will only work for one record. After you recorded this macro, your worksheet would look like the one shown in Figure 36.9.
When the default macro runs, it moves the name Vincent Alexander from Cell A2 and pastes it on top of the address in Cell B1. It then takes the address in Cell A3 and pastes it on top of the city in Cell C1. It then deletes Rows 2, 3, and 4, removing the city and state. As shown in Figure 36.10, the macro provides the wrong result.
If you blindly ran this macro 100 times to convert 100 addresses, the macro would happily “eat” all 100 records, leaving you with just one record (and not even a correct record), as shown in Figure 36.11.
To overcome this problem, use relative references as discussed in the next section.
There is an icon in the Code group on the Developer tab on the ribbon called Use Relative References. The key to recording useful macros is to judiciously turn on and off the relative recording setting. If you performed the steps described in the preceding section in relative recording mode, Excel would write code that does this:
These steps are far more generic than those recorded using the default state of the macro recorder and would work for any record, provided that you started the macro with the cell pointer on the first cell that contains a name.
For this example, you need to record the entire macro with relative recording turned on.
At this point, you have rehearsed the steps needed for a macro that puts data records into a format that will be usable for a mail merge. After you make sure that the cell pointer is starting on the name in Cell A1, you are ready to turn on the macro recorder.
You shouldn’t be nervous, but you really want to perform the steps correctly. If you move the cell pointer in the wrong direction, the macro recorder will happily record that for you and play it back. It would be annoying to watch the macro recorder play back your mistakes 100 times a day for the next five years, so follow these steps to correctly create the macro:
This macro will successfully fix any record in the database, provided the cellpointer is on the cell containing the name when you run the macro. Try playing back the macro by pressing Ctrl+A to fix one record. To fix all records, hold down Ctrl+A until all records are fixed.
To run a macro, you follow these steps:
Caution
When you run a macro, there is no undo. Therefore, you should save a file before running a new macro on it. It is easy to have accidentally recorded the macro in default mode instead of relative mode. You need to save the macro so that you can easily go back to the current state in case something doesn’t work right.
This first example represents an ideal use of a one-time macro. Someone gave you data that was in a bad format. The process to fix the data involved mindless repetition. If there had just been four records, you could have easily mindlessly fixed the records. But because there were 100 records in this example, it made sense to quickly record a macro and then run the macro repeatedly to solve the problem. You recorded the entire macro in relative mode, and you did not have to edit the macro at all. You probably run into a few situations a week where a quick one-time-use macro would make your job easier.
The macro recorder does not solve all tasks perfectly, however. Many times, you need to record a macro and then edit the recorded code to make the macro a bit more general. This example demonstrates how to do that.
In this example, a system writes out a file every day. This file contains a list of invoices generated on the previous day. The file predictably contains six columns—NAME, DATE, INVOICE, REVENUE, SALES TAX, and TOTAL—as shown in Figure 36.15. The file also looks horrible: The columns are the wrong width, there is no title, there is not a total row at the bottom. You would like a macro that would open this file, make the columns wider, add a total row, add a title, make the headings bold, and save the file with a new name. The following sections describe how to create this macro.
One of the inherent problems with this example is that your file will have a different number of rows every day. If you record a macro for this today to add totals in Row 16, it will not work tomorrow, when you might have 22 invoices. The solution is to use the End key to navigate to the last row of your data.
You use the End key to move to the edge of a contiguous range of data. In Figure 36.15, if you press the End key and then the Down Arrow key, you would move to Cell A15. If you press the End key and then the Up Arrow key, you move back to Cell A1. You can press the End key followed by the Right Arrow key to move to Cell F1.
You can also use the End key to jump over an abyss of empty cells. If you are currently at the edge of a range—for example, Cell F1—and press End followed by the Right Arrow key, Excel jumps over all the blank cells and stops either at the next nonblank cell in Row 1 or at the right edge of the worksheet, Cell XFD1.
You might be tempted to start in Cell A1, press End, press the Down Arrow key, and then press the Down Arrow key again to move to the first blank row in the data. However, that is the wrong thing to do. This data file is coming from another system. One day, I guarantee that some crazy cashier will find a way to enter an order without a customer name. She will happen upon the accidental keystroke combination that causes the cash register to allow an order without a customer name. On that day, the End+Down Arrow key combination will stop at the wrong row and add totals in the middle of your dataset. To prevent this problem, you should have the macro go through these steps:
="Invoices for "&TEXT(B4,"mmmm d, yyyy")
.Before recording this macro, you need to open a blank Excel workbook and save it with the name MacroToImportInvoices.xlsm.
In this macro example, you use a mix of relative and absolute recording to produce a macro that handles any number of rows of data. The macro will be fairly useful, with two annoying limitations:
Both of these changes require you to edit the recorded macro, as described in the next section.
To edit a macro, follow these steps:
A number of panes are available in the VBE, but it is common to have three particular panes displayed, as shown in Figure 36.17:
In the 1980s and early 1990s, many people going through school were exposed to an introductory class in a programming language called BASIC. Although Excel macros are written in Visual Basic for Applications, the fact that both languages contain the word basic does not mean that BASIC and VBA are the same or even similar. BASIC is a procedural language. VBA is an object-oriented language. In VBA, the focus is on objects. This can make VBA confusing to someone who has learned to program in BASIC.
The syntax of VBA is made up of objects, methods, collections, arguments, and properties. If you have never programmed in an object-oriented language, these terms, and the VBA code itself, might seem foreign to you. The following sections compare these five elements to parts of speech:
Each of the following sections describe the similarity between the VBA element and a part of speech. It also describes how to recognize the various elements when you examine VBA Code.
As an object-oriented language, the objects in VBA are of primary importance. Think of an object as any noun in Excel. Examples of objects are a cell, a row, a column, a worksheet, and a workbook.
A method is any action that you can peform on an object. This is similar to a verb. You can add a worksheet. You can delete a row. You can clear a cell. In Excel VBA, words like “Add,” “Delete,” and “Clear,” are methods.
Objects and methods are joined together by a period, although in VBA, people pronounce the period as a dot. The object is first, followed by a dot, followed by the method. For example, object.method is pronounced “object-dot-method” and indicates that the method performs on the object. This is confusing because it is backward from how English is spoken. If we all spoke VBA instead of English, our day would be filled with sentences like “car.drive” and “dinner.eat.” When you see a period in VBA, it usually means that the word after the period is acting upon the word to the left of the period.
In an Excel workbook, there is not a single cell but a collection of many cells. Many workbooks will contain several worksheets. Anytime you have multiple instances of a certain object, VBA refers to this as a collection.
The “s” at the end of an object may seem subtle, but it indicates you are dealing with a collection instead of a single object. While This Workbook refers to a single workbook, Workbooks refers to a collection of all of the open workbooks. This is a very important distinction to understand.
There are two main ways to refer to a single worksheet in a collection of worksheets: You can refer to a worksheet by its number or by its name. For example, Worksheets(1) and Worksheets(“Jan”) might refer to the same worksheet.
When you invoke a command such as the Save As command, a dialog box pops up, and you have the opportunity to specify several options that change how the command will be carried out. If the Save As command is a method, then the options for it are parameters. Just like an adverb modifies a verb, a parameter modifies a method.
Most of the time, parameters are recorded by using the syntax ParameterName
:=
ParameterValue
.
One of the reasons that recorded code gets to be so long is that the macro recorder makes note of every option on the dialog box, whether you select it or not.
Consider this line of code for SaveAs
:
In this recorded macro for SaveAs
, the recorder noted parameter values for Filename
, FileFormat
, and CreateBackup
. Figure 36.18 shows the Save As dialog box. Filename
and FileFormat
are clearly evident on the form, but where are the rest of those options?
In the bottom corner of the dialog is a Tools drop-down. If you choose Tools and then General Options, you see a dialog box with four additional options, as shown in Figure 36.19. Even though you did not touch this Save Options dialog, Excel recorded the values in it for you.
Parameters have some potentially confusing aspects. Most of the time, there is a space following the method and then a list of one or more ParameterName
:=
ParameterValue
constructs, separated by a comma and a space. However, there are a couple exceptions:
WordArt
to a worksheet:
It would be very difficult to figure out this line of code without looking at the help topic. To access Help, you can click anywhere in the method of AddTextEffect
. The help topic reveals that the correct parameter order is the one shown in Figure 36.20.
Again, parameters are like adverbs. They generally appear with a ParameterName
:=
ParameterValue
construct, but there are times when the macro recorder lists the parameter values in their default order, without the parameter names or the :=.
The final construct in VBA is the adjective used to describe an object. In VBA, adjectives are called properties. Think about a cell in Excel with a formula in it. The cell has many properties. These are some of the most popular properties:
Value
(the value shown in the cell)Formula
(the formula used to calculate Value)Font Name
Font Size
Font Color
Cell Interior Color
In VBA, you can either check on the value of a property or you can set the property to a new value. To change several cells to be bold, for example, you would change their Bold
property to true
:
Selection.Font.Bold = True
You could also check to see if a property equals a certain value.
If Selection.Value = 100 then Selection.Font.Bold = True
Properties are generally used with the dot construct, and they are almost always followed by =
(as contrasted with the :=
used with parameters). For example, PropertyName = value.
When you understand that a period generally separates an object from a method, you can start to make sense of the recorded code.
For example, the following line performs the Open
method:
Workbooks.Open Filename:="C:Invoices.xls"
In this example, the Filename
parameter is shown with := after the parameter name.
This first line in the following example performs the Select
method on one particular member of the Rows
collection:
The second line then sets the Bold
property of the Font
property of the selection to true
. Using these two lines of code is equivalent to selecting Row 1 and clicking the Bold icon.
Tip From
In the Excel user interface, you generally have to select a cell before you can change something in it. In a macro, there is no need to select something first. For example, you can replace the two lines in the preceding example with this single line of code:
Rows(“1:1”).Font.Bold = True
The advantage of this method is that the macro will run even faster than before.
The macro recorder never records a variable, but you can add variables to a macro when you edit the code. Let’s say that you need to do a number of operations to the row where the totals will be located. Rather than repeatedly going to the last row in the spreadsheet and pressing the End+Up Arrow, you could assign the row number to a variable:
The words FinalRow
and TotalRow
are variables that each hold a single value. If you have data in Rows 2 through 25 today, FinalRow
will hold the value 25
, and TotalRow
will hold the value of 26
. This allows you to use efficient code such as the following:
VBA also offers a powerful variable called an object variable. An object variable can be used to represent any object, such as a worksheet, a chart, or a cell. Whereas a simple variable holds one value, an object variable holds values for every property associated with the object.
Object variables are declared using the Dim statement and then assigned using the Set statement:
Using object variables offers several advantages. First, it is easier to refer to WSD
than to ActiveWorkbook.Worksheets("Sheet1")
. Second, if you define the object variable with a DIM
statement at the beginning of the macro, as you type new lines of code, the VBE’s AutoComplete feature shows a list of valid methods and properties for the object, as shown in Figure 36.21.
If you are a history buff, you might know that VisiCalc was the first spreadsheet program for PCs. When Dan Bricklin and Bob Frankston invented VisiCalc, they used the A1 style for naming cells. In those early days, VisiCalc had competitors such as SuperCalc and a Microsoft program called MultiPlan. This early Microsoft spreadsheet used the notation of R1C1 to refer to Cell A1. The cell that we know today as E17 would have been called R17C5, for Row 17, Column 5.
In 1985, Microsoft launched Excel version 1.0 for the Macintosh. Excel originally continued to use the R1C1 style of notation. During the next 10 years, Excel and Lotus 1-2-3 were locked in a bitter battle for market share. Lotus was the early leader, and it had adopted the A1 notation style familiar to VisiCalc customers. To capture more market share, Microsoft allowed Excel to use either A1-style notation or R1C1-style notation. Even today, in Excel 2007, you can turn on R1C1 style notation (by choosing Office Icon, Excel Options, Formulas, R1C1 Reference Style). Hardly anyone actually uses R1C1 reference style; however, the macro recorder always records formulas in R1C1 style.
Figure 36.22 shows the familiar formula =SUM(D$2:D15)
. When entered in Cell D16, this formula adds up everything from Row 2 to the row just above the current cell.
If you now turn on R1C1 style in this worksheet, the formula changes to =SUM(R2C:R[-1]C)
, as shown in Figure 36.23.
In R1C1 notation, the reference RC refers to the current cell. You can modify RC by adding a particular row number or column number. For example, R2C refers to the cell in Row 2 of the current column. RC1 refers to the cell in this row that is in Column 1.
If you put a row number or column number in square brackets, it refers to a relative number of cells from the current cell. If you have a formula in Cell D16 and use the reference R[1]C[-2], you are referring to the cell one row below D16 and two columns to the left of D16, which would be cell B17.
You are probably wondering why the macro recorder uses this arcane notation style when recording formulas. It turns out that this style is fantastic for formulas. For example, take a look at the formulas in Column F of the worksheet, as shown in Figure 36.24. Every formula is a little different. When you copy F2 to F3, Excel has to change the references of E2 and D2 to be E3 and D3.
Now, look at these same formulas in R1C1 style, as shown in Figure 36.25. Every formula in that range is exactly identical. This makes sense: The formula is actually saying, “Add the sales tax one cell to the left of me to the merchandise amount that is two cells to the left of me.”
If you were forced to use A1-style formulas in a macro, you might have to enter the formula in Cell F2 and then copy the formula from F2 to the remaining cells:
Instead, using R1C1 style formulas, you can enter all the formulas in one line of code:
Range("F2:F15").FormulaR1C1 = "=RC[-2]+RC[-1]"
If you are not yet convinced to learn how to use R1C1-style formulas, the final straw is that you have to use R1C1-style formulas when setting up conditional formatting in VBA.
Probably the most important reason to understand R1C1 formulas is to make sure that the macro recorder recorded the proper formula. Remember that when you recorded the macro described in the section “Using the End Key to Handle a Variable Number of Rows” earlier in this chapter, you had selected cells D16:F16 and clicked the AutoSum button. Excel recorded the following line of code:
Selection.FormulaR1C1 = "=SUM(R[-14]C:R[-1]C)"
This formula adds up a range from 14 rows above the selection to the cell just above the selection. This works only on days on which you have exactly 14 rows of data. This is one of the most annoying bugs in a macro. Although many times, a macro will return an error if you try to do something wrong, if you run this macro on tomorrow’s invoice file and there are 20 invoices, the macro will happily total only the last 14 invoices instead of all 20 invoices. You could easily distribute this report with a wrong total for several days before someone realizes that something is amiss.
It is easy to correct the formula. You know that you have headings in Row 1 and the first invoice will appear in Row 2. You need the macro to sum from Row 2 to the row just above the current cell. Therefore, you need to change the formula to this:
Selection.FormulaR1C1 = "=SUM(R2C:R[-1]C)"
GetOpenFileName
and GetSaveAsFileName
The everyday-use macro you recorded earlier in this chapter (for formatting an invoice register) is hard-coded to always open the same file and to always save with the same filename. To make the macro more general, you might like to allow the person running the macro to browse for the file each morning and then to specify a new filename during the Save As. Excel offers a very easy way to display the File Open or File Save As dialog. Here is the code you need to use:
Note that this code displays the File Open dialog and allows a file to be selected. When you click Open, the dialog assigns the filename to the variable. It does not actually open the file. You then need to open the file specified in the variable:
Workbooks.Open Filename:=FileToOpen
When you want to ask for the filename to use in saving the file, use this code:
The following macro is the final macro to use each day:
Out of the 19 lines in the macro, you needed to correct 1 line—the total formula. In order to add functionality, you added two lines and changed two other lines. This is fairly typical: Perhaps 10% to 20% of a recorded macro generally needs to be adjusted.
Say you work for a company that sells printers and scanners to commercial accounts. When you sell a piece of hardware, you also try to sell a service plan for that hardware. Customers in your state are taxed. Your accounting software provides a daily download that looks like Columns A:D in Figure 36.26.
You want to create a macro that examines each row in the dataset and carries out a different action, based on the value in Column D. This is a macro that you will probably want to write from scratch. The following sections describe how you do it.
The loop most commonly used in VBA is a For
-Next
loop. This is identical to the loop that you might have learned about in a BASIC class.
In this example, the loop starts with a For
statement. You specify that on each pass through the loop, a certain variable will change from a low value to a high value. This simple macro will run through the loop 10 times. On the first pass through the loop, the variable x
will be equal to 1
. The two lines inside the loop will assign the value 1
to Cells A1 and B1. When the macro encounters the Next x
line, it will return to the start of the loop, increment x
by 1
, and run through the loop again. The next time through the loop, the value of x
will be 2
. Cell A2 will be assigned the number 2
, and cell B2 will show 4
(which is the square of 2). Eventually, x
will be equal to 10
. At the Next x
line, the macro will allow the loop to finish. The following is the code for this macro
:
After you run this macro, you have a simple table that shows the numbers 1 through 10 and their squares, as shown in Figure 36.27
After a loop is written, it is easy to adjust it. If you want a table showing all the squares from 1 to 100, you would simply adjust the For x = 1 to 10
line to be For x = 1 to 100
.
There is an optional clause in the For
statement called the step value. If no step value is shown, the program moves through the loop by incrementing the variable by 1 each time through the loop. If you wanted to check only the even-numbered rows, you could change the loop to be For x = 2 to 100 Step 2
.
If you are going to be optionally deleting rows from a range of data, it is important to start at the bottom and proceed to the top of the range. You would use -1
as the step value:
For x = 100 to 1 step -1
The macro recorder uses the Range
property to refer to a particular range. You might see the macro recorder refer to ranges such as Range("B3")
or Range("W1:Z100")
.
The loop code shown in the preceding section emulates this style of referring to ranges. On the third time through the loop, this line of code would refer to Cell B3:
Range("B" & x).value = x * x
But what if you wanted to loop through each column? How would you handle that? If you wanted to continue using the Range
property, you would have to jump through some hoops to figure out the letter that is associated with Column 5:
This method works fine if you are using only 26 or fewer columns. But if you needed to loop through all the columns out to Column XFD, you would spend all day trying to write the logic to assign the column label WMJ
to Column 15896.
Instead of using the Range
property, you can use the Cells
property. Cells
requires that you specify a numeric row number and a numeric column number. For example, Cell B3 would be specified as follows:
Cells(3, 2)
If you need to refer to a rectangular range, you can use the Resize
property. Resize
requires you to specify the number of rows and the number of columns. To refer to W1:Z100, for example, you would use this:
Cells(1, 23).Resize(100, 3)
It is certainly difficult to figure out that this refers to W1:Z100, but it allows you to easily loop through rows or columns.
You could use the following code to make every other column bold:
FinalRow
Earlier in this chapter, you learned how to use the End key to find the final row in a dataset. After finding the final row in the dataset and assigning it to a variable, you can specify that the loop should run through FinalRow
:
Flow control is the ability to make decisions within a macro. As described in the following sections, two commonly used flow control constructs are If
-End If
and Select Case
.
Say that you need a macro to delete any records that say Sales Tax. You could accomplish this with a simple If
-End If
construct:
This construct always starts with the word If
, followed by a logical test, followed by the word Then
. Every line between the first line and the End If
line is executed only if the logical test is true
.
Now say that you want to enhance the macro so that any other amounts that contain service plan revenue are moved to Column F. To do this, you use the ElseIf
line to enter a second condition and block of lines to be used in that condition:
You could continue adding ElseIf
statements to handle other situations. Eventually, just before the End If
, you could add an Else
block to handle any other condition that you haven’t thought about.
Select Case
ConstructIf you reach a point where you have many ElseIf
statements all testing the same value, it might make sense to switch to a Select Case
construct. To illustrate, say that you want to loop through all the records, examining the product in Column C. If Column C contains a printer, you want to move the amount in Column D to a new Column E. Scanner revenue should go to a new Column F. Service plans go to a new Column H. Sales tax goes to a new Column I. You should also handle the situation where something is sold and contains none of those products. In that case, you would move the revenue to a new Column G.
The construct begins with Select Case
and then the value to check. The construct ends with End Select
, which is similar to End If
.
Each subblock of code starts with the word Case
and one or more possible values. If you needed to check for Printer
or Printers
, you would enclose each in quotes and separate them with a comma.
After checking for all the possible values you can think of, you might add a Case Else
subblock to handle any other stray values that might be entered in Column C.
The following code checks to see what product is in column C. Depending on the product, the program copies the revenue from column D to a specific column.
Using the building blocks described in the preceding sections, you can now write the code for a macro that finds the last row, loops through the records, and copies the total revenue to the appropriate column. Now you need to add new headings for the additional columns, as shown in Figure 36.28.
The macro should use the End
property to locate the final row. It should also prefill Columns E through I with 0s. Next, it should loop from Row 2 down to the final row. For each record, the revenue column should be moved to one of the columns. At the end of the loop, the program alerts you that the program is complete, using a MsgBox
command. The following is the complete code of this macro:
After you run this macro, you see that the revenue amounts have been copied to the appropriate columns, as shown in Figure 36.29.
Tip From
An alternative syntax of the Range
property is to specify the top-left and bottom-right cells in the range, separated by a comma. In the macro described here, for example, you know you want to fill from Cell E2 to the last row in Column I. You can describe this range as follows:
Range("E2", Cells(FinalRow, 9))
This syntax is sometimes simpler than using Cells()
and Resize()
.
If a loop will be conditionally deleting records, you will run into trouble if it is a typical For
-Next
loop. Let’s say you want to delete all the sales tax records, as follows:
Consider the data in Figure 36.30.
The first time through the loop, x
is equal to 2
. Cell C2 does not contain tax, so Cell E2 has the word checked. A similar result occurs for Rows 3 and 4. The fourth time through the loop, Cell C5 contains tax. The macro deletes the tax in Row 5. However, Excel then moves the old Row 6 up to Row 5, as shown in Figure 36.31. The next time through the loop, the program inspects Row 6, and the data that is now in Row 5 will never be checked.
The macro succeeds in deleting tax, but several rows were never checked, and several extra blank rows at the bottom were needlessly checked, as shown in Figure 36.32.
The solution is to have the loop run backward. You need to start at the final row and proceed up through the sheet to Row 2. When the macro deletes tax in Row 31, it can then proceed to checking Row 30, knowing that nothing has been destroyed (yet) in Row 30 and above.
To reverse the flow of the loop, you have to tell the loop to start at the final row, but you also have to tell the loop to use a step value of -1
. The start of the loop would use this line of code:
For x = FinalRow to 2 Step -1
The macro needed here represents a fairly common task: looping through all the records in order to conditionally do something to each record.
The following macro correctly deletes all the sales tax records:
For the example described here, the macro recorder would be almost no help. You would have to write this simple macro from scratch. However, it is a powerful macro that can simplify tasks when you have hundreds of thousands of rows of data.
Note
It is common to indent each line of code with four spaces. Any lines of code inside an If-EndIf block or inside a For-Next loop are indented an additional four spaces. If you have typed a line of code that is indented eight spaces and press Enter at the end of the line of code, the VBE will automatically indent the next line to eight spaces. Each press of the Tab key will indent by an additional four spaces. Pressing Shift+Tab will remove four spaces of indentation.
Many real-life scenarios require you to use a combination of recorded code and code written from scratch. For example, Figure 36.33 shows a dataset with all your invoices for the year. In this case, say you would like to produce a workbook for each customer that you can mail to the customer.
One way to handle this task would be to use an advanced filter to get a list of all unique customers in Column A. You would then loop through these customers, applying an AutoFilter to the dataset in order to see only the customers that match the selected customer. After the dataset is filtered, you could select the visible cells only and copy them to a new workbook. Then you could save the workbook with the name of the customer and then return to the original workbook.
You could start by creating a blank procedure with comments to spell out the steps in the preceding paragraphs. Then you would add in code for the loop and other simple tasks, such as copying the selection to a new workbook. Whenever you encounter a step that you’ve never written code for, you could just leave a comment with question marks. This would allow you to go back and record parts of the process in order to finish the macro.
Your first pass at a well-commented macro might look like this:
As described in the following sections, to create this macro, you need to figure out how to code the advanced filter to copy a unique list of customers to Column H. You then need to figure out how to apply a filter to Column A. Finally, you need to figure out how to select only the visible cells from the filter.
You need to figure out how to use an advanced filter to finish the following section of code:
To use an advanced filter on this section of code, follow these steps:
Tip From
Even though you have an existing Module1 with your code, Excel chooses to record the new macro into a new module. You therefore need to copy recorded code from Module2 and then use the Project Explorer to switch to Module1 to paste the code into your macro.
Even though the Advanced Filter dialog is still one of the most complicated facets of Excel 2007, the recorded macro is remarkably simple:
In your macro, there is no reason to select Cell H1, so you can delete that line of code. The remaining problem is that the macro recorder hard-coded that today’s dataset contains 1,000 rows. You might want to generalize this to handle any number of rows. The follow code reflects these changes:
When you have a list of customers, the macro will loop through each customer. The goal is to use an AutoFilter to display only the records for each particular customer. You now need to finish this section of code:
To apply an AutoFilter to this section of code, follow these steps:
The macro recorder always does too much selecting. You rarely have to select something before you can operate on it. You can theorize that the only line of this macro that matters is the Selection.AutoFilter
line. Because you will always be looking at the AutoFilter drop-down in Cell A1, you can replace Selection
with Range("A1")
. Rather than continually ask for one specific customer, you can replace the end of the line with a reference to a cell in Column H:
Range("A1").AutoFilter Field:=1, Criteria1:=Cells(x, 8).Value
After you use the AutoFilter in the macro, you see only records for one customer. However, as you can see in Figure 36.36, the other records are still there; they are just hidden. If you copied the range to a new worksheet, all the hidden rows would come along, and you would end up with 20 copies of your entire dataset.
The long way to select visible cells only is to press F5 to display the GoTo dialog. In the GoTo dialog, you click the Special button and then click Visible Cells Only. The shortcut, however, is to simply press Alt+;.
In order to learn how to select visible cells only in VBA, record the macro by following these steps:
In your original outline of the macro, you had contemplated selecting visible cells only and then doing the copy in another statement, like this:
Instead, simply copy the visible cells in one statement:
The following macro started as a bunch of comments and a skeleton of a loop:
After doing three small tests with the macro recorder, you were able to fill in the sections to actually copy the customer records to a new workbook. After running this macro, you should have a new workbook for each customer on your hard drive, ready to be distributed via email.
VBA macros open up a wide possibility of automation for Excel worksheets. Any time that you are faced with a daunting, mindless task, you can turn it into a challenging exercise by trying to design a macro to do the task instead. It usually takes less time to design a macro than it does to complete the task. You should save every macro you write. Soon you will have a library of macros that handle many common tasks, and they will allow you to develop macros faster. The next time you need to perform a similar task, you can roll out the macro and perform the steps in seconds instead of hours.
18.223.149.74