Practical Example
PETRAS Timesheet
In our practical example for this chapter, we add an application-level event handling class to our PETRAS timesheet application that will make two significant changes. First, it will enable us to convert the time-entry workbook into an Excel template. This will simplify creation of new time-entry workbooks for new purposes as well as allow multiple time entry workbooks to be open at the same time. Second, the event handler will automatically detect whether a time entry workbook is active and enable or disable our toolbar buttons accordingly. Chapter 7
The Template
A template workbook reacts differently than a normal workbook when opened using the Excel Workbooks.Open method. A normal workbook will simply be opened. When a template workbook is opened a new, unsaved copy of the template workbook will be created. To create a template workbook from a normal workbook, choose File > Save As from the Excel menu and select the Template entry from the Save as type drop-down. As soon as you select the Template option, Excel will unhelpfully modify the directory where you are saving your workbook to the Office Templates directory, so don't forget to change this to the location where you are storing your application files.After we begin using a template workbook, the user has complete control over the workbook filename. We will determine whether a given workbook belongs to us by checking for the unique named constant setIsTimeSheet that we have added to our template workbook for this purpose.A template workbook combined with an application-level event handler enables us to support multiple instances of the time entry workbook being open simultaneously. This might be needed, for example, if there is a requirement to have a separate time sheet for each client or project.Moving to a template user interface workbook also requires that we give the user a way to create new time sheet workbooks, because it is no longer a simple matter of opening and reusing the same fixed timesheet workbook over and over. In Figure 7-2, note the new toolbar button labeled New Time Sheet. This button enables the user to create new instances of our template.
Figure 7-2. The PETRAS Toolbar with the New Time Sheet Button
[View full size image]

