Hands-on 4: Error handling

advertisement
Database Design 3
Hands-on Excercises
Hands-on 3: Error handling
You have already learned how to deal with compile-time errors. These are errors that violate
the rules of VBA syntax. By compiling your code before you run it, these will not cause errors
at run-time.
Even when your code is correct, run-time errors can occur when the user does something
unexpected or something that you had not planned for. In the first part of this exercise you will
write code to disable the previous and next buttons when the form is in the first or last record
of a form’s recordset. This prevents the user from making this error. However, it is not
always possible to prevent errors in this way so. we need to anticipate the types of error the
user is likely to make and write our own error-handling routines to deal with them. You will
learn how to do this in the second part of the exercise.
Add VBA driven navigation to a form
You will first replace the default record navigation on the Supplier Details form with custom,
VBA-driven navigation buttons created using the Wizard.
Open frmSupplierDetails in design view
In the toolbox, make sure that the Wizard is ON.
Wizard on
Command button tool
Fig. 1
Create the next record button
Fig. 2
533574482
Page 1
of 10
Database Design 3
Hands-on Excercises
In the Command Button Wizard select Record Navigation as the category and Go To Next
Record as the Action.
Click Next and continue through the Wizard to create your button. In the last step, make sure
you give the button a meaningful name, such as cmdNext (note use of using the cmd prefix).
Click the Code button on the toolbar to open the Visual Basic editor to see the code the
Wizard has created for your button.
Fig. 3
Open the properties for the button. Note that the Wizard has attached this event procedure to
the On Click event of the button. If you click the ellipsis [...] it will take you to the associated
code.
Create the other buttons for the navigation system in the same way.
What buttons will be required?
How can you add a button to create a new record?
When you have created all the buttons, remove the record navigation from the bottom of the
form.
533574482
Page 2
of 10
Database Design 3
Hands-on Excercises
Test your buttons.
What happens when you click the next record button when you are already in the last record?
(The last record in the recordset is the blank record)
You get the error message generated by the error code in the event procedure.
You can’t go to the next record because it doesn’t exist!
You will get the same message if you click the previous button when you are in the first
record.
Smart navigation buttons
You will now write code to disable these buttons to prevent the user from clicking them when
the associated action is not possible.
Open the supplier details form in design view.
Next record button
We will first deal with the next record button. We want to grey it out when the form is
displaying a blank new record which is the last record in the recordset. We need to enable or
disable this button when the form is first opened and when the user moves from one record to
another. The event which occurs when this happens is the On Current event for the form.
Display the Event Properties for the form and click the ellipsis next to the On Current event to
open a code stub in the VBA editor.
Enter the following code:
Private Sub Form_Current()
If Me.NewRecord = True Then
cmdPrevRec.SetFocus
cmdNextRec.Enabled = False
End If
End Sub
How does this code work?
The Me object refers to the current form.
Me.NewRecord refers to the form’s NewRecord property which has two settings:
True
The form is currently displaying a new record
False
The form is not currently displaying a new record
We use the value of this property to find out whether the current record is a new record. If it is,
we set the focus to the previous record command button (note you should use the name you
have given to this button in your code) and set the Enabled property of the next record
button to False.
Change to form view and try out the buttons. When you click the new record button your next
record button should grey out.
533574482
Page 3
of 10
Database Design 3
Hands-on Excercises
So it seems that we have successfully prevented the user from using this button when they
are in a new record. However, although we have successfully deactivated the button when
the form is displaying a new record, when we go back to earlier records, the button remains
disabled. We need to add code to re-enable the button when the form is not displaying a new
record.
As the form’s Current event takes place each time the user changes it to a new record we can
do this by adding code to the event procedure we wrote above. The final procedure is shown
below. The code you need to add is highlighted.
Private Sub Form_Current()
If Me.NewRecord = True Then
cmdPrevRec.SetFocus
cmdNextRec.Enabled = False
Else
cmdNextRec.Enabled = True
End If
End Sub
There are now two ‘branches’ to the decision made by the progam. The code following If is
executed if the NewRecord property is True and the code following the Else is executed if it is
false. Note that we don’t need to specifically check whether the property is false. As it can
only have one of two possible values, it must be false if it isn’t true.
Previous record button
We will now add code to the procedure to grey out the previous record button when the user
is in the first record.
The code you need to add to do this is highlighted below:
Private Sub Form_Current()
If Me.NewRecord = True Then
cmdPrevRec.Enabled = True
cmdPrevRec.SetFocus
cmdNextRec.Enabled = False
ElseIf Me.CurrentRecord = 1 Then
cmdNextRec.Enabled = True
cmdNextRec.SetFocus
cmdPrevRec.Enabled = False
Else
cmdNextRec.Enabled = True
cmdPrevRec.Enabled = True
End If
End Sub
Notice that in this code there are three decisions to make:

The form is displaying a new record

The form is displaying the first record in the recordset

Neither of the above.
To choose between three alternatives we need to use Else If.
533574482
Page 4
of 10
Database Design 3
Hands-on Excercises
The condition we test to see whether the form is in the first record is the CurrentRecord
property. This property represents the current record number. If the form is displaying the first
record, the value of this will be 1.
If the form is in the first record we enable the next record button and set the focus to that
button and then disable the previous record button.
We also need to add a line of code to the first test. If we are in a new record we need to set
the enabled property of the previous button to true.
Finally, we need to make sure both buttons are enabled when we are not in the first or last
record so we need to set the enabled property for both buttons to True following the Else.
Interface and Database Engine errors
Default error handling
First we will look at some errors that might occur on a form and at the default Access error
messages generated by these errors.
Open the form frmEmployees and try the following:
Enter a new record without an employee first name. You should see the following error
message:
Enter both names but type a title that is not in the combo list (Sir, for example). You should
see the following error message:
Enter a date later than today’s date in the employee start date field. You should see the
following error message:
None of these messages are very user-friendly and would be likely to confuse an
inexperienced user.
Our task is to trap these errors and deal with them in a more helpful way.
533574482
Page 5
of 10
Database Design 3
Hands-on Excercises
Trappable Errors
Each error that we can write our own custom error handling routine for is known as a
trappable error. Each of these has a unique number associated with it. Access uses this
number to display the default error message (like the messages in the examples above).
You can get a complete list of thes errors by typing “Trappable errors” into the VBA help.
If you want to know the error number of a particular error, click the Help button in the error
message box. Try this for the errors you generated above.
Display the error number for an error
To handle these errors we need to use the On Error event property of the form.
We will first write some VBA code to display these error numbers in a message box.
Change the form to design view and select the On Error event property.
Enter the following code:
Private Sub Form_Error(DataErr As Integer, Response As Integer)
MsgBox DataErr
End Sub
You only needed to enter one line of code between the start and end lines of the code stub.
Note that the procedure has two arguments, both numbers. The DataErr argument is the
error code of the error that occurred. The code displays its value in the message box.
The Response argument tells Access how we want to deal with the error. It can have two
possible values represented by the constants acDataErrDisplay which displays the
default error message and acDataErrContinue which suppresses the default message
and allows us to replace it with our own custom error handling. Its default value is
acDataErrDisplay so as we have not given it a value in the code above, the default error
message will be displayed after the message box shows us the error number.
Change back to form view and make the same data entry errors. This time you will see a
message box that displays the number associated with each error. This is the message box
you will see when you enter a value that is not in the combo list, for example.
Make a note of the number for each error as you will use it in the error handling code below.
Once you click OK in the message box, Access will display the default error message as
described above.
Custom error handling code
We will now add code to the form’s Error event to handle each of the above errors and also
provide a default error handler for unforeseen events that might occur.
533574482
Page 6
of 10
Database Design 3
Hands-on Excercises
Enter the code as shown below into the code stub for the form’s Error event. (Note you can’t
have two different sub procedures associated with the same event so you will need to use the
same code stub as you used for the message box procedure above.
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Select Case DataErr
Case 2237
MsgBox "You have entered a title that is not in the list.
Click the list and choose one of the options shown."
Response = acDataErrContinue
Case 3314
MsgBox "You must enter both first and last name for the
employee. Fill in the missing value."
Response = acDataErrContinue
Case 3317
MsgBox "Employee start date must be today's date or
earlier. Please enter a valid date"
Response = acDataErrContinue
Case Else 'the default error handler displays VBA error
message
Response = acDataErrDisplay
End Select
End Sub
We have used a Case statement in this procedure. It would be possible to write it using a
sequence of If... Else statements but the code would not be as clear. (Try writing an
alternative version using If to prove it for yourself.)
Each Case uses the error values that we noted from the message boxes created earlier. For
each of the three errors we want to handle we have set the value of Response to the constant
acDataErrContinue which means that the default error message will not be displayed and
we can replace it with a more informative message of our own.
If an error that we had not anticipated occurs, the program will drop through to the final Case
Else where the value of response is set to acDataErrDisplay and the default error
message will be shown.
VBA Errors
To deal with VBA runtime errors you need to include error handling in your procedures.
You have already seen examples of this in the VBA-driven command buttons created by the
Control Wizard. An example of the code for a button to move to the next record is shown
below. The lines in the code which relate to error handling are highlighted. As you can see,
this is most of the code!
533574482
Page 7
of 10
Database Design 3
Hands-on Excercises
Private Sub cmdNextRec_Click()
On Error GoTo Err_cmdNextRec_Click
DoCmd.GoToRecord , , acNext
Exit_cmdNextRec_Click:
Exit Sub
Err_cmdNextRec_Click:
MsgBox Err.Description
Resume Exit_cmdNextRec_Click
End Sub
How does the error handling in this code work? It uses a rather outdated programming
concept of GoTo associated with a label. Program statements ending in : are labels, not lines
of executable code. There are two labels in the code above. Which lines of code are they?
The On Error statement in the first line of code announces our intention to handle errors in
this procedure. On error can be used in two ways: GoTo, as in the code above which tells the
program to jump to the specified label, or Resume Next which tells the program to continue
with the next line of code after the line that generated the error.
The label can have any name but conventionally it is created by attaching Err_ to the start of
the procedure name as in the Wizard-generated code above. If an error occurs, the procedure
jumps to the label Err_cmdNextRec_Click and executes the lines that follow. These lines
display an error message and then use the Resume statement to return the program to the
Exit_cmdNextRec_Click label which ends the sub procedure.
The error message is displayed in a message box. It uses the Description property of the Err
object to do this. The Err object also has a Number property which displays the number of the
error. The description is probably more helpful to users!
If no error occurs, the procedure executes the DoCmd.GoToRecord statement, ignores the
label on the following line as it is just a place marker and exits from the sub procedure. If we
did not include the exit statement here, the program would simply continue to execute the
error handling code as well which is obviously not what we want to happen.
Create an error handler
We will now use the same method as the Control Wizard code discussed above to create our
own error handler.
Enter the following code in the modTest standard module that you created earlier.
Public Sub TestErrors()
Dim dblResult As Double
dblResult = 10 / InputBox("Enter a number")
MsgBox "The result is " & dblResult
End Sub
This is just a simple procedure that uses an input box to get a number from the user and then
displays the result of dividing 10 by that number in a message box.
You can run the code by pressing F5 with the insertion point anywhere inside the procedure.
First enter 5 in the input box.
533574482
Page 8
of 10
Database Design 3
Hands-on Excercises
Press OK and the message box will display the result of the calculation 10 / 5 (2, of course!)
Run the procedure again but this time enter 0 in the input box. As you should know, division
by zero is an illegal operation so the code generates an error and you are thrown back into
the VBA editor with the following error message displayed.
If you click the Debug button, the line of code that generated the error will be highlighted.
(Run > Reset will remove the highlight.)
Run the procedure again and this time type the number two (as a word) into the input box.
This generates a different runtime error – type mismatch – as the program is expecting a
number, not a text string.
533574482
Page 9
of 10
Database Design 3
Hands-on Excercises
The two errors generated above are the ones most likely to occur when this procedure is run
so we will now create an error handler to deal with these two errors with a default case for any
errors that we have not anticipated.
Here’s the error handling code you need to add to the procedure.
Err_TestErrors:
Select Case Err.Number
Case 11 'division by zero
dblResult = 0
Resume Next
Case 13 'type mismatch
Resume
Case Else
MsgBox Err.Number & ": " & Err.Description
Resume Exit_TestErrors
End Select
You’ll also need to add an On Error GoTo statement at the start of the procedure and an
Exit label with associated code. Use the previous example to help you with this.
The code uses a Case statement based on the error number – Err.Number. Notice that we
have used comments to explain what each error number refers to. For the division by zero
error we set the result to zero and resume the code at the next line which outputs the result in
the message box. For the type mismatch error we simply try again. Any other error will simply
exit from the procedure.
Once you have tested this error handler you could try modifying it to include a message to
indicate to the user the type of error they have made. You could also try running the code
from a button on a form.
Summary
In this exercise you have:

Created custom error handling procedures for trappable errors

Added VBA error handling to procedures
Handling errors in an informative and user-friendly way is an important part of any application
you create.
533574482
Page 10
of 10
Download