Blue Theme Orange Theme Green Theme Red Theme
 
Discover the top 5 tips for understanding .NET Interop
Home | Forums | Videos | Advertise | Certifications | Downloads | Blogs | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
Nevron Chart
Search :       Advanced Search »
Home » Programming Best Practices » Raising Events, Event Handlers, and the Use of Delegates

Raising Events, Event Handlers, and the Use of Delegates

This article focuses on delegates, how to use them, their relation to events, and their relation to raising events. It is meant to help the beginner learn delegates by example code.

Page Views : 18509
Downloads : 0
Rating :
 Rate it
Level : Intermediate
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
 
Nevron Chart
Become a Sponsor
 Tag Cloud
 Latest Jobs
More ... 
 Latest Interview Questions
More ... 


Events

When working with an IDE like Visual Studio, most events, particularly those involving Windows Forms applications, are nonlinear. That is, you might have to wait for a user to click a button or press a key to then respond to that event. In server applications, you might have to wait (and listen) for an incoming network request. These capabilities are provided by events in the .NET Framework. Any developer will notice the connection between events and delegates in technical documentation. This article will assume a working knowledge of the .NET Framework 2.0 and Visual Studio 2005 (2008), with the intention of defining both events and delegates. While these subjects are treated separately, they are interrelated and can be welded together to gain a sharper understanding of the overall concept of having a method invoked upon an event or a reference to that method.

This first section will focus on events, what they are, and what they are capable of when defined in a class. The following section will then focus on delegates, what they are, how they are declared, instantiated, and used. Again, the reader should note that the underlying concept is the invocation of a method to perform an action based on an event, and a method that is invoked by a reference to that method other than a function pointer.

One kind of member that a class can define is an event. A type that defines an event member allows that type (or instances of that type (class)) to notify other objects that something special has occurred. For example, the Button class offers an event called Click. When a Button object is clicked, one or more objects in an application may want to receive notification about this event in order to perform some action. Events are types that allow this interaction. Specifically, defining an event member means that the class is offering the following capabilities:

  • A type's static method or an object's instance method can register its interest in the type's event.
  • A type's static method or an object's instance method can unregister its interest in the type's event.
  • Registered methods will be notified when the event occurs.

Types can offer this functionality when defining an event because they maintain a list of the registered methods. When an event occurs, the type notifies all of the registered methods in the list. The CLR's event model is based on delegates. A delegate is a type-safe way to invoke a callback's methods. Callback methods are the means by which objects receive the notifications they subscribe to.

An event is a message sent by an object to signal the occurrence of an action. The action could be caused by user interaction, such as a mouse click, or it could be triggered by some other program logic. The object that raises the event is called the event sender. The object that captures and responds to the event is called the event receiver. In event communication, the event sender class does not know which object or method will receive (or handle) the event it raises. What is needed is an intermediary (or pointer-like mechanism) between the source and the receiver. In other words, the object that receives the event cannot validate the source of the event. The .NET Framework defines a special type (Delegate) that provides the functionality of a function pointer. Here is code to exemplify an event and introduce delegates. It is not expected for the reader to first understand how the code works, as it will be explained through different sections of this article.
 

using System;

using System.ComponentModel;

namespace EventSample

   using System;

   using System.ComponentModel;

 

   // Class that contains the data for

   // the alarm event. Derives from System.EventArgs.

   //

   public class AlarmEventArgs : EventArgs

   { 

      private readonly bool snoozePressed ;

      private readonly int nrings;

     

      //Constructor.

      //

      public AlarmEventArgs(bool snoozePressed, int nrings)

      {

         this.snoozePressed = snoozePressed;

         this.nrings = nrings;

      }

 

      // The NumRings property returns the number of rings

      // that the alarm clock has sounded when the alarm event

      // is generated.

      //

      public int NumRings

      {    

         get { return nrings;}     

      }

 

      // The SnoozePressed property indicates whether the snooze

      // button is pressed on the alarm when the alarm event is generated.

      //

      public bool SnoozePressed

      {

         get {return snoozePressed;}

      }

 

      // The AlarmText property that contains the wake-up message.

      //

      public string AlarmText

      {

         get

         {

            if (snoozePressed)

            {

               return ("Wake Up!!! Snooze time is over.");

            }

            else

            {

               return ("Wake Up!");

            }

         }

      } 

   }

 

   // Delegate declaration.

   //

   public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);

 

   // The Alarm class that raises the alarm event.

   //

   public class AlarmClock

   { 

      private bool snoozePressed = false;

      private int nrings = 0;

      private bool stop = false;

 

      // The Stop property indicates whether the

      // alarm should be turned off.

      //

      public bool Stop

      {

         get {return stop;}

         set {stop = value;}

      }

 

      // The SnoozePressed property indicates whether the snooze

      // button is pressed on the alarm when the alarm event is generated.

      //

      public bool SnoozePressed

      {

         get {return snoozePressed;}

         set {snoozePressed = value;}

      }

      // The event member that is of type AlarmEventHandler.

      //

      public event AlarmEventHandler Alarm;

 

      // The protected OnAlarm method raises the event by invoking

      // the delegates. The sender is always this, the current instance

      // of the class.

      //

      protected virtual void OnAlarm(AlarmEventArgs e)

      {

         if (Alarm != null)

         {

            // Invokes the delegates.

            Alarm(this, e);

         }

      }

 

      // This alarm clock does not have

      // a user interface.

      // To simulate the alarm mechanism it has a loop

      // that raises the alarm event at every iteration

      // with a time delay of 300 milliseconds,

      // if snooze is not pressed. If snooze is pressed,

      // the time delay is 1000 milliseconds.

      //

      public void Start()

      {

         for (;;) 

         {

            nrings++;     

            if (stop)

            {

               break;

            }

            else if (snoozePressed)

            {

               System.Threading.Thread.Sleep(1000);

               {

                  AlarmEventArgs e = new AlarmEventArgs(snoozePressed,

                     nrings);

                  OnAlarm(e);

               }

            }

            else

            {

               System.Threading.Thread.Sleep(300);

               AlarmEventArgs e = new AlarmEventArgs(snoozePressed,

                  nrings);

               OnAlarm(e);

            }          

         }

      }

   }

 

   // The WakeMeUp class that has a method AlarmRang that handles the

   // alarm event.

   //

   public class WakeMeUp

   {

      public void AlarmRang(object sender, AlarmEventArgs e)

      {

         Console.WriteLine(e.AlarmText +"\n");

 

         if (!(e.SnoozePressed))

         {

            if (e.NumRings % 10 == 0)

            {

               Console.WriteLine(" Let alarm ring? Enter Y");

               Console.WriteLine(" Press Snooze? Enter N");

               Console.WriteLine(" Stop Alarm? Enter Q");

               String input = Console.ReadLine();

 

               if (input.Equals("Y") ||input.Equals("y")) return;

 

               else if (input.Equals("N") || input.Equals("n"))

               {

                  ((AlarmClock)sender).SnoozePressed = true;

                  return;

               }

               else

               {

                  ((AlarmClock)sender).Stop = true;

                  return;

               }

            }

         }

         else

         {

            Console.WriteLine(" Let alarm ring? Enter Y");

            Console.WriteLine(" Stop Alarm? Enter Q");

            String input = Console.ReadLine();

            if (input.Equals("Y") || input.Equals("y")) return;

            else

            {

               ((AlarmClock)sender).Stop = true;

               return;

            }

         }

      }

   }

  

   // The driver class that hooks up the event handling method of

   // WakeMeUp to the alarm event of an Alarm object using a delegate.

   // In a forms-based application, the driver class is the

   // form.

   //

   public class AlarmDriver

   { 

      public static void Main (string[] args)

      { 

         // Instantiates the event receiver.

         WakeMeUp w= new WakeMeUp();

                 

         // Instantiates the event source.

         AlarmClock clock = new AlarmClock();

 

         // Wires the AlarmRang method to the Alarm event.

         clock.Alarm += new AlarmEventHandler(w.AlarmRang);

 

         clock.Start();

      }

   }

} 

