Dec 10

Last week I discussed how to remove Select and Selection statements as a way to clean up recorded macro code. In the example I presented, this method reduced the number of lines of statements by over 50 percent and also cut the running time in half. While cutting the running time in half might not be a big deal when you go from 5 seconds to 2.5 seconds, as your macros get bigger and more ambitious, improving performance takes on greater importance.

Would you believe that by adding two lines of code to last week’s example I was able to cut the running time in half again? It’s true — two simple lines of code. Rather than show you the same code again, I have a new example that demonstrates this simple performance tip.

Have you ever needed to hide rows of data in Excel that meet certain conditions? Listing 1 shows a simple procedure that hides each row where the value in column 1 is 100 and the value in column 2 is 200. To keep things really simple here, I’ve just fixed the number of rows examined at 100 rows.

Listing 1: A demonstration of Application.ScreenUpdating

Sub HideRowsFast()
' Reset the worksheet
UnhideAllRows

' Call the HideRows procedure
' so that it uses Application.ScreenUpdating
HideRows True
End Sub

Sub HideRowsSlow()
' Reset the worksheet
UnhideAllRows

' Call the HideRows procedure
' so that is does not use Application.ScreenUpdating
HideRows False
End Sub

Sub HideRows(bGoFast As Boolean)
Dim nRow As Integer
Dim ws As Worksheet
Dim dTimer As Double

dTimer = Timer
Set ws = ThisWorkbook.Worksheets("Sheet1")

If bGoFast Then Application.ScreenUpdating = False

For nRow = 1 To 100
If ws.Cells(nRow, 1).Value = 100 And _
ws.Cells(nRow, 2).Value = 200 Then

ws.Cells(nRow, 1).EntireRow.Hidden = True
End If
Next

If bGoFast Then Application.ScreenUpdating = True

Set ws = Nothing
MsgBox "That took " & Timer - dTimer & " seconds.", vbOKOnly
End Sub

Sub UnhideAllRows()
ActiveSheet.Cells.EntireRow.Hidden = False
End Sub

The magical lines of code that can dramatically improve the performance and appearance of your macros are the ones that use Application.ScreenUpdating. For the purposes of this demonstration, I put them in an If/Then statement so that you could easily run it with or without using ScreenUpdating. In practice, all you need to do is surround any block of code that would affect the user interface with Application.ScreenUpdating = False and then set it back to Application.ScreenUpdating = True when you are done.

The effect on performance depends on how often you do something in code that would cause Excel to redraw the display. To experiment, put 100 in the first column and the first 100 rows of Sheet1. Likewise, put 200 in the second column and the first 100 rows. Then, run HideRowsSlow and HideRowsFast, noting the elapsed time of each. On my computer, HideRowsSlow takes about 1.03 seconds. HideRowsFast meanwhile only takes 0.03 seconds!

If you delete everything below row 20 however, HideRowsSlow only takes about 0.17 seconds while HideRowsFast only takes about 0.02 seconds (actually 0.0151).

The moral of the story is always precede a block of statements that will modify the contents or formatting of a worksheet with Application.ScreenUpdating = False. Just be sure to set Application.ScreenUpdating = True when you’re done. You get a substantial performance gain and your work will look more professional as the screen won’t flicker as your macro goes about its business.

Dec 05

The Macro Recorder is a mixed blessing. On the one hand it is a great way to automate repetitive, potentially tedious tasks. In addition, it is a great tool for learning how to program when used to illustrate objects that you need to manipulate to perform a given task. On the other hand, the Macro Recorder produces terribly inefficient code, and if you rely on it as your primary example of how to write code, you will develop some nasty habits. This week I’m going to discuss some things you can do to spruce up recorded macros. Fortunately, it’s fairly easy to tweak recorded macro code so that it runs more efficiently. To demonstrate this, I’ll record a macro that does some fairly standard macro activities. Then I’ll go through it and remove the unnecessary code.

n33eopfig1.jpg
Figure 1: A raw worksheet in need of formatting
Click to enlarge

The following code listing shows the code recorded by the macro to produce the worksheet shown in Figure 2.

Listing 1: Code produced by the Macro Recorder:

Sub Macro1()
'
' Macro1 Macro
'
Columns("B:N").Select
Selection.ColumnWidth = 9.86
Range("B3:N3").Select
With Selection
.HorizontalAlignment = xlRight
.VerticalAlignment = xlBottom
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
Range("B4:N9").Select
Selection.Style = "Comma"
Selection.NumberFormat = "_(* #,##0.0_);_(* (#,##0.0);_(* ""-""??_);_(@_)"
Selection.NumberFormat = "_(* #,##0_);_(* (#,##0);_(* ""-""??_);_(@_)"
Range("A4:A8").Select
Selection.InsertIndent 1
Selection.Font.Bold = True
Range("A9:N9").Select
Selection.Font.Bold = True
Range("N3:N8").Select
Selection.Font.Bold = True
Range("B3:M3").Select
Selection.Font.Bold = True
Range("A2").Select
Selection.Font.Bold = True
Rows("3:3").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A2").Select
End Sub

n33eopfig1.jpg
Figure 2: The worksheet after running a formatting macro
Click to enlarge

Looking at listing 1, the biggest thing that jumps out at me is the use of the Select method. The Macro Recorder has to record everything you do — it can’t make assumptions or educated guesses about what is really important or needed. One terribly inefficient yet common habit to avoid is using the Select method in conjunction with Selection. It is not necessary to select items in order to manipulate them programmatically. In Excel, the vast majority of the things you do as a user is manipulate ranges.

In the recorded code, any line that starts with Selection is actually manipulating a Range object. The main way you can make this code more efficient is to modify it to eliminate the use of Select and Selection as shown in Listing 2. In this listing, I commented out the unnecessary code (lines that start with an apostrophe) and followed each logical task with the statement that replaces the “commented out” code.

Generally after cleaning a macro in this manner and verifying it still works, I would delete all of the commented code. I left it in this listing so that you could easily compare the changes I made.

‘ A cleaner version of the recorded macro

Sub MoreEfficientMacro()
Dim ws As Worksheet
Set ws = ActiveSheet

'Columns("B:N").Select
'Selection.ColumnWidth = 9.86
ws.Range("B:N").ColumnWidth = 9.86

'Range("B3:N3").Select
'With Selection
' .HorizontalAlignment = xlRight
' .VerticalAlignment = xlBottom
' .WrapText = False
' .Orientation = 0
' .AddIndent = False
' .IndentLevel = 0
' .ShrinkToFit = False
' .ReadingOrder = xlContext
' .MergeCells = False
'End With
ws.Range("B3:N3").HorizontalAlignment = xlRight

'Range("B4:N9").Select
'Selection.Style = "Comma"
'Selection.NumberFormat = "_(* #,##0.0_);_(* (#,##0.0);_(* ""-""??_);_(@_)"
'Selection.NumberFormat = "_(* #,##0_);_(* (#,##0);_(* ""-""??_);_(@_)"
ws.Range("B4:N9").NumberFormat = _
"_(* #,##0_);_(* (#,##0);_(* ""-""??_);_(@_)"

'Range("A4:A8").Select
'Selection.InsertIndent 1
'Selection.Font.Bold = True
ws.Range("A4:A8").InsertIndent 1
ws.Range("A4:A8").Font.Bold = True

'Range("A9:N9").Select
'Selection.Font.Bold = True
'Range("N3:N8").Select
'Selection.Font.Bold = True
'Range("B3:M3").Select
'Selection.Font.Bold = True
'Range("A2").Select
'Selection.Font.Bold = True
ws.Range("A9:N9").Font.Bold = True
ws.Range("N3:N8").Font.Bold = True
ws.Range("B3:M3").Font.Bold = True
ws.Range("A2").Font.Bold = True

'Rows("3:3").Select
'Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
ws.Range("3:3").Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove

'Range("A2").Select
Set ws = Nothing
End Sub

This listing could be improved in several more ways, but given the space of this column and without providing more background, I’ll need to save them for another day.

If you remove the comments, the cleaned version has 13 lines of statements versus 32 lines in the original version. From a performance standpoint, the cleaned version runs in less than half the time of the original version. Next week, we’ll go just a little bit deeper on this and show how you can halve the running time of the cleaned version with two lines of code.

Dec 05

Last week we explored the capabilities of the Visual Basic Editor’s (VBE) Object Browser. In addition to a comprehensive listing of all programmable elements in a library, you can use it to search for items, navigate to related help topics, and navigate to related classes and libraries.

This week we’ll examine what you can do with the Locals windows. Together, the Object Browser and Locals windows are useful for investigating and learning various object models that you’ll use.

The difference is that the Locals window is something you use when you are debugging a procedure for displaying the properties associated with a variable that you have defined in your procedure. An example of the Locals window is shown in Figure 1.

Figure 1: Using the Locals window to explore the properties of a variable.

n32progfig1.jpg
Figure 1 - Using the Locals window
to explore the properties of a variable
Click to enlarge

The cool thing about the Locals window is that not only does it show you the properties associated with a given variable, but it also displays the current value of each property. This can be invaluable when debugging. The basic use of the Locals window is demonstrated with the following example.

1. In the VBE, double-click on Sheet1 (Sheet1) in the Project Explorer window.

2. Enter the code shown below into the editor window (you can copy and paste it into the window if you wish):

Sub ExploreRange()
Dim rg as Range
Set rg = Me.Range("A1")
Stop
rg.Value2 = 100
rg.Font.Bold = true
Stop
Set rg = nothing
End Sub

3. Place the cursor anywhere inside the procedure and press F5 to run it.

4. When code execution pauses at the first Stop statement, display the Locals window by selecting View\Locals Window.

5. Click on the + sign next to “rg” in the Locals window and take a minute to view all of the properties associated with the rg variable. In particular, observe the values for the Value2 property and the Bold property underneath the Font property.

6. Continue running the procedure by pressing F5.

7. At the second Stop statement, observe the Value2 and Bold properties.

8. Continue running the procedure by pressing F5.

You can even use the Locals window in conjunction with the Immediate window. For example, if you find a property in the Locals window that you want to change, you could enter a statement in the Immediate window to make the change. For example, in the proceeding example, you could enter

rg.Value2 = 250

in the Immediate window to change the value. The Locals window will reflect the change immediately. Note however that some properties are read-only.

That concludes my discussion of the various VBE windows. Now you’re ready to start coding! How about testing the water next week by discussing some ways to clean-up and optimize recorded macros?

Dec 05

While learning how to program, one can broadly divide the things you have to learn into two categories. First, you need to learn the semantics of a programming language, in this case “classic” Visual Basic (as opposed to VB.Net). Second, you need to learn the object models that you’ll use to achieve your programming objectives.

This week I’m going continue my coverage of the various Visual Basic Editor (VBE) windows by discussing the Object Browser. Next week I’ll explain the Locals windows. These windows are useful for investigating and learning various object models that you’ll use.

The Object Browser, shown in Figure 1, is useful for general object model investigation.

n31progfig1.jpg
Figure 1: Viewing the Worksheet object
in the Object Browser. Click to enlarge

A simple exercise will take you through all of the useful capabilities of the Object Browser.

1. In Excel, open the VBE (Alt + F11) and display the Object Browser by selecting View/Object Browser (F2).

2. Change the Project/Library drop-down from to Excel.

3. In the Search Text drop-down, enter “worksheet” (without the quotation marks).

4. Click the Search button (the binoculars).

5. Select the first item in the Search Results list.

6. Notice that Worksheet is selected in the Classes list.

7. Find and select Visible in the Members of “Worksheet” list.

8. Notice the use of icons to signify the difference between libraries (the books icon next to Excel in the Search Results), classes (the icons used in the Classes list), properties (the hand and index card icon), and methods (the flying rectangle). You’ll also come across icons for events (lightning bolt) and enumerations.

9. Select the Unprotect method in the Members of “Worksheet” list.

10. Press F1. Notice that a help window appears displaying help for the Unprotect method.

11. Select the UsedRange property in the Members of “Worksheet” list. UsedRange is a property that is represented by a range object.

12. To see what you can do to a range object, click the green “Range” link located underneath the list of Classes.

To summarize, this exercise demonstrated the multiple things the Object Browser is capable of. In addition to a comprehensive listing of all programmable elements in a library, you can search for items, navigate to related help topics, and navigate to related classes and libraries.

Next week we’ll look at the Locals window. Then we’ll be ready to start coding!

Dec 05

