CHAPTER 43
Working with Excel Events

In the preceding chapters, we presented a few examples of VBA event-handler procedures for ActiveX controls. These procedures are the keys to making your Excel applications interactive. This chapter introduces the concept of events for Excel objects and includes many examples that you can adapt to meet your own needs.

Understanding Events

Excel monitors a variety of events and can execute your VBA code when any of these events occur. This chapter covers the following types of events:

  • Workbook events  These occur for a particular workbook. Examples include 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.
  • Worksheet events  These occur for a particular worksheet. Examples include Change (a cell on the sheet is changed), SelectionChange (the selection on the worksheet is changed), 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).
  • Special Application events  The final category consists of two useful application-level events: OnTime and OnKey. These are different from other events because the code isn't in a class module. Rather, you set the events by calling a method of the Application object.

Many events exist at both the worksheet and workbook levels. For example, Sheet1 has an event called Change that fires when any cell on Sheet1 is changed. The workbook has an event called SheetChange that fires every time any cell on any sheet is changed. The workbook version of this event has an additional argument that lets you know which sheet was affected.

Entering Event-Handler VBA Code

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). These modules are added to the project automatically by Excel. You can't add or remove the ThisWorkbook module. To add or remove a sheet's module, you must add or remove the sheet from the workbook.

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 Visual Basic Editor (VBE) 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 and 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.

Snapshot of the best way to create an event procedure is to let the VBE do it for you.

FIGURE 43.1 The best way to create an event procedure is to let the VBE do it for you.

For example, if you select Workbook from the objects list and Open from the procedures list, the VBE 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 VBE 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

Using Workbook-Level Events

Workbook-level events occur for a particular workbook. Table 43.1 lists the most used workbook events along with a brief description of each.

TABLE 43.1 Workbook Events

EventAction That Triggers the Event
ActivateThe workbook is activated.
AfterSaveThe workbook was saved.
BeforeCloseThe workbook is about to be closed.
BeforePrintThe workbook (or anything in it) is about to be printed.
BeforeSaveThe workbook is about to be saved.
DeactivateThe workbook is deactivated.
NewSheetA new sheet is created in the workbook.
OpenThe workbook is opened.
SheetActivateAny sheet in the workbook is activated.
SheetBeforeDoubleClickAny worksheet in the workbook is double-clicked. This event occurs before the default double-click action.
SheetBeforeRightClickAny worksheet in the workbook is right-clicked. This event occurs before the default right-click action.
SheetChangeAny worksheet in the workbook is changed by the user.
SheetDeactivateAny sheet in the workbook is deactivated.
SheetSelectionChangeThe selection on any worksheet in the workbook is changed.
WindowActivateAny workbook window is activated.
WindowDeactivateAny workbook window is deactivated.

The following sections present examples of using workbook-level events.

Using the Open event

One of the most common monitored events is a workbook's Open event. This event is triggered when the workbook opens and executes the Workbook_Open procedure. A Workbook_Open procedure is versatile, and it is often used for the following tasks:

  • Displaying welcome messages
  • Opening other workbooks
  • Activating a specific sheet
  • Ensuring that certain conditions are met; for example, a workbook may require that a particular add-in is installed

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

Using the SheetActivate event

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

Using the NewSheet event

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 _
    Sh.Range("A1").Value = "Sheet added " & Now()
End Sub

Using the BeforeSave event

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.

Using the BeforeClose event

The BeforeClose event occurs before a workbook is closed. This event is often 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.

When a user attempts to close a read-only workbook that has changes, Excel will prompt the user to save a copy of the workbook. In some cases, this is the desired behavior. But if the workbook is designed to be used and not saved, dismissing the Save As dialog is an annoyance and you might end up with copies of the workbook that you don't want because the user became confused. You can use the BeforeClose event, as in the following example, to specify that the workbook has already been saved (even though it hasn't) and avoid that message.

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    Me.Saved = True
End Sub

In Chapter 41, “Creating UserForms,” you saw that the Me keyword in the UserForm's code module referred to the UserForm itself. It works the same in the ThisWorkbook and sheet code modules. In this case, Me refers to the workbook that contains the event procedure.

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.

