Wednesday, January 17, 2024

Foundations: Events And C#

 

I was going over some notes on events, event handlers, etc. and I thought I'd post up some general foundational information that I have written in the past, as it's still relevant today (as are most foundational topics). As is always the case with some older foundational code, you may need to update for current versions, etc.

What really are Events, Event Handlers, Raising Events, etc. and how can you create your own class that has its own Events which can be consumed? This series of articles takes a look at what Events are, how to implement them within your own classes, and how they are useful.

Don’t I already create Events when I say what happens when something like a "submit" button is pressed?

No. You’re telling what should occur when an Event takes place, and this is very helpful in learning how to create a class with its own Events, but you’re already working with a class that has its own Events, Event Handlers (or what should take place when an event happens), and methods to raise the Events.

So can you tell me what is an Event, an Event Handler, and show me Event Messaging in simple terms?

Yes! And that’s what this article is all about. Rather than talk about telephones, callers, and things like that it’s much easier to take a look at an example that you’re already familiar with to see what Events are and what Event messaging is all about.

First though, let’s just take a look at an easy definition of an Event, which simply is: a way for a client, program, object, etc., to be notified when another object does something, or changes, or something happens to that object.

An easy example is the Page class and how you work with it in ASP.NET. When a page Loads that’s the Event (specifically the Event is called a Load). Remember, an Event is something that simply happens, like in normal everyday life.

So when the Event happens, in our example, the loading of a page, something should happen. In order to say what happens when an Event occurs, you set what’s called an Event Handler. An Event Handler does exactly what its name suggests: it allows you to point to a method, or methods, that handle the Event, in our case the loading of a page.

This shouldn’t be a new concept to you as you do it all the time. Take the code below:

this.Load += new System.EventHandler(this.Page_Load);

The code above simply says “when the event Load occurs, handle it by running the method Page_Load”. An Event Handler simply points to a method with a specific signature.

So how do you know when an Event happens, or more specifically, say that the event took place? The Event must be raised, and is done so with a method that is defined as OnEventName. This method can be called from any type of client, and in ASP.NET for the Page class, usually happens behind the scenes (as a series of events are raised as an ASP.NET page is run) and should be in the class where the event is declared.

To that extent, event messaging is the whole process of what happens when an event takes place, how it is handled, and how it is raised.

So to quickly recap, an Event is something that occurs, and needs to be raised (in order to let us know it’s happening). When the event is raised it should be handled by a method, which is set by an event handler.

So can you give me an example of how I create a class with an event and how to use it in an ASP.NET page?

Below is a simple class called InnerPanelClass which has one event, called Load (the code will be walked through in the rest of the article).

namespace MyEventClasses{

  public delegate void PanelEventHandler (object sender,     
  System.EventArgs e);
	
	
  //Now the class
  public class InnerPanelClass 
  {
     //The event is called “Load”
     public event PanelEventHandler Load;
     
     //The constructor for the class
     public InnerPanelClass() {}

     //The method that raises the event
     public void OnLoad()
     {	
       System.EventArgs e = new System.EventArgs();
       Load(this,e);
       return;
     }
   }
}

Some of the code should look familiar like the class definition, constructor, and namespace definition. So let’s take a look at actually declaring the Event in our class.

public event PanelEventHandler Load;