Listing 7-21. The NewTimeSheet Procedure
We turn off screen updating and call InitGlobals to ensure that our global variables are properly initialized. We then simply open the template workbook and turn screen updating back on. When you open a template workbook from VBA, it is treated differently than a normal workbook. Rather than opening PetrasTemplate.xlt, a new copy of PetrasTemplate.xlt, called PetrasTemplate1, is created. Each time the user clicks the New Time Sheet button, he gets a completely new, independent copy of PetrasTemplate.xlt.The act of opening the template triggers the NewWorkbook event in our event handing class. This event performs all the necessary actions required to initialize the template. This event procedure is shown in the next section.
Public Sub NewTimeSheet()
Application.ScreenUpdating = False
InitGlobals
Application.Workbooks.Add gsAppDir & gsFILE_TIME_ENTRY
Application.ScreenUpdating = True
End Sub
The Application-Level Event Handler
Within our application-level event handling class, we encapsulate many of the tasks that were previously accomplished by procedures in standard modules. For example, the MakeWorksheetSettings procedure and the bIsTimeEntryBookActive function that we encountered in Chapter 5 Function, General and Application-Specific Add-ins are now both private procedures of the class. We will describe the layout of the class module in Listing 7-22, then explain what the pieces do, instead of showing all of the code here. You can examine the code yourself in the PetrasAddin.xla workbook of the sample application for this chapter on the CD, and are strongly encouraged to do so.
Listing 7-22. Class Module Layout of the CAppEventHandler Class
Module-Level Variables
Class Event Procedures
Private WithEvents mxlApp As Excel.Application
Class Method Procedures
Class_Initialize
Class_Terminate
mxlApp_NewWorkbook
mxlApp_WorkbookOpen
mxlApp_WindowActivate
mxlApp_WindowDeactivate
Class Private Procedures
SetInitialStatus
Because the variable that holds a reference to the instance of the CAppEventHandler class that we use in our application is a public variable, we use the InitGlobals procedure to manage it. The code required to do this is shown below.In the declarations section of the MGlobals module:
EnableDisableToolbar
MakeWorksheetSettings
bIsTimeEntryBookActive
bIsTimeEntryWorkbook
In the InitGlobals procedure:
Public gclsEventHandler As CAppEventHandler
The InitGlobals code checks to see whether the public gclsEventHandler variable is initialized and initializes it if it isn't. InitGlobals is called at the beginning of every nontrivial entry-point procedure in our application, so if anything causes our class variable to lose state, it will be instantiated again as soon as the next entry-point procedure is called. This is a good safety mechanism.When the public gclsEventHandler variable is initialized, it causes the Class_Initialize event procedure to execute. Inside this event procedure, we initialize the event handling mechanism by setting the class module-level WithEvents variable to refer to the current instance of the Excel Application, as follows:
' Instantiate the Application event handler
If gclsEventHandler Is Nothing Then
Set gclsEventHandler = New CAppEventHandler
End If
Similarly, when our application is exiting and we destroy our gclsEventHandler variable, it causes the Class_Terminate event procedure to execute. Within this event procedure we destroy the class reference to the Excel Application object by setting the mxlApp variable to Nothing.All the rest of the class event procedures, which are those belonging to the mxlApp WithEvents variable, serve the same purpose. They "watch" the Excel environment and enable or disable our toolbar buttons as appropriate when conditions change.Disabling toolbar buttons when they can't be used is a much better user interface technique than displaying an error message when the user clicks one in the wrong circumstances. You don't want to punish the user (that is, display an error message in response to an action) when he can't be expected to know he has done something wrong. Note that we always leave the New Time Sheet and Exit PETRAS toolbar buttons enabled. The user should always be able to create a new timesheet or exit the application.In addition to enabling and disabling the toolbar buttons, the mxlApp_NewWorkbook and mxlApp_WorkbookOpen event procedures detect when a time entry workbook is being created or opened for the first time, respectively. At this point they run the private MakeWorksheetSettings procedure to initialize that time entry workbook. All of the mxlApp event procedures are shown in Listing 7-23. As you can see, the individual procedures are very simple, but the cumulative effect is very powerful.
Set mxlApp = Excel.Application
Listing 7-23. The mxlApp Event Procedures
The full power of having an event handling class in your application is difficult to convey on paper. We urge you to experiment with the sample application for this chapter to see for yourself how it works in a live setting. Double-click the PetrasAddin.xla file to open Excel and see how the application toolbar behaves. Create new timesheet workbooks, open non-timesheet workbooks and switch back and forth between them. The state of the toolbar will follow your every action.It is also educational to see exactly how much preparation the application does when you create a new instance of the timesheet workbook. Without the PetrasAddin.xla running, open the PetrasTemplate.xlt workbook and compare how it looks and behaves in its raw state with the way it looks and behaves as an instance of the timesheet within the running application.
Private Sub mxlApp_NewWorkbook(ByVal Wb As Workbook)
If bIsTimeEntryWorkbook(Wb) Then
EnableDisableToolbar True
MakeWorksheetSettings Wb
Else
EnableDisableToolbar False
End If
End Sub
Private Sub mxlApp_WorkbookOpen(ByVal Wb As Excel.Workbook)
If bIsTimeEntryWorkbook(Wb) Then
EnableDisableToolbar True
MakeWorksheetSettings Wb
Else
EnableDisableToolbar False
End If
End Sub
Private Sub mxlApp_WindowActivate(ByVal Wb As Workbook, _
ByVal Wn As Window)
' When a window is activated, check to see if it belongs
' to one of our workbooks. Enable all our toolbar controls
' if it does.
EnableDisableToolbar bIsTimeEntryBookActive()
End Sub
Private Sub mxlApp_WindowDeactivate(ByVal Wb As Workbook, _
ByVal Wn As Window)
' When a window is deactivated, disable our toolbar
' controls by default. They will be re-enables by the
' WindowActivate event procedure if required.
EnableDisableToolbar False
End Sub
PETRAS Reporting
The PETRAS reporting application has been modified in much the same way, and for the same reasons, as the PETRAS timesheet add-in. By adding a class module to handle application-level events, we can enable the user to have multiple consolidation workbooks open at the same time and switch between them using the new Window menu, as shown in Figure 7-3.
Figure 7-3. The PETRAS Reporting Menu Bar with the New Window Menu