We compile this code, eventsample.cs, with a reference to System.dll:

1.JPG

So, What is a Delegate?

A delegate is a class (that uses the delegate keyword) that can hold a reference to a method. Unlike other classes, a delegate class has a signature, and can hold references to methods that match its signature. A delegate is thus equivalent to a type-safe function pointer or a callback. While delegates have many other uses, this section will focus on the event-handling functionality of delegates. A delegate declaration is sufficient to define a delegate class. The declaration supplies the signature of the delegate, and the CLR provides the implementation. The following example shows an event delegate declaration:

public delegate void AlarmEventHandler(object sender, EventArgs e);

The standard signature of an event handler delegate defines a method that does not return a value, whose first parameter is of type Object and refers to the instance that raises the event, and whose second parameter is derived from type EventArgs and holds the event data. So, a delegate in C# is similar to a function pointer in C or C++. Using a delegate allows us to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code, which can call the referenced method (provided the parameters match), without having to know at compile time which method will be invoked. Unlike function pointers in C or C++, delegates are object-oriented, type-safe, and secure. Just as the CLR does not even have to know which .NET language has been compiled, the CLR knows that managed code emits metadata and IL code. The metadata tables are examined to perform type-safe checking to ensure that proper data is being passed to the proper method, as the IL code is JIT's. In short, a delegate declaration defines a type that encapsulates a method with a particular set of arguments and return type. For static methods, a delegate object encapsulates the method to be called. For instance methods, a delegate object encapsulates both an instance and a method on the methods. Recall that the difference between static and instance methods is that one operates on the type itself and the other operates on an instance of the type. If you have a delegate object and an appropriate set of arguments, you can invoke the delegate with the arguments.
Examine the code below:

using System;

 

// A set of classes for handling a bookstore:

namespace Bookstore

{

   using System.Collections;

 

   // Describes a book in the book list:

   public struct Book

   {

      public string Title;        // Title of the book.

      public string Author;       // Author of the book.

      public decimal Price;       // Price of the book.

      public bool Paperback;      // Is it paperback?

      public Book(string title, string author, decimal price, bool paperBack)

      {

         Title = title;

         Author = author;

         Price = price;

         Paperback = paperBack;

      }

   }

 

   // Declare a delegate type for processing a book:

   public delegate void ProcessBookDelegate(Book book);

 

   // Maintains a book database.

   public class BookDB

   {

      // List of all books in the database:

      ArrayList list = new ArrayList();  

 

      // Add a book to the database:

      public void AddBook(string title, string author, decimal price, bool paperBack)

      {

         list.Add(new Book(title, author, price, paperBack));

      }

 

      // Call a passed-in delegate on each paperback book to process it:

      public void ProcessPaperbackBooks(ProcessBookDelegate processBook)

      {

         foreach (Book b in list)

         {

            if (b.Paperback)

            // Calling the delegate:

               processBook(b);

         }

      }

   }

}

 

// Using the Bookstore classes:

namespace BookTestClient

{

   using Bookstore;

 

   // Class to total and average prices of books:

   class PriceTotaller

   {

      int countBooks = 0;

      decimal priceBooks = 0.0m;

 

      internal void AddBookToTotal(Book book)

      {

         countBooks += 1;

         priceBooks += book.Price;

      }

 

      internal decimal AveragePrice()

      {

         return priceBooks / countBooks;

      }

   }

 

   // Class to test the book database:

   class Test

   {

      // Print the title of the book.

      static void PrintTitle(Book b)

      {

         Console.WriteLine("   {0}", b.Title);

      }

 

      // Execution starts here.

      static void Main()

      {

         BookDB bookDB = new BookDB();

 

         // Initialize the database with some books:

         AddBooks(bookDB);     

 

         // Print all the titles of paperbacks:

         Console.WriteLine("Paperback Book Titles:");

         // Create a new delegate object associated with the static

         // method Test.PrintTitle:

         bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));

 