Working with Worksheet Events

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 would otherwise 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

EventAction That Triggers the Event
ActivateThe worksheet is activated.
BeforeDoubleClickThe worksheet is double-clicked. This event occurs before the default double-click action.
BeforeRightClickThe worksheet is right-clicked. This event occurs before the default right-click action.
ChangeCells on the worksheet are changed by the user.
DeactivateThe worksheet is deactivated.
FollowHyperlinkA hyperlink on the worksheet was clicked.
SelectionChangeThe selection on the worksheet is changed.

Using the Change event

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 group of cells 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, as in the following examples:

  • Changing the formatting of a cell does not trigger the Change event (as expected), but choosing Home ➪ Editing ➪ Clear ➪ Clear Formats does.
  • Pressing Delete generates an event even if the cell is empty at the start.
  • Cells changed via Excel commands may or may not trigger the 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 Total row to a table do trigger the event.
  • If your VBA procedure changes a cell, it does trigger the Change event.

Monitoring a specific range for changes

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 = Me.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 potential 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. As it is, all of the changed cells must be within InputRange. If you still want to act on cells within InputRange, even if some of the changes are not, you have to change the procedure to loop through all of 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 = Me.Range("InputRange")
  For Each cell In Target.Cells
    If Union(cell, VRange).Address = _
      VRange.Address Then
 
      Msgbox "The changed cell is in the input range."
    End if
  Next cell
End Sub

One problem with the preceding procedure is that it displays a message for each cell in InputRange. If you change a lot of cells, you will be closing a lot of messages. You can use the Intersect method to get the cells that two ranges have in common, as in the following procedure.

Private Sub Worksheet_Change( _
  ByVal Target As Excel.Range)
 
  Dim VRange As Range
  Dim cell As Range
  Dim IRange As Range
 
  Set VRange = Me.Range("InputRange")
  Set IRange = Intersect(VRange, Target)
 
  If Not IRange Is Nothing Then
    MsgBox "The range " & IRange.Address & " was changed."
  End If
End Sub

If the two ranges don't have any cells in common, the IRange variable won't contain any cells. You test for that condition with the Is Nothing keywords. We want the If statement to return TRUE when IRange contain cells, so we use the Not keyword to test the opposite of Is Nothing. The construct If Not Object Is Nothing isn't the easiest code to read, but it's used often, and you'll likely encounter it if you read other people's code.

Using the SelectionChange event

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)
 
   Me.Cells.Interior.ColorIndex = xlNone
   With Target
     .EntireRow.Interior.ColorIndex = 35
     .EntireColumn.Interior.ColorIndex = 35
   End With
End Sub

This procedure shades the row and column of the selected cells, making the selected cells easy to identify. The first statement removes the background color of all cells. Next, the entire rows and columns of the selected cells are shaded light green. Figure 43.2 shows the shading.

Snapshot of selecting a cell causings the active cell's row and column to become shaded.

FIGURE 43.2 Selecting a cell causes the active cell's row and column to become shaded.

Using the BeforeRightClick event

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

Using Special Application Events

So far, the events discussed in this chapter are associated with an object like a worksheet. The following sections discuss 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.

Using the OnTime event

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 TimeSerial(15,0,0), "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 (the TimeSerial function is an easy way to get a time, and the hour argument of 15 is 3 p.m.) 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.

You could also use VBA's 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 another 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 either of these:

Application.OnTime Now + TimeSerial(0, 20, 0), _
  "DisplayAlarm"
 
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.

To cancel an OnTime event, you must know the exact time the event was scheduled to run. Then you use the schedule argument of OnTime set to False. OnTime works to the nearest second. If you schedule something for 3 p.m., you can unschedule it with this code:

Application.OnTime TimeSerial(15, 0, 0), _
  "DisplayAlarm", , False

If you scheduled something relative to the current time and want to cancel it, you need to store that time. This code will schedule the event:

TimeToRun = Now + TimeSerial(0, 20, 0)
Application.OnTime TimeToRun, "DisplayAlarm"

The TimeToRun variable can be used to unschedule it, assuming that the variable is still in scope:

Application.OnTime TimeToRun, "DisplayAlarm", , False

Using the OnKey event

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 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
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset