Welcome to SofiaDev.Org's Blogs Sign in | Join | Help

Generic event-handlers in .NET 2.0

I was writing-up an event handler with custom event arguments, getting ready for defining the event args class along with a delegate for the event definition (as you’re supposed to in .NET 1.1), when I’ve noticed that in VS intellisense is helping me with an advice saying that I can use a generic event argument. Cool !!!

So I’ve decided to take a couple of minutes off and see what’s can I do with .NET 2.0.

In .NET 1.x, we had to do the following:
  1. Define a delegate type that holds the signature for the even type.
  2. Define a custom class that inherits from System.EventArgs.
  3. Define a class that will raise the the event instance
  4. And encapsulate an event of the delegate type in the class from #2.
  5. Attach event handlers for the event
  6. And then consume the event

Here’s a simple event definition compatible with MS.NET 1.1 (and still working in 2.0)

 

using System;

namespace SofiaDev.Events.dn11
{
      public delegate void CustomEventHandler(object sender, CustomEventArgs e);

      /// <summary>
      /// Event Args class. Holds data, specific for the event instance.
      /// </summary>
      public class CustomEventArgs : System.EventArgs
      {
            private DateTime m_dtEventSend;
            private string m_strMessage;
           

            /// <summary>
            /// returns a message send to the mesasge subscriber.
            /// </summary>
            public string Message
            {
                  get { return m_strMessage; }
            }
            /// <summary>
            /// another specific field for the event subscriber
            /// </summary>
            public DateTime DateEventSend
            {
                  get { return m_dtEventSend;  }

            }           

            /// <summary>
            /// Initializes a new instance of the custom event arg instance
            /// </summary>
            public CustomEventArgs(string message, DateTime dateEventSend)
            {
                  m_strMessage = message;
                  m_dtEventSend = dateEventSend;
            }
      }
}

 

So that’s the event args definition and the delegate.
Then we need the class that does the incapsulation and raises the event. 

Like this:

 

using System;
using
System.ComponentModel;
using
System.Threading;
 

namespace SofiaDev.Events.dn11
{
      /// <summary>
      /// Class that raises events of the type we've defined in dn11_Event.cs
      /// </summary>
      public class SomeClassThatRaisesEvent
      {
            /// <summary>
            /// This is the event subscription hook. You should attach you're handler to it. When the moment is right, the class will
            /// call your handler with the appropriate event args data.
            /// </summary>
            public event CustomEventHandler CustomEvent;
           

            /// <summary>
            /// This method actually does the calling. If the event handler queue is not empty, it should call
            /// the delegate, passing the appropriate event arguments.
            /// </summary>
            /// <param name="e"></param>
            protected virtual void OnCustomEvent(CustomEventArgs e)
            {
                  if ( null!=CustomEvent )
                  {
                        CustomEvent(this, e);
                  }
            }
           

            /// <summary>
            /// calls a method that will return in 2 seconds
            /// </summary>
            public void CallEventhandlersIn2Seconds()
            {
                  BackgroundWorker bgw = new BackgroundWorker();
                  bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
                  bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
 

                  bgw.RunWorkerAsync();
            }
 

            /// <summary>
            /// Actual work being done here.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void bgw_DoWork(object sender, DoWorkEventArgs e)
            {
                  Thread.Sleep(2000);
            }

 
            /// <summary>
            /// we'll call the event handler from here.
            /// </summary>

            void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {

                  string strMessage = string.Format("hello from the second thread at {0}", DateTime.Now.ToString("dd-MM-yyyy hh:mm:ss"));
                 

                  //init the custom event args instance with specific data
                  CustomEventArgs args = new CustomEventArgs(strMessage, DateTime.Now);
                 

                  //call our internal method that does the actual call of the events on clients.
                  OnCustomEvent(args);
            }
      }
}

 

In this one, we have a couple of additional methods that will help us demonstrate the event call itself.  Check out the comments for more info.

Then we can use the whole thing. Like this:
 

using System;
using
System.Collections.Generic;
using
System.Text;
using
System.Threading;
using
SofiaDev.Events.dn11;

namespace EventArgs
{
      class Program
      {
            static bool StillWaiting = true;           

            static void Main(string[] args)
            {
                  //init the instance that will we're gonna be waiting on.
                  SomeClassThatRaisesEvent instance = new SomeClassThatRaisesEvent();
 

                  //attach an event handler of our own
                  instance.CustomEvent += new CustomEventHandler(instance_CustomEvent);                 

                  //call method that will call us in a couple of secs (when he's ready
                  instance.CallEventhandlersIn2Seconds(); 

                  Console.WriteLine("about to start waiting");

                 

                  while ( StillWaiting )
                  {
                        Console.Write(".");
                  }

                
                  Console.WriteLine("\n\nExiting!!!");
            }

             static void instance_CustomEvent(object sender, CustomEventArgs e)
            {
                  Console.WriteLine("\nCall returned. ");
                  Console.WriteLine("Message: {0},\ndate: {1}", e.Message, e.DateEventSend.ToString());

 
                  StillWaiting = false;
            }
      }
}

 

The problem here is that when you author an extensive library, it takes a lot of time to do the same code over and over again.

The new thing in .NET 2.0, is the possibility to use a generic delegate (there is one within the .NET BCL already) and use it everywhere. To be able to do that, you only need to define your custom event class holding the data specific to your event. In fact we can use the same class declaration as in the .NET 1.1 example and still be able to use a generic delegate as the event type.

Here’s how the same class will look with a generic event declaration:

 

using System;

namespace SofiaDev.Events
{
      /// <summary>
      /// same class as in the .NET 1.1 example
      /// </summary>
      public class SomeClassThatRaisesEvent
      {
            /// <summary>
            /// Only this time the event handler will have a generic definition
            /// </summary>
            public EventHandler<CustomEventArgs> CustomEvent;

          
            protected void OnCustomEvent(object sender, CustomEventArgs e)
            {
                  if (null != CustomEvent)
                        CustomEvent(this, e);
            }
      }
}

 

An example solution with both ways of doing the event declaration is available as an attachment.



Published 02 Май 2006 10:33 by branimir
Attachment(s): EventArgs.zip

Comments

No Comments

Anonymous comments are disabled