While the title of last week’s column (see http://vbatips.com/2007/12/05/essential-office-programming-very-hidden-excel-worksheets/) was “Very Hidden Excel Worksheets,” my primary objective was to introduce two of the Visual Basic Editor’s (VBE) windows. I’ll continue on this tack this week by discussing the Immediate window, a handy window with several uses. The Immediate window is an essential window that I use constantly while writing VBA code.

Figure 1 shows the window configuration I always use when writing VBA. If the Immediate window isn’t visible in your VBE, you can display it by pressing Ctrl + G or selecting View/Immediate Window from the main menu. I’ve changed the background color of the window for reasons I’ll save for a future tip. By default, your Immediate window will have a white background.

n30progfig1.jpg
Figure 1: The VBE with the Project,
Properties, and Immediate windows open. Click to enlarge

One use of the Immediate window is to evaluate individual VBA statements. In this scenario, you precede the VBA statement you want evaluated with a question mark. You can evaluate the values of object properties, functions, and mathematical statements, among other things. For example, enter?ThisWorkbook.Name

In the Immediate window and press Enter. Notice that a couple of things happen. Of course, after you pressed Enter, the name of the current workbook was displayed on the next line. You probably noticed another very useful feature called Intellisense after you entered the period (.) between ThisWorkbook and Name.

Intellisense is a feature that displays a drop-down list of applicable properties and methods given a VBA object. For example, ThisWorkbook is a Workbook object. The items listed in the drop-down box are the properties and methods (collectively referred to as members) of a Workbook object. Properties are represented by the hand and index card icon, while methods have a “flying square” icon next to them.

For now, it’s not important to know the difference between properties and methods, but it would be good to start mentally paying attention to the fact there is a difference. Figure 2 shows an example of Intellisense in the Immediate window.

n30progfig2.jpg
Figure 2: An example of Intellisense
in the Immediate window. Click to enlarge

Why is this so important, you may be wondering? The reason is that it is a very good way to learn about (and experiment with) VBA statements and the various object models (in this case Excel).You can also use the Immediate window to execute VBA statements — rather than displaying the value of a given property, you can change the property’s value. For example, last week we changed the Visible property of a Worksheet object using the Properties window in conjunction with the Project window. You could also perform this feat using the Immediate window. Try this:

1. In the Immediate window enter:
?ThisWorkbook.Worksheets(2).Visible

2. Press Enter. Notice that the value of the Visible property is displayed.

3. In the Immediate window enter (without a question mark):
ThisWorkbook.Worksheets(2).Visible = xlSheetVeryHidden

4. Switch to Excel and notice that the worksheet is very hidden.

5. Switch back to the VBE and enter:
?ThisWorkbook.Worksheets(2).Visible

6. Notice that the Visible property has been changed.

An example of this exercise is shown in Figure 3.

n30progfig3.jpg
Figure 3: Using the Immediate
window to change a property value. Click to enlarge

The final use of the Immediate window I’ll discuss is as an output window using Debug.Print when executing code. Typically you would display diagnostic information in the Immediate window. This can be preferable to using the MsgBox function because if there are several things you want to display, the MsgBox method can grow tiresome rather quickly.As an example, try the following exercise.

1. If there is text in the Immediate window, you can clear it all by putting the cursor in the Immediate window, pressing Ctrl + A, and then pressing Delete.

2. Insert a module by selecting Insert/Module.

3. Enter the following procedure in the module:

Sub WorksheetVisibilityReport()
Dim ws As Worksheet
Debug.Print "***********************"
Debug.Print "xlSheetHidden = 0"
Debug.Print "xlSheetVeryHidden = -1"
Debug.Print "xlSheetVisible = 1"
Debug.Print "***********************" & vbCrLf
For Each ws In ThisWorkbook.Worksheets
Debug.Print "The Visible property of " & ws.Name & " is " _
& ws.Visible & "."
Next
End Sub
4. To execute the procedure, place the caret anywhere inside the procedure and press F5.

A completed example of this exercise is shown in Figure 4.

n30progfig4.jpg
Figure 4: Using Debug.Print to
output to the Immediate window Click to enlarge

Next week I’ll wrap up the discussion of various VBE windows by introducing the Locals window, another great way to learn about and find useful objects that you can manipulate with VBA code.