Excel makes it very easy for you to write code that runs when a range of worksheet, chart sheet, and workbook events occur. In previous chapters, we have already seen how to highlight the active row and column of a worksheet by placing code in the Worksheet_SelectionChange event procedure (see Chapter 2). This runs every time the user selects a new range of cells. (You can also find examples demonstrating how to synchronize worksheets in a workbook using the Worksheet_Deactivate and Worksheet_Activate events in Chapter 19.)
It is easy to create workbook, chart sheet, and worksheet events, because Excel automatically provides you with code modules for these objects. However, note that the chart events that are supplied automatically in a chart module apply only to chart sheets, not to embedded charts. If you want to write event procedures for embedded charts, you can do so, but it takes a bit more knowledge and effort.
There are also many other high-level events that can be accessed, for the Application object, for example. These events will be covered later on in Chapters 6 and 14. Events associated with controls and forms will also be treated in the respective chapters. In this chapter we will look, in more detail, at worksheet, chart, and workbook events and related issues.
Event procedures are always associated with a particular object and are contained in the class module that is associated with that object, such as the ThisWorkbook module or the code module behind a worksheet or a UserForm. Events may only be defined in class modules.
The following worksheet event procedures are available in the code module behind each worksheet:
You can use the drop-down list at the top of the code module to create the empty event handler. For example, in a worksheet code module, you can select the Worksheet object from the left-hand drop-down list. This will generate the following lines of code:
Private Sub Worksheet_SelectionChange(ByVal Target As Range) … End Sub
The SelectionChange event is the default event for the Worksheet object. If you want a different event, select the event from the right-hand drop-down list, and delete the preceding lines.
As an alternative to using the drop-downs, you can type the first line of the procedure yourself. The procedure type and arguments must correspond, in number, order, and type (referred to as the signature) with those shown in the preceding code. You are permitted to use different parameter names, if you wish, but it is better to stick with the standard names to avoid confusion.
Most event parameters must be declared with the ByVal keyword, which prevents your code from passing back changes to the object or item referenced by assigning a new value to the parameter. If the parameter represents an object, you can change the object's properties and execute its methods, but you cannot pass back a change in the object definition by assigning a new object definition to the parameter.
Some event procedures are executed before the associated event occurs and have a Cancel parameter that is passed by reference (ByRef). You can assign a value of True to the Cancel parameter to cancel the associated event. For example, you could prevent a user accessing the worksheet shortcut menu by canceling the RightClick event in the Worksheet_BeforeRightClick event procedure:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, _ Cancel As Boolean) Cancel = True End Sub
It is important to turn off event handling in some event procedures to prevent unwanted implicit recursion. For example, if a worksheet Change event procedure changes the worksheet, it will itself trigger the Change event and run itself again. The event procedure will change the worksheet again and trigger the Change event again, and so on.
If only one event procedure is involved, Excel 2000, 2002, and 2003 will usually detect the recursion and terminate it after some hundreds of cycles (Excel 2003 repeats the Change event after about 226 repetitions, whereas Excel 97 will stop after about 40 repetitions). If more than one event procedure is involved, the process can continue indefinitely or until you press Esc or Ctrl+Break enough times to stop each process.
For example, there could be a Calculation event procedure active as well as a Change event procedure. If both procedures change a cell that is referenced in a calculation, both events are triggered into an interactive chain reaction. That is, the first event triggers the second event, which triggers the first event again, and so on. The following Change event procedure makes sure that it does not cause a chain reaction by turning off event handling while it changes the worksheet. It is important to turn event handling back on again before the procedure ends:
Private Sub Worksheet_Change(ByVal Target As Range) Application.EnableEvents = False Range(“A1”).Value = 100 Application.EnableEvents = True End Sub
Application.EnableEvents = False does not affect events outside the Excel Object Model. Events associated with ActiveX controls and UserForms, for example, will continue to occur.
The Worksheet_Calculate event occurs whenever the worksheet is recalculated. It is usually triggered when you enter new data into the cells that are referenced in formulas in the worksheet. You could use the Worksheet_Calculate event to warn you, as you enter new data assumptions into a forecast, when key results go outside their expected range of values. In the worksheet shown in Figure 5-1, you want to know when the profit figure in cell N9 exceeds 600 or is lower than 500.
The following event procedure runs every time the worksheet recalculates, checks cell N9, which has been named FinalProfit, and generates messages if the figure goes outside the required band of values:
Private Sub Worksheet_Calculate() Dim Profit As Double Profit = Sheet2.Range(“FinalProfit”).Value If Profit > 600 Then MsgBox “Profit has risen to ” & Format(Profit, “#,##0.0”) ElseIf Profit < 500 Then MsgBox “Profit has fallen to ” & Format(Profit, “#,##0.0”) End If End Sub
The following chart event procedures are available in the code module for each chart object:
Normally, when you double-click a chart element, you open the formatting dialog box for the element. You could provide some shortcut formatting by trapping the double-click event and writing your own code.
The following event procedure formats three chart elements when they are double-clicked. If, in the chart shown in Figure 5-2, you double-click the legend, it is removed.
If you double-click the chart area (around the outside of the plot area), the legend is displayed. If you double-click a series line with all points selected, it changes the color of the line. If a single point in the series is selected, the data label at the point is toggled on and off:
Private Sub Chart_BeforeDoubleClick(ByVal ElementID As Long, _ ByVal Arg1 As Long, ByVal Arg2 As Long, Cancel As Boolean) Dim theSeries As Series Select Case ElementID Case xlLegend Me.HasLegend = False Cancel = True Case xlChartArea Me.HasLegend = True Cancel = True Case xlSeries 'Arg1 is the Series index 'Arg2 is the Point index (−1 if the entire series is selected) Set theSeries = Me.SeriesCollection(Arg1) If Arg2 = −1 Then With theSeries.Border If .ColorIndex = xlColorIndexAutomatic Then .ColorIndex = 1 Else .ColorIndex = (.ColorIndex Mod 56) + 1 End If End With Else With theSeries.Points(Arg2) .HasDataLabel = Not .HasDataLabel End With End If Cancel = True End Select End Sub
The ElementID parameter passes an identifying number to indicate the element that was double-clicked. You can use intrinsic constants, such as xlLegend, to determine the element. At the end of each case, Cancel is assigned True so that the default double-click event is canceled and the Formatting dialog box does not appear.
Note the use of the keyword Me to refer to the object associated with the code module. Using Me instead of Chart1 makes the code portable to other charts. In fact, you can omit the object reference “Me.” and use “HasLegend =”. In a class module for an object, you can refer to properties of the object without qualification. However, qualifying the property makes it clear that it is a property and not a variable you have created.
If the chart element is a series, Arg1 contains the series index in the SeriesCollection and if a single point in the series has been selected Arg2 contains the point index. Arg2 is 1 if the whole series is selected.
If the whole series is selected, the event procedure assigns 1 to the color index of the series border, if the color index is automatic. If the color index is not automatic, it increases the color index by 1. As there are only 56 colors available, the procedure uses the Mod operator, which divides the color index by 56 and gives the remainder, before adding 1. The only color index value that is affected by this is 56. 56 Mod 56 returns zero, which means that the next color index after 56 is 1.
If a single point is selected in the series, the procedure toggles the data label for the point. If the HasDataLabel property of the point is True, Not converts it to False. If the HasDataLabel property of the point is False, Not converts it to True.
The following workbook event procedures are available. Again, those new to Excel 2003 are highlighted in bold:
Some of the workbook event procedures are the same as the worksheet and chart event procedures. The difference is that when you create these procedures (such as the Change event procedure) in a worksheet or chart, it applies to only that sheet. When you create a workbook event procedure (such as the SheetChange event procedure) it applies to all the sheets in the workbook.
One of the most commonly used workbook event procedures is the Open event procedure. This is used to initialize the workbook when it opens. You can use it to set the calculation mode, establish screen settings, alter the menu structure, decide what toolbars should appear, or enter data into combo boxes or listboxes in the worksheets.
Similarly, the Workbook_BeforeClose event procedure can be used to tidy up when the workbook is closed. It can restore screen and menu settings, for example. It can also be used to prevent a workbook's closure by setting Cancel to True. The following event procedure will only allow the workbook to close if the figure in the cell named FinalProfit is between 500 and 600:
Private Sub Workbook_BeforeClose(Cancel As Boolean) Dim Profit As Double Profit = ThisWorkbook.Worksheets(2).Range(“FinalProfit”).Value If Profit < 500 Or Profit > 600 Then MsgBox “Profit must be in the range 500 to 600” Cancel = True End If End Sub
Note that if you assign True to Cancel in the workbook BeforeClose event procedure, you also prevent Excel from closing.
If you want to make sure that all changes are saved when the workbook closes, but you don&t want the user to be prompted to save changes, you can save the workbook in the BeforeClose event procedure. You can check to see if this is really necessary using the Saved property of the workbook, which will be False if there are unsaved changes:
Private Sub Workbook_BeforeClose(Cancel As Boolean) If Not ThisWorkbook.Saved Then ThisWorkbook.Save End If End Sub
If, on the other hand, you want to discard any changes to the workbook and you don't want users to be prompted to save changes in a workbook when they close it, you can set the Saved property of the workbook to True in the BeforeClose event procedure:
Private Sub Workbook_BeforeClose(Cancel As Boolean) ThisWorkbook.Saved = True End Sub
This fools Excel into thinking that any changes have been saved.
A common need in Excel is to print information in the page header or footer that either comes from the worksheet cells, or is not available in the standard header and footer options. You might want to insert a company name that is part of the data in the worksheet and display the full path to the workbook file.
The full path and file name is available as an option in headers and footers in Excel 2003. You still need to use code like the following to insert text from worksheet cells. You can insert this information using the BeforePrint event procedure to ensure it is always up-to-date in reports. The following procedure puts the text in cell A2 of the worksheet named Profit in the left footer, clears the center footer and puts the full file name in the right footer. It applies the changes to every worksheet and chart in the Workbook:
Private Sub Workbook_BeforePrint(Cancel As Boolean) Dim aWorksheet As Worksheet Dim FullFileName As String Dim CompanyName As String CompanyName = Worksheets(“Profit”).Range(“A2”).Value FullFileName = ThisWorkbook.FullName For Each aWorksheet In ThisWorkbook.Worksheets With aWorksheet.PageSetup .LeftFooter = CompanyName .CenterFooter = “” .RightFooter = FullFileName End With Next aWorksheet Dim aChart As Chart For Each aChart In ThisWorkbook.Charts With aChart.PageSetup .LeftFooter = CompanyName .CenterFooter = “” .RightFooter = FullFileName End With Next aChart End Sub
The footer can be seen in a Print Preview, as shown in Figure 5-3.
In this section you have seen some useful examples of how to utilize event procedures to respond to user actions.
You have been introduced to worksheet, chart, and workbook events. We've also delved a little deeper into the following events:
VBA is essentially an event-driven language, so a good knowledge of the events at your disposal can open up a whole new world of functionality you never knew existed.
To find out more, have a play with the Object Browser, and consult the object model in Appendix A.
18.222.125.171