Gate to Delegates in VB.NET
- Arun Nair, MCSD, MCSE

Visual Basic .NET has not only extended the versatility of the Visual Basic language but also harnessed the power of the .NET framework, the new "Delegate" feature is one of them. So now you don't get the sneer of the self proclaimed C++ or Java programmers looking down on VB programmers as mere mortals!

More about Delegates, what is the closest you can think of a Delegate? Well a Delegate is an entity that is entrusted with the task of representation, assign or passing on information. In code sense, it means a Delegate is entrusted with a Method to report information back to it when a certain task(which the Method expects) is accomplished outside the Method's class.

Now does that sound familiar to you? Well, think Event Handlers and you got Delegates into the picture. Let me demystify, when a Mouse move event occurs its the Form_MouseMove method that is invoked. But who does the work of passing the invokation from the event to the method? Its the MouseEventHandler Delegate that is entrusted with representing the Form_MouseMove method and whose job is report back to Form_MouseMove when a MouseMove event occurs. Now delegates are Typesafe which means the Delegate can work with a Method or "callback" only if it has the same signature as the Delegate. Delegates form an integral part of the .NET framework and the more you know the better!

Now that I've hopefully clarified what a Delegate is, lets see an example of a Printer to give you a clearer picture. We'll create a Printer class which has basic attributes like the Name of the Printer and the Ink level in the cartridge. Now the Printer class has a delegate CartridgeEventHandler which is invoked by the InkLevel property when the level of ink in the cartridge passes through certain intervals. The delegate in turn invokes the Method it represents or is entrusted with dutifully passing on information to it.

Public Class Printer

            ' ** The attributes of the Printer
            Private m_sName As String ' ** Name of the Printer
            Private m_nInkLevelinCartridge As Short ' ** Ink Level in the cartridge

            ' ** Enumeration of the various ink states of the cartridge
            Public Enum CartridgeState
                        EMPTY
                        LOW
                        HALF
                        FULL
            End Enum

            ' *** IMPORTANT
            ' Declare the Delegate CartridgeEventHandler which is capable of invoking a Method with the same signature, i.e any Method which accepts a CartridgeState enumeration as parameter and that returns a Void

            Public Delegate Sub CartridgeEventHandler(ByVal nState As CartridgeState)
            Private m_dlgCEH As CartridgeEventHandler

            ' ** This method will be used by the Caller to pass the Delegate of the function, i.e the Delegate will carry the Address of the Method
            Public Sub SetDlgRef(ByVal dlgCartridge As CartridgeEventHandler)
                        m_dlgCEH = dlgCartridge
            End Sub

            Public Property Name() As String
                        Get
                                    Return m_sName
                        End Get

                        Set(ByVal Value As String)
                                    If Value.Length = 0 Or Value.Length > 20 Then
                                                m_sName = "Not Specified"
                                    Else
                                                m_sName = Value
                                    End If
                        End Set

            End Property

               '** Read or set the level of ink in the cartridge
                 the range of the ink level is 0-100.     
  
            Public Property InkLevel() As Short
                        Get
                                    Return m_nInkLevelinCartridge
                        End Get

                        Set(ByVal Value As Short)
                                    If Value < 0 Or Value > 100 Then
                                                m_nInkLevelinCartridge = 0
                                    Else
                                                m_nInkLevelinCartridge = Value
                ' ** Invoke the delegate and pass the state according to the ink level
                                                Select Case m_nInkLevelinCartridge
                                                Case 100                                                           
                                                            m_dlgCEH.Invoke(CartridgeState.FULL)
                                                Case 45 To 55
                                                            m_dlgCEH.Invoke(CartridgeState.HALF)
                                                Case 1 To 20
                                                            m_dlgCEH.Invoke(CartridgeState.LOW)
                                                Case 0
                                                            m_dlgCEH.Invoke(CartridgeState.EMPTY)
                                                End Select

                                    End If
                        End Set

            End Property

End Class

Now lets create a Windows form which interfaces with the Printer class. The Windows form has a Status panel which displays the Ink status of the cartridge(Full, Half, Low and Empty) and a Trackbar. We can decrease/increase the level of ink in the cartridge using the Trackbar.

    ' ** Create an instance of the Printer class   
    Dim oPrinter As New Printer

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
       ' ** register the Delegate of CartridgeStatus Method with the Printer class
       ' Notice that when we pass the Address of the Method along with a new instance of the Delegate, New Printer.CartridgeEventHandler       

       oPrinter.SetDlgRef(New Printer.CartridgeEventHandler(AddressOf CartridgeStatus))
        oPrinter.Name = "HP DeskJet 640C"
       ' ** Set the level of ink in the cartridge
        oPrinter.InkLevel = 100

       ' ** set the position of the Trackbar based on the value of the Ink level
        Me.trInkLevel.Value = (oPrinter.InkLevel / 100) * trInkLevel.Maximum
    End Sub

    ' ** The Method that will invoked by the Delegate, Notice the Method signature is the same as that of the Delegate
    ' ** The status panel will be updated with values based on the values that was passed by the delegate
     Private Sub CartridgeStatus(ByVal nCartridgeState As Printer.CartridgeState)
        Dim sState As String

        Select Case nCartridgeState
            Case Printer.CartridgeState.FULL
                sState = "FULL"
            Case Printer.CartridgeState.HALF
                sState = "HALF"
            Case Printer.CartridgeState.LOW
                sState = "LOW"
            Case Printer.CartridgeState.EMPTY
                sState = "EMPTY"
        End Select

        pnCartridgeInk.Text = sState

    End Sub

' ** As the Trackbar is scrolled, the InkLevel property of the Printer is also updated
   Private Sub trInkLevel_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles trInkLevel.Scroll
        oPrinter.InkLevel = (trInkLevel.Value / trInkLevel.Maximum) * 100
    End Sub


Imagine the number of applications you can have with the power of Delegates woven into your programming! To be crystal clear with the usage of Delegates, I suggest you get your hands dirty and try examples on your own and don't delegate it to somebody!

The above example is that of Singlecast Delegates. Now what if you want the Delegate to represent Multiple Methods ...sounds like fun,thats where Multicast Delegates come in. But that is in the next section later on this week. Ciao!