Understanding Events in C#


Events are one of the core and important concepts of the C# .Net Programming environment and frankly speaking, sometimes it's hard to understand them without a proper explanation and example.

So I thought of writing this article to make things easier for learners and beginners.

Our Topic

An event in very simple terms is an action or occurrence, such as clicks, key press, mouse movements, or system generated notifications. Applications can respond to events when they occur. Events are messages sent by the object to indicate the occurrence of the event. Events are an effective method of inter-process communication. They are useful for an object because they provide signal state changes that may be valuable to a client of the object.

If the preceding sentences were tough to understand then let's make it simpler. If a button on a form is clicked by a user then an event is fired. If a user types something into a TextBox then keys are pressed and hence an event is fired and so on.

The following figure is the generalized representation that explains events and event handling.

generalized representation

In C#, delegates are used with events to implement event handling. The .NET Framework event model uses delegates to bind notifications with methods known as event handlers. When an event is generated, the delegate calls the associated event handler.

Investigating .NET Windows Application Button Click Event

Just open your Visual Studio and create a Windows application. You'll get a form in your Windows application project named Form1.cs. Add a simple button to that form, just drag and drop. In the properties panel of that button, bind the onclick event of that button with an event in the code behind and show some text on its click. You can browse the attached source code for understanding.

Now when you run the application, your form will be shown with just one button, click that button and see what happens.

click that button

As we can see the button on the form was clicked by the user and hence the button click event was fired that was handled by the delegate that in turn called the button_click method (event handler method) that showed our Message Box.

In the preceding example of an event declaration, delegate declaration and event handler declaration, all is done by .NET. We just need to write code handle to our event. In this case the code to display the message box.

Behind the curtain

If we investigate Form1.Designer.cs and follow the step shown in the figure below we can easily determine the event keyword and delegate keyword and hence determine their definition.

Since we haven't seen the definition syntax it might look alien to you but we will get to it soon. For now just follow the procedure in the figure.

Step 1

Open Form1.Designer.Cs from Solution Explorer.


Step 2

In Form1.Designer.Cs Click event and EventHandler delegate.

EventHandler delegate

Step 3

Double-click this.button1. Click and press F12 to see the Click Event definition, similarly double-click System.EventHandler and Press F12 to see the EventHandler Delegate definition.

The following shows the event's definition:

event hendlar

The following shows the delegate's definition:


Publisher-Subscriber model

The events are declared and raised in a class and associated with the event handlers using delegates within the same class or other classes. Events are part of a class and the same class is used to publish its events. The other classes can, however, accept these events, or in other words can subscribe to these events. Events use the publisher and subscriber model.

A publisher is an object that contains the definition of the event and the delegate. The association of the event with the delegate is also specified in the publisher class. The object of the publisher class invokes the event that is notified to the other objects.

A subscriber is an object that wants to accept the event and provide a handler to the event. The delegate of the publisher class invokes the method of the subscriber class. This method in the subscriber class is the event handler. The publisher and subscriber model implementation can be defined by the same class.

The following figure shows the mechanism used by the publisher and subscriber objects.


Getting your hand dirty

Let's get our hand dirty by building our own event handling example. In the example below we will see how to define our customized event and how to raise it and how to handle it by our own customized event handler.

In our simple example we'll build a console application for a bank in which the event TransactionMade is raised whenever the customer makes a transaction and in response a notification is sent to him.

Let's do some serious coding now.

First we define our class Account.

We can add a constructor to initialize our variable int BalanceAmount that will hold the account balance in our class.

  1. public int BalanceAmount;  
  2. public Account(int amount)  
  3. {  
  4.    this.BalanceAmount = amount;  
  5. }  
Then we define our event and our delegate.

The definition of the event in a publisher class (Account class ) includes the declaration of the delegate as well as the declaration of the event based on the delegate. The following code defines a delegate named TransactionHandler and an event TransactionMade that invokes the TransactionHandler delegate when it is raised:
  1. public delegate void TransactionHandler(object sender,TransactionEventArgs e);  
  2. public event TransactionHandler TransactionMade;  
As you see, our delegate's name is TransactionHandler and its signature contains a void return value and two parameters of type object and TransactionEventArgs. If you somewhere want to instantiate this delegate, the function ed in as a constructor parameter should have the same signature as this delegate.

When an event is raised, we some data to the subscriber in a class that is derived from. For example, in our example we want to provide the Transaction Amount and the Type of Transaction made. So we define a class TransactionEventArgs that will inherit EventArgs to data to the subscriber class. We have declared two private variables, one int _transactionAmount to the transaction amount information and the other is string _transactionType to the transaction type (credit/debit) information to the subscriber class.

And here is the class definition:
  1. public class TransactionEventArgs : EventArgs  
  2. {  
  3.     private int _transactionAmount;  
  4.     private string _transactionType;  
  5.     public TransactionEventArgs(int amt,string type)  
  6.     {  
  7.         this._transactionAmount = amt;  
  8.         this._transactionType = type;  
  9.     }  
  10.     public int TranactionAmount  
  11.     {  
  12.         get  
  13.         {  
  14.             return _transactionAmount;  
  15.         }  
  16.     }  
  17.     public string TranactionType  
  18.     {  
  19.         get  
  20.         {  
  21.             return _transactionType;  
  22.         }  
  23.     }  
  24. }  
