EVENTS

Properties let the application view and modify an object’s data. Methods let the program invoke the object’s behaviors and perform actions. Together, properties and methods let the program send information (data values or commands) to the object.

In a sense, events do the reverse: They let the object send information to the program. When something noteworthy occurs in an object’s code, the object can raise an event to tell the main program about it. The main program can then decide what to do about the event.

For example, the most commonly used event is probably a Button’s Click event. When the user presses and releases the mouse over a Button object, the object raises its Click event to tell the program that this has happened. Normally the program performs some action in response.

The following sections describe events. They explain how a class declares events and how other parts of the program can catch events.

Declaring Events

A class object can raise events whenever it needs to notify the program of changing circumstances. Normally, the class declares the event using the Event keyword. The following text shows the Event statement’s syntax:

[attribute_list] [accessibility] [Shadows] _
Event event_name([parameters]) [Implements interface.event]

The following sections describe the pieces of this declaration. Some of these are similar to earlier sections that describe constant, variable, and class declarations. By now, you should notice some similarity in the use of the attribute_list and accessibility clauses. For more information on constant and variable declarations, see Chapter 14. For more information on class declarations, refer to the section “Classes” earlier in this chapter.

attribute_list

The attribute_list defines attributes that apply to the event. For example, the following declaration defines a description that the code editor should display for the ScoreAdded event:

Imports System.ComponentModel
 
Public Class Student
    <Description("Occurs when a score is added to the object")>
    Public Event ScoreAdded(test_number As Integer)
    ...
End Class

accessibility

The accessibility value can take one of the following values: Public, Protected, Friend, Protected Friend, or Private. These values determine which pieces of code can catch the event.

The meanings of these keywords are very similar to those of the class accessibility keywords described earlier in this chapter. See the section “Accessibility” earlier in this chapter for details.

Shadows

The Shadows keyword indicates that this event replaces an event in the parent class that has the same name but not necessarily the same parameters.

parameters

The parameters clause gives the parameters that the event will pass to event handlers. The syntax for the parameter list is the same as the syntax for declaring the parameter list for a subroutine or function.

If an event declares a parameter with the ByRef keyword, the code that catches the event can modify that parameter’s value. When the event handler ends, the class code that raised the event can read the new parameter value.

Implements interface.event

If the class implements an interface and the interface defines an event, this clause identifies this event as the one defined by the interface. For example, the IStudent interface shown in the following code defines the ScoreChanged event handler. The Student class implements the IStudent interface. The declaration of the ScoreChanged event handler uses the Implements keyword to indicate that this event handler provides the event handler defined by the IStudent interface.

Public Interface IStudent
    Event ScoreChanged()
    ...
 
End Interface
 
Public Class Student
    Implements IStudent
 
    Public Event ScoreChanged() Implements IStudent.ScoreChanged
    ...
End Class

Raising Events

After it has declared an event, a class raises it with the RaiseEvent keyword. It should pass the event whatever parameters were defined in the Event statement.

For example, the Student class shown in the following code declares a ScoreChange event. The AddScore method shown in the following code makes room for a new score, adds the score to the Scores array, and then raises the ScoreChanged event, passing the event handler the index of the score in the Scores array.

Public Class Student
    Private Scores() As Integer
    ...
    Public Event ScoreChanged(ByVal test_number As Integer)
    ...
    Public Sub AddScore(ByVal new_score As Integer)
        ReDim Preserve Scores(Scores.Length)
        Scores(Scores.Length - 1) = new_score
        RaiseEvent ScoreChanged(Scores.Length - 1)
    End Sub
    ...
End Class

Catching Events

You can catch an object’s events in two ways. First, you can declare the object variable using the WithEvents keyword, as shown in the following code:

Private WithEvents TopStudent As Student

Then in the code editor, click the left drop-down list and select the variable’s name. In the right drop-down list, select the event. This makes the code editor create an empty event handler similar to the following one. When the object raises its ScoreChanged event, the event handler executes.

Private Sub TopStudent_ScoreChanged(test_number As Integer) _
 Handles TopStudent.ScoreChanged
 
End Sub

The second method for catching events is to use the AddHandler statement to define an event handler for the event at run time. First, write the event handler subroutine. This subroutine must take parameters of the proper type to match those defined by the event’s declaration in the class.

The following code shows a subroutine that can handle the ScoreChanged event. Note that the parameter’s name has been changed, but its accessibility (ByRef or ByVal) and data type must match those declared for the ScoreChanged event.

Private Sub HandleScoreChanged(quiz_num As Integer)
 
End Sub

After you build the event handler routine, use the AddHandler statement to assign the routine to a particular object’s event. The following statement makes the HandleScoreChanged event handler catch the TopStudent object’s ScoreChanged event:

AddHandler TopStudent.ScoreChanged, AddressOf HandleScoreChanged

AddHandler is particularly convenient if you want to work with an array of objects. The following code shows how a program might create an array of Student objects and then use the HandleScoreChanged subroutine to catch the ScoreChanged event for all of them:

' Create an array of Student objects.
Const MAX_STUDENT As Integer = 30
Dim students(0 To MAX_STUDENT) As Student
For i As Integer = 0 To MAX_STUDENT
    students(i) = New Student
    AddHandler students(i).ScoreChanged, AddressOf HandleScoreChanged
Next i
...

If you plan to use AddHandler in this way, you may want to ensure that the events provide enough information for the event handler to figure out which object raised the event. For example, you might modify the ScoreChanged event so that it passes a reference to the object raising the event into the event handler. Then the shared event handler can determine which Student object had a score change.

AddHandler lets you add an event handler to an event. Conversely, RemoveHandler lets you remove an event handler from an event. The syntax is similar to the syntax for AddHandler, as shown here:

RemoveHandler TopStudent.ScoreChanged, AddressOf HandleScoreChanged

Note that relaxed delegates allow an event handler to declare its parameters to have different data types from those provided by the event, as long as the new data types are compatible, or to omit the parameters entirely.

For example, suppose the Student class defines a ScoreChanged event that takes an Integer parameter. The following three subroutines could all catch this event. The first matches the event’s parameters precisely. The second version declares its quiz_num parameter to be a Long. Long is compatible with Integer so, when it invokes the event handler, Visual Basic can convert the Integer value into a Long parameter safely. The third version of the event handler declares no parameters so the event’s Integer value is ignored.

Private Sub HandleScoreChanged1(quiz_num As Integer)
        
End Sub
        
Private Sub HandleScoreChanged2(quiz_num As Long)
        
End Sub
        
Private Sub HandleScoreChanged3()
        
End Sub

STRICTLY SPEAKING
The second version works because you can always store an Integer value in a Long parameter. The reverse is not always true: A Long value won’t necessarily fit in an Integer. If the event is declared with a Long parameter but the event handler is declared with an Integer parameter, the result depends on the Option Strict setting. If Option Strict is off, Visual Basic allows the code and tries to convert the Long value into an Integer parameter, possibly crashing at runtime. If Option Strict is on, Visual Basic flags this as an error.

For more information, see the section “Relaxed Delegates” in Chapter 16.

Shared Variables

If you declare a variable in a class with the Shared keyword, all objects of the class share a single instance of that variable. Any instance of the class can get or set the variable’s value. Code outside of the class can use the class itself to get or set the variable’s value.

For example, suppose the Student class declares a shared NumStudents variable and uses it, as shown in the following code:

Public Class Student
    Shared NumStudents As Integer
 
    Public Sub ShowNumStudents()
        MessageBox.Show("# Students: " & NumStudents)
    End Sub
    ...
End Class

In this case, all instances of the Student class share the same NumStudents value. The following code creates a Student object. It uses the class to set the shared NumStudents value and then calls the student’s ShowNumStudents method.

Dim student1 As New Student
Student.NumStudents = 100
student1.ShowNumStudents()

Because all instances of the class share the same variable, any changes to the value that you make using one object are visible to all the others. Figure 23-4 illustrates this idea. Each Student class instance has its own FirstName, LastName, Scores, and other individual data values, but they all share the same NumStudents value.

FIGURE 23-4: If a variable in a class is declared Shared, all instances of a class share the same value.

image

Shared Methods

Shared methods are a little less intuitive than shared variables. Like shared variables, shared methods are accessible using the class’s name. For example, the NewStudent function shown in the following code is declared with the Shared keyword. This function creates a new Student object, initializes it by adding it to some sort of database, and then returns the new object.

Public Class Student
    ...
    ' Return a new Student.
    Public Shared Function NewStudent() As Student
        ' Instantiate the Student.
        Dim new_student As New Student
 
        ' Add the new student to the database.
        ' ...
 
        ' Return the new student.
        Return new_student
    End Function
    ...
End Class

This type of function that creates a new instance of a class is sometimes called a factory method. In some cases, you can use an appropriate constructor instead of a factory method. One time when a factory method is useful is when object creation might fail. If data passed to the method is invalid, some resource (such as a database) prohibits the new object (perhaps a new Student has the same name as an existing Student), or the object may come from more than one place (for example, it may be either a new object or one taken from a pool of existing objects). In those cases, a factory method can return Nothing. A constructor could raise an error, but it cannot return Nothing if it fails.

If you want to force the program to use a factory method rather than create an instance of the object directly, give the class a private constructor. Code that lies outside of the class cannot use the constructor because it is private. It also cannot use the default constructor associated with the New statement because the class has an explicitly defined constructor. The code must create new objects by using the factory method, which can use the private constructor because it’s inside the class.

As is the case with shared variables, you access a shared method by using the class’s name.

The following code declares the student1 variable and initializes it by calling the NewStudent factory method using the class’s name:

Dim student1 As Student = Student.NewStudent()

One oddity of shared methods is that they can use class variables and methods only if they are also shared. If you think about accessing a shared method through the class name, this makes sense. Because you don’t use an instance of the class to call the method, there is no instance to give the method data.

Figure 23-5 illustrates the situation. The shared NewStudent method is contained within the class itself and has access to the NumStudents variable. If it wanted to use a FirstName, LastName, or Scores value, however, it needs to use an instance of the class.

FIGURE 23-5: A shared method can only access other shared variables and methods.

image
..................Content has been hidden....................

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