IN THIS CHAPTER
In the preceding chapters, I presented a few examples of VBA event-handler procedures. These procedures are the keys to making your Excel applications interactive. This chapter introduces the concept of Excel events and includes many examples that you can adapt to meet your own needs.
Excel can monitor a variety of events and execute your VBA code when a particular event occurs. This chapter covers the following types of events:
Open
(the workbook is opened or created), BeforeSave
(the workbook is about to be saved), and NewSheet
(a new sheet is added). You must store VBA code for workbook events in the ThisWorkbook
code module.Change
(a cell on the sheet is changed), SelectionChange
(the cell pointer is moved), and Calculate
(the worksheet is recalculated). VBA code for worksheet events must be stored in the code module for the worksheet (for example, the module named Sheet1
).OnTime
and OnKey
. These work differently from other events.Every event-handler procedure must reside in a specific type of code module. Code for workbook-level events is stored in the ThisWorkbook
code module. Code for worksheet-level events is stored in the code module for the particular sheet (for example, the code module named Sheet1
).
In addition, every event-handler procedure has a predetermined name. You can declare the procedure by typing it, but a much better approach is to let the VB Editor do it for you by using the two drop-down controls at the top of the window.
Figure 43.1 shows the code module for the ThisWorkbook
object. Select this code module by double-clicking it in the Project window. To insert a procedure declaration, select Workbook
from the objects list in the upper left of the code window. Then select the event from the procedures list in the upper right. When you do, you get a procedure “shell” that contains the procedure declaration line and an End Sub
statement.
For example, if you select Workbook
from the objects list and Open
from the procedures list, the VB Editor inserts the following (empty) procedure:
Private Sub Workbook_Open()
End Sub
Your event-handler VBA code goes between these two lines.
Some event-handler procedures contain an argument list. For example, you may need to create an event-handler procedure to monitor the SheetActivate
event for a workbook. (This event is triggered when a user activates a different sheet.) If you use the technique described in the previous section, the VB Editor creates the following procedure:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
End Sub
This procedure uses one argument (Sh
), which represents the activated sheet. In this case, Sh
is declared as an Object
data type rather than a Worksheet
data type because the activated sheet also can be a chart sheet.
Your code can, of course, use information passed as an argument. The following example displays the name of the activated sheet by accessing the argument's Name
property. The argument becomes either a Worksheet
object or a Chart
object:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
MsgBox Sh.Name & " was activated."
End Sub
Several event-handler procedures use a Boolean
argument named Cancel
. For example, the declaration for a workbook's BeforePrint
event is
Private Sub Workbook_BeforePrint(Cancel As Boolean)
The value of Cancel
passed to the procedure is FALSE
. However, your code can set Cancel
to TRUE
, which cancels the printing. The following example demonstrates this:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Msg = "Have you loaded the 5164 label stock? "
Ans = MsgBox(Msg, vbYesNo, "About to print… ")
If Ans = vbNo Then Cancel = True
End Sub
The Workbook_BeforePrint
procedure executes before the workbook prints. This procedure displays a message box asking the user to verify that the correct paper is loaded. If the user clicks the No button, Cancel
is set to TRUE
, and nothing prints.
Here's another procedure that uses the workbook's BeforePrint
event. This example overcomes a deficiency in Excel's headers and footers: it's not possible to use the contents of a cell for a page header or footer. This simple procedure is triggered when the workbook is printed. It places the contents of cell A1 in the page header:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
ActiveSheet.PageSetup.CenterHeader = Worksheets(1).Range("A1")
End Sub
Workbook-level events occur for a particular workbook. Table 43.1 lists the most commonly used workbook events, along with a brief description of each.
Table 43.1 Workbook Events
Event | Action That Triggers the Event |
Activate |
The workbook is activated. |
AfterSave |
The workbook was saved. |
BeforeClose |
The workbook is about to be closed. |
BeforePrint |
The workbook (or anything in it) is about to be printed. |
BeforeSave |
The workbook is about to be saved. |
Deactivate |
The workbook is deactivated. |
NewSheet |
A new sheet is created in the workbook. |
Open |
The workbook is opened. |
SheetActivate |
Any sheet in the workbook is activated. |
SheetBeforeDoubleClick |
Any worksheet in the workbook is double-clicked. This event occurs before the default double-click action. |
SheetBeforeRightClick |
Any worksheet in the workbook is right-clicked. This event occurs before the default right-click action. |
SheetChange |
Any worksheet in the workbook is changed by the user. |
SheetDeactivate |
Any sheet in the workbook is deactivated. |
SheetSelectionChange |
The selection on any worksheet in the workbook is changed. |
WindowActivate |
Any window of the workbook is activated. |
WindowDeactivate |
Any workbook window is deactivated. |
The remainder of this section presents examples of using workbook-level events.
One of the most common monitored events is a workbook's Open
event. This event is triggered when the workbook (or add-in) opens and executes the Workbook_Open
procedure. A Workbook_Open
procedure is versatile and is often used for the following tasks:
The following is a simple example of a Workbook_Open
procedure. It uses the VBA Weekday
function to determine the day of the week. If it's Friday, a message box appears to remind the user to perform a file backup. If it's not Friday, nothing happens.
Private Sub Workbook_Open()
If Weekday(Now) = 6 Then
Msg = "Make sure you do your weekly backup!"
MsgBox Msg, vbInformation
End If
End Sub
The following example performs a number of actions when the workbook is opened. It maximizes the workbook window, activates the sheet named DataEntry, selects the first empty cell in column A, and enters the current date into that cell. If a sheet named DataEntry does not exist, the code generates an error.
Private Sub Workbook_Open()
ActiveWindow.WindowState = xlMaximized
Worksheets("DataEntry").Activate
Range("A1").End(xlDown).offset(1,0).Select
ActiveCell.Value = Date
End Sub
The following procedure executes whenever the user activates a sheet in the workbook. The code simply selects cell A1. Including the On Error Resume Next
statement causes the procedure to ignore the error that occurs if the activated sheet is a chart sheet:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
On Error Resume Next
Range("A1").Select
End Sub
An alternative method to handle the case of a chart sheet is to check the sheet type. Use the Sh
argument, which is passed to the procedure:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
If TypeName(Sh) = "Worksheet" Then Range("A1").Select
End Sub
The following procedure executes whenever a new sheet is added to the workbook. The sheet is passed to the procedure as an argument. Because a new sheet can be either a worksheet or a chart sheet, this procedure determines the sheet type. If it's a worksheet, it inserts a date and time stamp in cell A1:
Private Sub Workbook_NewSheet(ByVal Sh As Object)
If TypeName(Sh) = "Worksheet" Then _
Range("A1") = "Sheet added " & Now()
End Sub
The BeforeSave
event occurs before the workbook is actually saved. As you know, choosing File Save sometimes brings up the Save As dialog box — for example, when the file has never been saved or was opened in read-only mode.
When the Workbook_BeforeSave
procedure executes, it receives an argument that enables you to identify whether the Save As dialog box will appear. The following example demonstrates this:
Private Sub Workbook_BeforeSave _
(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If SaveAsUI Then
MsgBox "Use the new file-naming convention."
End If
End Sub
When the user attempts to save the workbook, the Workbook_BeforeSave
procedure executes. If the save operation brings up the Save As dialog box, the SaveAsUI
variable is TRUE
. The preceding procedure checks this variable and displays a message only if the Save As dialog box is displayed. In this case, the message is a reminder about how to name the file.
The BeforeSave
event procedure also has a Cancel
variable in its argument list. If the procedure sets the Cancel
argument to TRUE
, the file is not saved.
The BeforeClose
event occurs before a workbook is closed. This event often is used in conjunction with a Workbook_Open
event handler. For example, use the Workbook_Open
procedure to initialize items in your workbook, and use the Workbook_BeforeClose
procedure to clean up or restore settings to normal before the workbook closes.
If you attempt to close a workbook that hasn't been saved, Excel displays a prompt that asks whether you want to save the workbook before it closes.
The events for a Worksheet
object are some of the most useful. As you'll see, monitoring these events can make your applications perform feats that otherwise would be impossible.
Table 43.2 lists the more commonly used worksheet events, with a brief description of each. Remember that these event procedures must be entered into the code module for the sheet. These code modules have default names like Sheet1, Sheet2, and so on.
Table 43.2 Worksheet Events
Event | Action That Triggers the Event |
Activate |
The worksheet is activated. |
BeforeDoubleClick |
The worksheet is double-clicked. This event occurs before the default double-click action. |
BeforeRightClick |
The worksheet is right-clicked. This event occurs before the default right-click action. |
Change |
Cells on the worksheet are changed by the user. |
Deactivate |
The worksheet is deactivated. |
SelectionChange |
The selection on the worksheet is changed. |
A Change
event is triggered when a user changes any cell in the worksheet. A Change
event is not triggered when a calculation generates a different value for a formula or when an object (such as a chart or a shape) is added to the sheet.
When the Worksheet_Change
procedure executes, it receives a Range
object as its Target
argument. This Range
object corresponds to the changed cell or range that triggered the event. The following example displays a message box that shows the address of the Target
range:
Private Sub Worksheet_Change(ByVal Target As Range)
MsgBox "Range " & Target.Address & " was changed."
End Sub
To get a feel for the types of actions that generate the Change
event for a worksheet, enter the preceding procedure into the code module for a Worksheet
object. After you enter this procedure, activate Excel and, using various techniques, make changes to the worksheet. Every time the Change
event occurs, a message box displays the address of the range that changed.
Unfortunately, the Change
event doesn't always work as expected. For example,
Change
event (as expected), but choosing Home Editing Clear Clear Formats does.Change
event. For example, sorting and goal-seeking operations do not trigger the Change
event. However, operations such as Find and Replace, using the AutoSum button, or adding a Totals row to a table do trigger the event.Change
event.Although the Change
event occurs when any cell on the worksheet changes, most of the time you'll be concerned only with changes that are made to a specific cell or range. When the Worksheet_Change
event-handler procedure is called, it receives a Range
object as its argument. This Range
object corresponds to the cell(s) that changed.
Assume that your worksheet has a range named InputRange
, and you want your VBA code to monitor changes to this range only. No Change
event exists for a Range
object, but you can perform a quick check within the Worksheet_Change
procedure. The following procedure demonstrates this:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim VRange As Range
Set VRange = Range("InputRange")
If Union(Target, VRange).Address = VRange.Address Then
Msgbox "The changed cell is in the input range."
End if
End Sub
This example creates a Range
object variable named VRange
, which represents the worksheet range that you want to monitor for changes. The procedure uses the VBA Union
function to determine whether VRange
contains the Target
range (passed to the procedure in its argument). The Union
function returns an object that consists of all the cells in both its arguments. If the range address is the same as the VRange
address, VRange
contains Target
, and a message box appears. Otherwise, the procedure ends, and nothing happens.
The preceding procedure has a flaw: Target
may consist of a single cell or a range. For example, if the user changes more than one cell at a time, Target
becomes a multicell range. Therefore, the procedure requires modification to loop through all the cells in Target
. The following procedure checks each changed cell and displays a message box if the cell is within the desired range:
Private Sub Worksheet_Change(ByVal Target As Range)
Set VRange = Range("InputRange")
For Each cell In Target
If Union(cell, VRange).Address = VRange.Address Then
Msgbox "The changed cell is in the input range."
End if
Next cell
End Sub
The following procedure demonstrates a SelectionChange
event. It executes whenever the user makes a new selection on the worksheet:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Cells.Interior.ColorIndex = xlNone
With ActiveCell
.EntireRow.Interior.ColorIndex = 35
.EntireColumn.Interior.ColorIndex = 35
End With
End Sub
This procedure shades the row and column of an active cell, making it easy to identify. The first statement removes the background color of all cells. Next, the entire row and column of the active cell is shaded light green. Figure 43.2 shows the shading.
Normally, when the user right-clicks in a worksheet, a shortcut menu appears. If, for some reason, you want to prevent the shortcut menu from appearing, you can trap the RightClick
event. The following procedure sets the Cancel
argument to TRUE
, which cancels the RightClick
event — and, thus, the shortcut menu. Instead, a message box appears:
Private Sub Worksheet_BeforeRightClick _
(ByVal Target As Range, Cancel As Boolean)
Cancel = True
MsgBox "The shortcut menu is not available."
End Sub
So far, the events discussed in this chapter are associated with an object (Application
, Workbook
, Sheet
, and so on). This section discusses two additional events: OnTime
and OnKey
. These events are not associated with an object. Instead, you access them by using methods of the Application
object.
The OnTime
event occurs at a specified time. The following example demonstrates how to program Excel to beep and then display a message at 3 p.m.:
Sub SetAlarm()
Application.OnTime 0.625, "DisplayAlarm"
End Sub
Sub DisplayAlarm()
Beep
MsgBox "Wake up. It's time for your afternoon break!"
End Sub
In this example, the SetAlarm
procedure uses the OnTime
method of the Application
object to set up the OnTime
event. This method takes two arguments: the time (0.625
, or 3 p.m., in the example) and the procedure to execute when the time occurs (DisplayAlarm
in the example). In the example, after SetAlarm
executes, the DisplayAlarm
procedure is called at 3 p.m., bringing up the message.
Most people find it difficult to think of time in terms of Excel's time numbering system. Therefore, you may want to use the VBA TimeValue
function to represent the time. TimeValue
converts a string that looks like a time into a value that Excel can handle. The following statement shows an easier way to program an event for 3 p.m.:
Application.OnTime TimeValue("3:00:00 pm"), "DisplayAlarm"
If you want to schedule an event that's relative to the current time — for example, 20 minutes from now — you can write an instruction like this:
Application.OnTime Now + TimeValue("00:20:00"), "DisplayAlarm"
You also can use the OnTime
method to schedule a procedure on a particular day. Of course, you must keep your computer turned on, and Excel must be running.
While you work, Excel constantly monitors what you type. As a result, you can set up a keystroke or a key combination that — when pressed — executes a particular procedure.
The following example uses the OnKey
method to set up an OnKey
event. This event essentially reassigns the PgDn and PgUp keys. After the Setup_OnKey
procedure executes, pressing PgDn executes the PgDn_Sub
procedure, and pressing PgUp executes the PgUp_Sub
procedure. The next effect is that pressing PgDn moves down one row, and pressing PgUp moves up one row:
Sub Setup_OnKey()
Application.OnKey "{PgDn}", "PgDn_Sub"
Application.OnKey "{PgUp}", "PgUp_Sub"
End Sub
Sub PgDn_Sub()
On Error Resume Next
ActiveCell.Offset(1, 0).Activate
End Sub
Sub PgUp_Sub()
On Error Resume Next
ActiveCell.Offset(-1, 0).Activate
End Sub
By executing the following procedure, you cancel the OnKey
events, and the keys return to their normal functions:
Sub Cancel_OnKey()
Application.OnKey "{PgDn}"
Application.OnKey "{PgUp}"
End Sub