Now, everything is in our Account class. Now we will define our notifier methods that will be invoked on a credit or debit transaction and raise our event.

In the Debit method the balance amount will be deducted and the event will be raised to notify the subscriber that the balance amount has been changed, similarly in case of the Credit method the balance amount will be credited and notification will be sent to the subscriber class.

Debit Method
  1. public void Debit(int debitAmount)  
  2. {  
  3.     if (debitAmount < BalanceAmount)  
  4.     {  
  5.         BalanceAmount = BalanceAmount - debitAmount;  
  6.         TransactionEventArgs e = new TransactionEventArgs(debitAmount,"Debited");  
  7.         OnTransactionMade(e); // Debit transaction made  
  8.     }  
  9. }  
Credit Method
  1. public void Credit(int creditAmount)  
  2. {  
  4.     BalanceAmount = BalanceAmount + creditAmount;  
  5.     TransactionEventArgs e = new TransactionEventArgs(creditAmount,"Credited");  
  6.      OnTransactionMade(e); // Credit transaction made  
  8. }  
As you can see in the preceding methods, we have created an instance of TransactionEventArgs and we ed that instance in an OnTransactionMade() method and called it. You must be thinking, what is OnTransactionMade() doing here? Well this is our method that is raising our event. So here is its definition:
  1. protected virtual void OnTransactionMade(TransactionEventArgs e)  
  2. {  
  3.     if (TransactionMade != null)  
  4.     {  
  5.         TransactionMade(this, e); // Raise the event   
  6.     }  
  7. }  
The following is the complete code for our Account Class:
  1. namespace EventExample  
  2. {  
  3.  public delegate void TransactionHandler(object sender,TransactionEventArgs e); // Delegate Definition  
  4.     class Account  
  5.     {  
  6.         public event TransactionHandler TransactionMade; // Event Definition  
  8.         public int BalanceAmount;  
  10.        public Account(int amount)  
  11.         {  
  12.             this.BalanceAmount = amount;  
  14.         }  
  16.         public void Debit(int debitAmount)  
  17.         {  
  18.             if (debitAmount < BalanceAmount)  
  19.             {  
  20.                 BalanceAmount = BalanceAmount - debitAmount;  
  21.                 TransactionEventArgs e = new TransactionEventArgs(debitAmount,"Debited");  
  22.                 OnTransactionMade(e); // Debit transaction made  
  23.             }  
  24.         }  
  26.         public void Credit(int creditAmount)  
  27.         {  
  29.             BalanceAmount = BalanceAmount + creditAmount;  
  30.             TransactionEventArgs e = new TransactionEventArgs(creditAmount,"Credited");  
  31.              OnTransactionMade(e); // Credit transaction made  
  33.         }  
  35.         protected virtual void OnTransactionMade(TransactionEventArgs e)  
  36.         {  
  37.             if (TransactionMade != null)  
  38.             {  
  39.                 TransactionMade(this, e); // Raise the event   
  40.             }  
  41.         }  
  42.     }  
Raising an event is accomplished by calling our event TransactionMade(this, e);

And this event will be handled by our event handler.

Now let's define our Subscriber class that will react to an event and process it accordingly using its own methods.

First create a class named TestMyEvent and define a method SendNotification, its return type and parameter should match our Delegate declared earlier in the publisher class. Basically this method will react on an event that is changed in our balance amount and informs the user (by writing this on the console).

The following is the definition:
  1. private static void SendNotification(object sender, TransactionEventArgs e)  
  2. {  
  3.     Console.WriteLine("Your Account is {0} for Rs.{1} ", e.TranactionType, e.TranactionAmount);  
  5. }  
Now in the Main() method of the subscriber class create an instance and some initial balance amount. Then a reference of the event handler is ed to the event and method to be called on that event response is ed to the event handler.
  1. private static void Main()  
  2. {  
  3.     Account MyAccount = new Account(10000);  
  4.     MyAccount.TransactionMade += new TransactionHandler(SendNotification);  
  5.     MyAccount.Credit(500);  
  6.     Console.WriteLine("Your Current Balance is : " + MyAccount.BalanceAmount);  
  7.     Console.ReadLine();  
  10. }  
So, the following is the complete definition of our subscriber class (TestMyEvent):
  1. class TestMyEvent  
  2. {  
  4.     private static void SendNotification(object sender, TransactionEventArgs e)  
  5.     {  
  6.   Console.WriteLine("Your Account is {0} for Rs.{1} ", e.TranactionType, e.TranactionAmount);  
  8.     }  
  10.     private static void Main()  
  11.     {  
  12.         Account MyAccount = new Account(10000);  
  13.         MyAccount.TransactionMade += new TransactionHandler(SendNotification);  
  14.         MyAccount.Credit(500);  
  15.         Console.WriteLine("Your Current Balance is : " + MyAccount.BalanceAmount);  
  16.         Console.ReadLine();  
  19.     }  
  20. }  
Our output will be:

Our output


We investigated .NET events and built our own custom event and saw how events are raised and handled. So I wouldn't be wrong to say that events encapsulate delegates and delegates encapsulates methods. So the subscriber class doesn't need to know what's happening behind the curtain, it just requires notification from the publisher class that an event has been raised and it must respond accordingly.


I hope you are satisfied after reading the article and that most of have enjoyed reading and coding.


reading and coding