Introduction
I am really writing this for myself because on the rare occasion that I have to rely on events for something, I am constantly forgetting how to write my own events. I don’t write my own events that often because the type of work that I do just doesn’t call for it most of the time. Events are great, but just like any other tool, I use them when I need them. My most recent scenario was a very complicated 3rd party ascx control was doing all kinds of crazy stuff after an aspx page has loaded. I needed the end result produced by the control to drive something else on the page. This was the effort to produce an optimization because the same end result was being generated twice which is just plain wasteful and goes against DRY which is ALWAYS a NO-NO! Therefore created an event to be fired off after the 3rd party control was finished doing all of the crazy stuff it was doing. I used the provided result to drive the aforementioned other stuff on the page. It worked great.
High-level view
- Your parent object needs a reference to its child object that will be raising the event. Without this, you cannot magically catch the event – aside from bubble events, but let’s not get into that. Bubble events are something you deal with specifically in ASP.NET with pages being the parent and user controls being the child. However, I want to focus on creating custom events.
- After your parent object has a reference to its child object, you need to explicitly state that you are listening for an event to be raised from the child object by registering a method handle, located in the parent object, with an event handler delegate provided by the child object reference.
- The previous step leads into this step – you need to define your method.
Very simple relatable example
Writing your custom event
- Delegate
- Event
- Boilerplate raise method (optional, but nice to have)
- Optional event arguments class if required, to be declared outside of the child object for the sake of reusability
1. Delegate
public delegate void CryHandler(object sender, CryEventArgs e);
2. Event
public event CryHandler Cry;
3. Raising your event
private void RaiseCryEvent(CryEventArgs e) { if(Cry != null) Cry(this, e); }
4. Event arguments class
public class CryEventArgs : EventArgs { //Wouldn't it be nice to know why the baby is crying? public string Reason { get; set; } }
- If the answer is no, then you can just use the EventArgs class because you won’t care about what is being sent back. This is okay sometimes if all you are looking for is the callback. Think of a click event on a page, 99.99% of the time, you just care that someone pressed the button, you are not looking at the sender object or the EventArgs e.
- If the answer is yes, then you should really create a new Event Arguments class because it just makes life easier. Make things explicit to avoid depending on casting, boxing and unboxing which is what you would need to do if you just use the stock EventArgs class. Don’t do that, it is very bad practice, lazy and pointless.
Using your Custom Event
public class Mother { //Child object reference Baby _billy; public Mother() { //Child object _billy = new Baby(); //Registering for a call back or listening for the baby to cry _billy.Cry += Billy_CryHandler; //Put the baby to sleep, the baby will wake up eventually _billy.Sleep(); } //When the baby is awoken, you need to ask the baby //why it woke up and put it back to sleep private void Billy_CryHandler(object sender, CryEventArgs e) { //The reason was passed over using our custom event argument object Console.WriteLine("Why are you awake Billy? - " + e.Reason + "n"); Console.WriteLine("Billy, go back to sleep!n"); //Put the baby back to bed _billy.Sleep(); } } public class Baby { //The model method that will be called when the event is raised "Call me back at" public delegate void CryHandler(object sender, CryEventArgs e); //The way that you can register for a call back public event CryHandler Cry; Random _r; public Baby() { _r = new Random(); } public void Sleep() { //Get a value between 0 and 10 - which is indeterminate enough for this example int sleep = _r.Next(10); //Announce that the baby will be put back to bed Console.WriteLine("Baby will be sleeping for " + sleep + " seconds.n"); //Long running process System.Threading.Thread.Sleep(sleep * 1000); //Callback when the baby wakes up RaiseCryEvent(new CryEventArgs() { Reason = "I pissed myself again..." }); } private void RaiseCryEvent(CryEventArgs e) { //This is syntactic sugar - a one liner that replaces //the if statement in the prior example Cry?.Invoke(this, e); } }