When you declare an Event the syntax is public event A Delegate Type The name of your Event. A delegate is simply a way of allowing anonymous methods to be invoked, but with a specific signature. In essence what we’re doing when we declare our Event is saying “The Event is called Load, to handle that event a client using our class needs to use an Event Handler, specifically the PanelEventHandler, which is actually a Delegate. So let’s take another look at that Delegate code (for more information on Delegates please see the article Using Delegates in C# ):

public delegate void PanelEventHandler (object sender,System.EventArgs e);

What's great about Delegates is that they allow you to specify any method as long as it has the signature defined by the Delegate. In our case, that means that a new PanelEventHandler can be any method which takes an object and System.EventArgs as its parameters.

The last piece of code in our class is the method OnLoad which will actually raise the event and is pretty easy to follow:

public void OnLoad()
{	
  System.EventArgs e = new System.EventArgs();
  Load(this,e);
  return;
}

When you create your method to raise the Load Event, you’re just calling the Event with the proper parameters of the Delegate, in our case an object (which should be itself using the keyword "this", and a System.EventArgs object) and returning it. When the Event is raised, it will then execute any new PanelEventHandlers set up by the client who is using the class.

Using the class

So now it’s just using the class, which is pretty easy to do. Below is the full code needed for this example:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace MyWeb
{

  public class EventSample : System.Web.UI.Page
  {
    //Create a new instance of our class.	
    public MyEventClasses.InnerPanelClass Ip = new MyEventClasses.InnerPanelClass();
		
    private void Page_Load(object sender, System.EventArgs e)
    {
     Response.Write("This is a page load");
    }
    
    //Set the method to be called when the event occurs.	
    public void Panel_Load(object sender, System.EventArgs e)
    {
     Response.Write("This is my InnerPanel load");
    }

    #region Web Form Designer generated code
    override protected void OnInit(EventArgs e)
    {
     //
     // CODEGEN: This call is required by the ASP.NET Web Form Designer.
     //
     InitializeComponent();
     base.OnInit(e);
     
     //Raise the Event when the page initializes
     //using the object’s method “OnLoad”.
     Ip.OnLoad();		
     }
		

    private void InitializeComponent()
    {    
     this.Load += new System.EventHandler(this.Page_Load);
     
     //Create an Event Handler for the Load Event and point it 
     //to the method "Panel_Load".
     Ip.Load += new MyEventClasses.PanelEventHandler(this.Panel_Load);
    }
    #endregion

  }
}

namespace MyEventClasses{

  public delegate void PanelEventHandler (object sender,     
  System.EventArgs e);
	
	
  //Now the class
  public class InnerPanelClass 
  {
     //The event is called “Load”
     public event PanelEventHandler Load;
     
     //The constructor for the class
     public InnerPanelClass() {}

     //The method that raises the event
     public void OnLoad()
     {	
       System.EventArgs e = new System.EventArgs();
       Load(this,e);
       return;
     }
   }
}

As you can see, once you create your class with the Events and necessary methods and delegates, using it from a client (our ASP.NET page) is easy to do.

While this example is simple, it creates the foundation and understanding for working with more advanced type of scenarios, like creating your own class of Event Arguments, passing in Event Data, and creating your own classes which define more user controlled Events (like a click event for a button object) and will go a long way in understanding the role of Events which help to modularize programs.

-----

But what about sending data along with the Event? In order for this to happen, you can setup your own EventArgs class which is really easy to do. Just create a new class which inherits from System.EventArgs. After that you can then create properties just like any other class. Building on the example from the previous article on Events, we'll create our own class called InnerPanelEventArgs which has one property called MyData that can be used to send data along with the event.

public class InnerPanelEventArgs: System.EventArgs
{
  private string _MyData;
  public InnerPanelEventArgs(){}
		
  public string MyData
  {
   get {return _MyData;}
   set {_MyData = value;}
  }
}

Now instead of using System.EventArgs for the Delegate, we'll use our new class instead and also make sure that when the Event is raised, it also needs to pass in an object reference, so the new code would be:

namespace MyEventClasses{
	
	public delegate void PanelEventHandler (object sender, InnerPanelEventArgs e);
	
	public class InnerPanelEventArgs: System.EventArgs
	{
		private string _MyData;
		public InnerPanelEventArgs(){}
		
		public string MyData
		{
			get {return _MyData;}
			set {_MyData = value;}
		}
	}

	public class InnerPanelClass 
	{
		
		public event PanelEventHandler Load;
	
		public InnerPanelClass() 
		{
		}

		public void OnLoad(MyEventClasses.InnerPanelEventArgs e)
		{	
			Load(this,e);
			return;
		}
	}
}

Since the signature has changed for the EventHandler you'll also need to change the signature for the method (and then at the same time we'll print out the new property for the Event data):

public void Panel_Load(object sender, MyEventClasses.InnerPanelEventArgs e)
{
   Response.Write("This is my InnerPanel load"+e.MyData);
}

When the Event is raised, since it needs to pass in an object reference to InnerPanelEventArgs, this is where you can also set and pass in any Event data:

override protected void OnInit(EventArgs e)
{
  InitializeComponent();
  base.OnInit(e);
  MyEventClasses.InnerPanelEventArgs ipArgs = new MyEventClasses.InnerPanelEventArgs();
  ipArgs.MyData="This is some event data";
  Ip.OnLoad(ipArgs);		
}

Now when the page is run, you will not only see the message "This is my InnerPanel load", but also the string "This is some event data". From here, it should be pretty easy to see how you can than pass in information when an Event occurs from other data, like a control's properties or other client information which can be useful to know when an Event happens.