Events and Delegates
Events and Delegates
.NET Framework 4 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 the event and responds to it is called the event receiver. In event communication, the event sender class does not know which object or method will receive (handle) the events it raises. What is needed is an intermediary (or pointer-like mechanism) between the source and the receiver. The .NET Framework defines a special type (Delegate) that provides the functionality of a function pointer. A delegate is a class that can hold a reference to a method. Unlike other classes, a delegate class has a signature, and it can hold references only to methods that match its signature. A delegate is thus equivalent to a type-safe function pointer or a callback. While delegates have other uses, the discussion here focuses 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 common language runtime provides the implementation. The following example shows an event delegate declaration. public delegate void AlarmEventHandler(object sender, AlarmEventArgs e); public event AlarmEventHandler AlarmEvent; protected void OnAlarmEvent(AlarmEventArgs e) { AlarmEvent(this, e); }
If you want your class to raise an event, you must provide the following three elements:
y y y
A class that provides event data. An event delegate. The class that raises the event.
using System; using System.Collections.Specialized; using System.Configuration; using System.IO; public class DiskSpaceWarningEventArgs : EventArgs { private long currentFreeSpace; private long currentTotalSpace; private string driveName;
public DiskSpaceWarningEventArgs(string name, long freeSpace, long totalSpace) { this.driveName = name; this.currentFreeSpace = freeSpace; this.currentTotalSpace = totalSpace; } public string Name { get { return this.driveName; } } public long FreeSpace { get { return this.currentFreeSpace; } } public long TotalSpace { get { return this.currentTotalSpace; } } } public delegate void DiskSpaceWarningEventHandler(object sender, DiskSpaceWarningEventArgs e); public class DiskSpaceMonitor { public event DiskSpaceWarningEventHandler DiskSpaceWarning; private decimal threshhold; public DiskSpaceMonitor() { // Retrieve threshhold to fire event from configuration file. try { NameValueCollection settings = ConfigurationManager.AppSettings; this.threshhold = Convert.ToDecimal(settings["Threshhold"]); } // If there is no configuration file, provide a default value. catch (ConfigurationErrorsException) { this.threshhold = 10m; } catch (InvalidCastException) { this.threshhold = 10m; }
} public void CheckFreeSpace() { // Get drives present on system. DriveInfo[] drives = DriveInfo.GetDrives(); foreach (DriveInfo drive in drives) { if (drive.IsReady) { if (drive.TotalFreeSpace / drive.TotalSize <= this.threshhold) OnDiskSpaceWarning(new DiskSpaceWarningEventArgs(drive.Name, drive.TotalFreeSpace, drive.TotalSize)); } } } protected void OnDiskSpaceWarning(DiskSpaceWarningEventArgs e) { if (DiskSpaceWarning != null) DiskSpaceWarning(this, e); } }
Events are the means by which Windows Application receive notification. In a Windows application a lot of Events are occurring at a particular instant for e.g. Mouse Move, Mouse out, Mouse Click etc. Delegates are pointer to the function and are type-safe. Another very interesting feature in C# is delegates. Delegates are best complemented as new type of Object in C#. They are also represented as pointer to functions. Technically delegate is a reference type used to encapsulate a method with a specific signature and return type. Since in this article delegate discussion is event centric. If we consider a real world scenario then delegates can be understood as any delegate representing a country a group of people representing a company etc. This same definition can be mapped to C# as delegate act as an intermediary between event source and destination. The DotNetFrameWork has a Name Space System.Delagate. We have two flavors of delegate in C#. Single Delegate Multi-cast Delegate A delegate is called a single delegate that derives from the System.Delegate class contains an invocation list with one method. Now we will look at how the single-cast delegates are declared. In single-cast delegates, the delegate can point to both static and non-static method if both have the same signature as the delegate. Look at the code below how to declare a single-cast delegate. public delegate void Myfunction(string,System.Int32) The above code shows a simple delegate which will point to a method with no return type and taking two arguments as parameters. Now we see delegate that return a value. public delegate bool MyFunctionOne();
The above example doesnt show the true usage of delegates but I think we are getting the idea what we want to understand. Above is a simple program which compares two strings. If the strings are equal then only one name is returned otherwise both the names are returned if the condition executes to false. Now first the main method is invoked which is situated in DelegateExample Class. In this class we have also declared our delegate. public delegate string CompareNames(string NameOne,string NameTwo); It accepts two arguments of type string and also returns a string. In the main method we create the instance of Name class and then we create the instance of our delegate passing the name of our method in it as its argument so that it points to compare method now. Then we just call our delegates by passing the arguments. Which in return call the Compare method and return the string.
A delegate is called Multi-cast Delegate that derives from the System.MulticastDelegate contains an invocation list with multiple methods. At times it is desirable to call two methods through a single delegate. This can be achieved through Single-cast Delegate but it is different from having a collection, each of which invokes a single method. In Multi-casting you create a single delegate that will invoke multiple encapsulated methods. The return type of all the delegates should be same. Now the question why are we using Multi-cast delegates when Single-cast delegates are enough. Well the answer to this question is what if you want to call three methods when a button is clicked. The Multi-cast delegates are used with events where multiple call to different methods are required. System.MulticastDelegate contains two methods Combine and Remove. The Combine is a static method of class System.MulticastDelegate and is used to Combine the delegates
and the remove method is used to remove the delegate from the list. For user convenience we have += operator overloaded for delegate Combine method and -= operator overloaded for Remove method Multicast delegates are similar to Single-cast delegates for e.g. public delegate void MulticastDelegate(); Multi-cast delegate can have arguments and can return value as well. All the Methods pointed by delegates should return the similar value as the delegate return type. public delegate string MultiCastOne(string Name); Consider a simple example: using System; namespace Multi_castDelegate { class MyClassDelegate { /// <summary> /// The main entry point for the application. /// </summary>
public delegate string StringDelegate(string s); } /// <summary> /// Summary description for MyImplementingClass. /// </summary> public class MyClass { public MyClass() { } public static string WriteString(string s) { Console.WriteLine("Writing string"); return "null"; } public static string logString(string s) { Console.WriteLine("loging string"); return "null"; } public static string TransmitString(string s) { Console.WriteLine("Transmitting string"); return "null"; }
{ /// <summary> /// Summary description for Test. /// </summary> public class Test { static void Main(string[] args) { MyClassDelegate.StringDelegate Writer, Logger, Transmitter;
MyClassDelegate.StringDelegate myDelegate; Writer = new MyClassDelegate.StringDelegate(MyClass.WriteString); /// calling Writer Writer("hello i am Writer just acting like Single cast"); Logger = new MyClassDelegate.StringDelegate(MyClass.logString); ///calling Logger Logger("hello i am Logger just acting like Single-cast"); Transmitter = new MyClassDelegate.StringDelegate(MyClass.TransmitString); ///calling Transmitter Transmitter("hello i am Transmitter just acting like Single-cast"); ///here mydelegate used the Combine method of System.MulticastDelegate ///and the delegates combine myDelegate = (MyClassDelegate.StringDelegate)System.Delegate.Combine(Writer, Logger); myDelegate("used Combine"); ///here Transmitter is also added using the overloaded form of Combine myDelegate += Transmitter; myDelegate("Using Overloaded Form"); ///now using the Remove method myDelegate = (MyClassDelegate.StringDelegate)System.Delegate.Remove(myDelegate, Writer); myDelegate("Without Writer"); ///overloaded Remove myDelegate -= Transmitter; myDelegate("Without Transmitter"); System.Threading.Thread.Sleep(2300);
} } } } The above program contains three classes, MyClassDelegate contains the delegate.
public delegate string StringDelegate(string s); The class MyClass Contains the static methods that contains the static methods that have a similar signature as the delegate StringDelegate. The third class is the Test Class which shows how to combine the delegates and how to remove the delegate from the list. Events are the messages sent by an object to indicate the occurrence of an event. Event can also be defined as a member that enables an object to provide notification. Events provide a very powerful means of inter-process communication. The most familiar example of events are graphical user interface, events are fired when any control is clicked on the GUI. Events are not used only for graphical user interfaces. Events provide a generally useful way for objects
to signal state changes that may be useful to the client of that object. In C# events are used with delegates. If you dont have through understanding of delegates please refer the above portion. In event communication the event raiser class doesnt know who is going to handle the event now the delegates comes into play that acts as an intermediary between the source and the receiver. // Delegate public delegate void newdelegate(); // Event Declaration public event newdelegate newevent; We can categories events into two types Customized Events Predefined Events Dont confuse these terms because I have categorized events so that the explanation become simple. Now what do we mean by customize events, they are events which we define according to our needs and are not defined. For e.g we want to raise an event whenever a dynamic control is created on the form. To declare an event, first define the delegate for that event, if none is already declared. The delegate type defines the set of argument that are to be passed to the method which will act as event handler. Example of Customized Events: namespace Eventhandling { public delegate void IamClicked(); /// <summary> /// Summary description for Form1. /// </summary> public class Form1 : System.Windows.Forms.Form { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public System.Int32 o_IntCounter=0; public event IamClicked ClickMe; System.Int32 o_intXaxis=120; System.Int32 o_intYaxis=80;
public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after
InitializeComponent call // Button b1=new Button(); b1.Parent=this; b1.Location=new Point(o_intXaxis,o_intYaxis); b1.Name="Click1"; b1.Text="Click Me"; ClickMe+=new IamClicked(Show); ClickMe(); } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(304, 237); this.Name = "Form1"; this.Text = "Events"; } #endregion /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main()
{ Application.Run(new Form1()); } /// <summary> /// Event Handler Function Which is called when the /// Click Event is Raised. /// </summary> /// <param name="o"></param> /// <param name="ea"></param> public void Show() { MessageBox.Show("JUST BORN"); } } } The above program shows hoe we can fire our own events. In this program a button is created dynamically. Button b1=new Button(); b1.Parent=this; b1.Location=new Point(o_intXaxis,o_intYaxis); b1.Name="Click1"; b1.Text="Click Me"; ClickMe+=new IamClicked(Show); ClickMe(); The delegate and event defined in above program are public delegate void IamClicked(); public event IamClicked ClickMe; The delegate points to the following function. public void Show() { MessageBox.Show("JUST BORN"); } When the ClickME event is fired the delegate attached to this event call the above function Show. Look at the signature of the function and the delegate both are same. The function just shows a message box with the message Just Born. Events and delegates go hand in hand. Now we are considering Predefined events like Click Closed Closing DragDrop Enter Load Leave etc We normally use predefined events in our programming practice. Multiple events can share the same delegate type. The event is declared as the delegate type. In C# we must follow precise signature for
Handler. void OnClick(object o,EventArgs ea) { //Code } Example of Events: using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace Eventhandling { /// <summary> /// Summary description for Form1. /// </summary> public class Form1 : System.Windows.Forms.Form { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public System.Int32 o_IntCounter=0; private System.Windows.Forms.Button btnNewControl; public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) {
if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.btnNewControl = new System.Windows.Forms.Button(); this.SuspendLayout(); // // btnNewControl // this.btnNewControl.Name = "btnNewControl"; this.btnNewControl.Size = new System.Drawing.Size(112, 32); this.btnNewControl.TabIndex = 0; this.btnNewControl.Text = "New Control"; this.btnNewControl.Click += new System.EventHandler(this.btnNewControl_Click); // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(304, 237); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.btnNewControl}); this.Name = "Form1"; this.Text = "Events"; this.ResumeLayout(false); } #endregion /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.Run(new Form1()); } /// <summary> /// Event Handler Function Which is called when the
/// Click Event is Raised. /// </summary> /// <param name="o"></param> /// <param name="ea"></param> public void btnAdd(object o,EventArgs ea) { o_IntCounter++; MessageBox.Show(o_IntCounter.ToString()); } private void btnNewControl_Click(object sender, System.EventArgs e) { System.Int32 b_intCount; System.Int32 b_intXaxis=120; System.Int32 b_intYaxis=80; for(b_intCount=1;b_intCount<=3;b_intCount++,b_intYaxis+=20) { ///new buttons are created at run time ///with there location and names. Button b1=new Button(); b1.Parent=this; b1.Location=new Point(b_intXaxis,b_intYaxis); b1.Name="Click1"+b_intCount; b1.Text="Click Me"; b1.Click+=new EventHandler(btnAdd); } } } } The above program creates three buttons at run time and when any of the buttons is clicked the Click event of that button is fired. for(b_intCount=1;b_intCount<=3;b_intCount++,b_intYaxis+=20) { ///new buttons are created at run time ///with there location and names. Button b1=new Button(); b1.Parent=this; b1.Location=new Point(b_intXaxis,b_intYaxis); b1.Name="Click1"+b_intCount; b1.Text="Click Me"; b1.Click+=new EventHandler(btnAdd); } The Click event belongs to the button class. We will reference it when we are registering a delegate. b1.Click+=new EventHandler(btnAdd);
The delegate EventHandler is also defined in the System namespace of Dot net Framework library. All what we have to do is to define our callback function that is invoked when the button is clicked and accepts two arguments object and EventArgs type conforms the signature of delegate EventHandler defined in System namespace public void btnAdd(object o,EventArgs ea) { o_IntCounter++; MessageBox.Show(o_IntCounter.ToString()); } The handler btnAdd() Method catches this event and shows a message box that indicates a number that how many times these buttons are clicked. The above example is quite self explanatory so just copy the code in a new project and run for yourself.