The next task is to put some code behind the navigation buttons . We could just double-click on each button and then add the code to the click event, but why be normal? I would rather we have all our navigation code in one procedure so we can find it easily. In VB 6 we would have done this with a control array, but VB .NET doesn't support control arrays. What are we to do?
VB .NET does support something called a delegate . If you're familiar with C or C++, you know that you can create a function pointer and then call the function through the pointer. A function pointer is a variable that contains the address of the entry point of the function. A VB .NET delegate is similar to a function pointer in C++. It is a reference type that points to a procedure. The biggest difference is that the delegate is a lot smarter than a simple pointer. In C++ the compiler does nothing to validate the pointer. In other words, you can very easily create a pointer to nowhere. This was often the cause of the General Protection Fault error that plagued the early days of Windows.
In VB .NET, the compiler validates the delegate to make sure it points to a valid procedure. It also validates that the parameter signature of the delegate and the target procedure match. The best part is that you can assign several controls' events to the same delegate. What this means in simple terms is that several controls' click events can all call the same procedure. We no longer need control arrays. The programmer jargon for this is wiring up events. When we attach an event to a procedure we say we've wired the event to procedure x .
We will use the AddHandler statement to create a delegate and assign the control's click event to it. First we have to create the subprocedure that will receive the events. The only requirement of the delegate is that the parameter signatures match the event's and that the procedures not return a value. Since the click event is a standard event it passes two parameters, the sender As Object and the EventArgs object as EventArgs. So let's create our procedure:
Private Sub NavButtons(ByVal sender As Object, ByVal e As EventArgs) End Sub
Don't worry about the contents of the procedure yet. The next question is where to put the AddHandler statements. They have to appear after the buttons are created but before anyone can click a button. The best place is in the constructor of the class. Expand the region labeled Windows Forms Designer Generated Code. Right at the top you will find Public Sub New().This is the default (and only) constructor. Add the AddHandler statements right after the comment that says Add Any Initialization After the InitializeComponent() Call.
Public Sub New() MyBase.New() 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call ' Assign Navigation Buttons to same click event handler AddHandler cmdFirst.Click, AddressOf NavButtons AddHandler cmdPrevious.Click, AddressOf NavButtons AddHandler cmdNext.Click, AddressOf NavButtons AddHandler cmdLast.Click, AddressOf NavButtons End Sub
Notice the use of AddressOf in the statement. This does the same thing as in VB 6 right? Wrong! In VB 6 it returned the address of a procedure, and it could only be a procedure in a standard module. In VB .NET it returns an actual delegate, and it can refer to any procedure that does not return a value, no matter what kind of module it is in. This is not the only way to create a delegate, but the other ways are beyond the scope of this book. Now when we click any of the navigation buttons we will call the NavButtons procedure. How do we know which button was clicked? There is no index parameter to test. The answer is the sender object. It is passed as an object type (which is the root base class of the whole .NET class library) so it can accept any type of object. Since we know we are passing a button, we can typecast the sender to a button and then test the name property to see which button was clicked.