         // Get the average price of a paperback by using

         // a PriceTotaller object:

         PriceTotaller totaller = new PriceTotaller();

         // Create a new delegate object associated with the nonstatic

         // method AddBookToTotal on the object totaller:

         bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));

         Console.WriteLine("Average Paperback Book Price: ${0:#.##}",

            totaller.AveragePrice());

      }

 

      // Initialize the book database with some test books:

      static void AddBooks(BookDB bookDB)

      {

         bookDB.AddBook("The C Programming Language",

            "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);

         bookDB.AddBook("The Unicode Standard 2.0",

            "The Unicode Consortium", 39.95m, true);

         bookDB.AddBook("The MS-DOS Encyclopedia",

            "Ray Duncan", 129.95m, false);

         bookDB.AddBook("Dogbert's Clues for the Clueless",

            "Scott Adams", 12.00m, true);

      }

   }

}

Now, examine the output: 

Paperback Book Titles:

   The C Programming Language

   The Unicode Standard 2.0

   Dogbert's Clues for the Clueless

Average Paperback Book Price: $23.97

We instantiated the delegate because once the delegate has been declared, a delegate object must be created and associated with a particular method. Like all other objects, a new delegate object is created with a new expression. Herein lies one principle difference between the object-oriented and the procedural C language: in C, when we declare a variable, we are informing the compiler with the type of data that will function as a variable that will have an assigned value. The compiler then allocates the storage needed for that particular data type. When creating a delegate, however, the argument passed to the new expression is special - it is written like a method call, but without the arguments to the method, as shown in the following statement:

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));

When creating a delegate, however, the argument passed to the new expression is special - it is written like a method call, but without the arguments to the method.

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));

That statement creates a new delegate object associated with the static method Test.PrintTitle. The following statement:

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));

creates a new delegate object associated with the non-static method AddBookToTotal on the object totaller. In both cases, this new delegate object is immediately passed to the ProcessPaperbackBooks method. Note that once a delegate is created, the method it is associated with never changes - delegate objects are immutable.

Responding to an Event

The downloadable file, SystemTimer, is a Windows Forms application that contains a progress bar control. The idea is to control this progress bar control by responding to timer events. Timer objects can be used to throw events after a specified number of milliseconds. You simply create a Windows Forms project, drag and drop a progress bar control onto the form's surface, and then declare a Timer object. Download the code into a folder named TimerEvents in the Projects folder of your edition of Visual Studio. Do not try to run the solution file from the Zip archive. When these files are extracted to this new subfolder of the Projects folder, double-click the solution file to see how the progress bar reacts after a certain number of milliseconds pass.
Below is referenced code from Jeffrey Richter's Book "CLR via C#". This code is exemplary, and a student of .NET development should refer to this code continually in order to demystify delegates:

using System;

using System.Windows.Forms;

using System.IO;

 

 

// Declare a delegate type; instances refer to a method that

// takes an Int32 parameter and returns void.

internal delegate void Feedback(Int32 value);

 

 

public sealed class Program {

   public static void Main() {

      StaticDelegateDemo();

      InstanceDelegateDemo();

      ChainDelegateDemo1(new Program());

      ChainDelegateDemo2(new Program());

   }

 

   private static void StaticDelegateDemo() {

      Console.WriteLine("----- Static Delegate Demo -----");

      Counter(1, 3, null);

      Counter(1, 3, new Feedback(Program.FeedbackToConsole));

      Counter(1, 3, new Feedback(FeedbackToMsgBox)); // "Program." is optional

      Console.WriteLine();

   }

 

   private static void InstanceDelegateDemo() {

      Console.WriteLine("----- Instance Delegate Demo -----");

      Program p = new Program();

      Counter(1, 3, new Feedback(p.FeedbackToFile));

 

      Console.WriteLine();

   }

 

   private static void ChainDelegateDemo1(Program p) {

      Console.WriteLine("----- Chain Delegate Demo 1 -----");

      Feedback fb1 = new Feedback(FeedbackToConsole);

      Feedback fb2 = new Feedback(FeedbackToMsgBox);

      Feedback fb3 = new Feedback(p.FeedbackToFile);

 

      Feedback fbChain = null;

      fbChain = (Feedback) Delegate.Combine(fbChain, fb1);

      fbChain = (Feedback) Delegate.Combine(fbChain, fb2);

      fbChain = (Feedback) Delegate.Combine(fbChain, fb3);

      Counter(1, 2, fbChain);

 

      Console.WriteLine();

      fbChain = (Feedback) Delegate.Remove(fbChain,

                           new Feedback(FeedbackToMsgBox));

      Counter(1, 2, fbChain);

   }

 

   private static void ChainDelegateDemo2(Program p) {

      Console.WriteLine("----- Chain Delegate Demo 2 -----");

      Feedback fb1 = new Feedback(FeedbackToConsole);

      Feedback fb2 = new Feedback(FeedbackToMsgBox);

      Feedback fb3 = new Feedback(p.FeedbackToFile);

 

      Feedback fbChain = null;

      fbChain += fb1;

      fbChain += fb2;

      fbChain += fb3;

      Counter(1, 2, fbChain);

 

      Console.WriteLine();

      fbChain -= new Feedback(FeedbackToMsgBox);

      Counter(1, 2, fbChain);

   }

  

   private static void Counter(Int32 from, Int32 to, Feedback fb) {

      for (Int32 val = from; val <= to; val++) {

         // If any callbacks are specified, call them

         if (fb != null)

            fb(val);

      }

   }

 

   private static void FeedbackToConsole(Int32 value) {

      Console.WriteLine("Item=" + value);

   }

 

   private static void FeedbackToMsgBox(Int32 value) {

      MessageBox.Show("Item=" + value);

   }

 

   private void FeedbackToFile(Int32 value) {

      StreamWriter sw = new StreamWriter("Status", true);

      sw.WriteLine("Item=" + value);

      sw.Close();

   }

}

When you compile and execute this code, notice the command line display lines that correspond with the Windows Forms buttons. Now, recall that a delegate indicates the signature of a callback method. The code starts with the declaration of the internal delegate, Feedback. Feedback identifies a method that takes a parameter (an Int32) and returns a void. Now, the Program class defines a private static method named Counter. This method counts integers from the from argument to the to argument. The Counter also takes an fb, which is a reference to a Feedback delegate object. Recall or understand that the CLR requires that any object must be created by calling the new operator. For instance, a Patient object would be created like so:

Patient p = new Patient();

The new operator performs a series of underlying operations, not to mention using the variable (in this case, p) as reference where the constructor's parameters are stored. The Counter loops through all of the integers, and for each integer, if the fb variable is not null, the callback method (specified by the fb variable) is called. This callback method is passed the value of the item being processed, the item number. The callback method can be designed and implemented to process each item in any manner deemed appropriate.

Comment Request!
Thank you for reading this post. Please post your feedback, question, or comments about this post Here.
Login to add your contents and source code to this article
 [Top] Rate this article
 
 About the author
 
Dave Richter
I have been a student of the .NET Framework for a little over a year. These studies of the Common Language Runtime and managed code have brought me to about an intermediate level in coding C#. I enjoy learning how to code and have realized that learning the associated technologies is just as important. Any article that this web site decides to print is strictly an effort on my part what level I am at; my articles are not meant to be substitute for any other material that has obviously been written by experienced developers.
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Discover the Top 5 .NET Memory Management Fundamentals
To write the best .NET code, you need to know exactly how the .NET framework really manages memory. Ricky Leeks presents the Top 5 fundamental facts of .NET memory management. Learn more.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
ASP.NET 4 Hosting
Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites – Click Here!
 
 Post a Feedback, Comment, or Question about this article
Subject:
Comment:
6 Months Free & No Setup Fees ASP.NET Hosting!
Become a Sponsor
 Comments
very good by abas On August 16, 2009
very good
Reply | Email | Modify 

 © 2012  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.