How To: Use Events In .NET

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Frinavale
    Recognized Expert Expert
    • Oct 2006
    • 9749

    How To: Use Events In .NET

    Background
    An Event is a message sent out by an object to notify other objects that an action/trigger/state-change (ie. an event) has taken place. Therefore, you should use an event when an object's state change requires other objects to change as well.

    The object that sends an event is known as the event sender.
    The object that receives (or listens for) the event is known as the event receiver.

    Events implement the "Observer Design Pattern". This pattern contains two key objects: a subject (the event sender), and an observer (the event receiver). The subject may have one or many observers.

    Delegates are used in order to communicate between the sender and the receiver objects since the sender does not know what object type may be receiving (or handling) the event. The Delegate connects the Event with it's receiver (or handler).

    Recall that, in .NET, Delegates are classes that serve as function pointers. They hold a reference (memory location) to a method. Also remember that Delegates can only hold references to methods that match its signature.

    The Delegate identifies the method that provides the response to the event and holds the event data.

    Using Generic Events (EventHandler Delegate)

    A generic event handler delegate has been made available in the.NET Framework 2.0. It's referred to as EventHandler.

    The EventHandler delegate holds a reference to a method that handles events raised which do not contain event data. In other words, it represents the method that will handle a typical event.

    The EventHandler delegate's signature is as follows:
    [code=vbnet]
    Public Delegate Sub EventHandler(By Val sender As Object, ByVal e As EventArgs)[/code][code=cpp]
    public delegate void EventHandler(Ob ject sender, EventArgs e)
    [/code]
    • The "sender" parameter is the object raising the event.
    • The "e" parameter is the event arguments that are passed back to the receiving object and contain data about the event. Since the event doesn't generate event data, this parameter is an instance of EventArgs.
    • If you require the event to generate event data (data that can be used by the receiving object in order to process something), then you cannot use the EventHandler. Instead you must use the EventHandler<T> . This is explained in more detail in the next section.


    So if you needed to raise an event that does not generate event data you would declare your event as:
    [code=vbnet]
    Public Event NoDataEventHand ler As EventHandler
    'EventHandler is the delegate that holds a reference to a method with the signature: (Object sender, EventArgs e)
    'You do not need to create a delegate called EventHandler as this is now part of the System class
    [/code]
    Then to raise the event in your sender class would do the following:
    [code=vbnet]
    Private Sub OnMyEvent()
    RaiseEvent NoDataEventHand ler(Me, New EventArgs())
    End Sub
    [/code]


    Using Events That Return Event Data (EventHandler<T > Delegate)
    If, when you raise the event, you need to pass data to the receiving objects, you cannot use the EventHandler delegate. Instead you need to use EventHandler<T> .

    Since your event generates event data, you must first create a custom type to hold this data. This custom type has to be derived from EventArgs.
    [code=vbnet]
    Public Class MyObjectsEventA rgs
    Inherits EventArgs
    Private _theEventData As String
    Public Sub New(ByVal eventData As String)
    _theEventData = eventData
    End Sub
    Public Property Message() As String
    Get
    Return _theEventData
    End Get
    Set(ByVal value As String)
    _theEventData = value
    End Set
    End Property
    End Class
    [/code]

    Now, because you cannot use the generic EventHandler, you need to create a Delegate using the EventHandler<T> delegate instead:
    [code=vbnet]
    Public Delegate Sub XEventHandler(O f MyObjectsEventA rgs)(ByVal sender as Object, ByVal e As MyObjectsEventA rgs)
    'Note that MyObjectsEventA rgs is the custom type derived from EventArgs used to pass the event data to the receiving class.
    [/code]
    Remember that you have to declare Delegates out side of your Class that raises the event. Also, note that this is a Public Delegate. It needs to be Public in order for the receiving object to be able to access it.

    If you declare you Delegate as:
    [code=vbnet]
    Public Delegate Sub XEventHandler(B yVal sender as Object, ByVal e As MyObjectsEventA rgs)
    'Note that MyObjectsEventA rgs is the custom type derived from EventArgs used to pass the event data to the receiving class.
    [/code]
    You will receive a warning from the compiler (run using code analysis):
    Originally posted by compiler
    ... generates EventHandler delegates for events that use the following pattern: Event MyEvent(ByVal Sender As Object, ByVal e As MyEventArgs). Change the event that defines EventHandler 'EventHandler' to use EventHandler<T> ..."
    It is complaining that you are declaring a Delegate that uses the EventHandler's signature except that you are not using EventArgs in the signature.

    Now that you have a delegate to use to link the sender and receiver classes, you can declare an event as:
    [code=vbnet]
    Public Event SendMessageEven t As XEventHandler(O f MyObjectsEventA rgs)
    [/code]
    Remember that you declare your event inside of your sender class. It should also be made public for the receiver class to handle.

    To raise the event in your sender code you would do the following:
    [code=vbnet]
    Private Sub OnMyEvent(ByVal message As String)
    RaiseEvent SendMessageEven t(Me, New MyObjectsEventA rgs(message))
    End Sub
    [/code]

    In your Receiving Class you need to create a method that matches the signature of the delegate in order to handle the event:
    [code=vbnet]
    Private Sub TestEventSendin g_theEvent(ByVa l sender As Object, ByVal e As MyObjectsEventA rgs) Handles TestEventSendin g.SendMessageEv ent
    'Note that "TestEventSendi ng" is the instance of the Sender Object used in my Receiving Object
    Label_Message = e.Message
    'e is an instance of the MyObjectsEventA rgs class we implemented to be able to send event data to the receiving object for it to use
    'Recall that Message is a property of MyObjectsEventA rgs that we defined to hold our event data
    End Sub
    [/code]

    References and Resources
    EventHandler Generic Delegate
    EventHandler(TE ventArgs) Generic Delegate
  • Plater
    Recognized Expert Expert
    • Apr 2007
    • 7872

    #2
    I am not sure how this applies in VB.NET, but I know how to handle it in C#.

    If you have a custom event in your class called say "MyEvent", if you trigger that event without an event handler attached, an exception is thrown. BUT, you can check for null on the event first to know if it is safe to fire the event.

    Example(C#)
    [code=c#]
    public delegate void NotifyStatusHan dler(string Status);

    public class MyClass
    {
    public event NotifyStatusHan dler NotifyStatus;
    private void _NotifyStatus(s tring Status)
    {
    if (NotifyStatus != null)
    {
    NotifyStatus(St atus);
    }
    }
    public void TriggerEvent()
    {
    _NotifyStatus(" testing");
    }
    }
    [/code]

    Instead of calling the event directly to trigger it, I call a private function that checks to see if there is a delegate attached first or not

    Comment

    • vekipeki
      Recognized Expert New Member
      • Nov 2007
      • 229

      #3
      Good practice would also be to assign the event handler to a local, temporary variable before checking if it is null. This way you can avoid an exception in case some other thread unsubscribes from the handler just after you have checked it.

      Code:
          private void _NotifyStatus(string Status)
          {
              // get a local reference
              NotifyStatusHandler handler = NotifyStatus;
      
              if (handler != null)
              {
                  // even if NotifyStatus is null in this moment,
                  // we are sure that 'handler' is non-null
                  handler (Status);
              }
          }

      Comment

      • vkbishnoi
        New Member
        • Oct 2009
        • 6

        #4
        How to call event or delegate asynchronously in C#

        If we call Delegate.Invoke () then the handler will get called synchronously. Means till the handler function is completed the control will not return back.

        Code:
        delegate void Foo();
        
        Foo evtFoo;
        
        evtFoo.Invoke(); //This will not return until the associated handler function is completed
        In order to call it async, use BeginInvoke(). This function is coupled with EndInvoke(). EndInvoke() will wait till the handler operation is completed. In case you do not want to use EndInvoke() then BeginInvoke() accepts one parameter as function callback. This function will get called once the handler operation is completed.

        Code:
        evtFoo.BeginInvoke(MyCallback, null);
        MyCallback should have a signature like this

        Code:
        void MyCallback(IAsynResult ar);

        Comment

        Working...