Click here to Skip to main content
Click here to Skip to main content

Events and event handling in C#

By , 16 Oct 2001
 

Introduction

An event is a mechanism via which a class can notify its clients when something happens. For example when you click a button, a button-click-event notification is sent to the window hosting the button. Events are declared using delegates. So if you don't know what a delegate is, you may go and read my article on delegates first. The first time you go through this article you might find it confusing, don't worry about that. Just try out the sample program and go through the sample source, line by line. Maybe, you can read the article once more after that. Once you get the hang of it, things will seem simple. By the way this article is intended for beginners and is not meant for advanced level programmers.

The Program

I include the full program below. It's not commented but later down this article, I have taken the program part by part and explained each part. I have also included the output you'll get on running the program.

using System;

public delegate void DivBySevenHandler(object o, DivBySevenEventArgs e);

public class DivBySevenEventArgs : EventArgs
{
    public readonly int TheNumber;
    
    public DivBySevenEventArgs(int num)
    {
        TheNumber = num;
    }    
    
}

public class DivBySevenListener
{
    public void ShowOnScreen(object o, DivBySevenEventArgs e)
    {
        Console.WriteLine(
            "divisible by seven event raised!!! the guilty party is {0}",
            e.TheNumber);
    }    
}

public class BusterBoy
{
    public static event DivBySevenHandler EventSeven;
    
    public static void Main()
    {
        DivBySevenListener dbsl = new DivBySevenListener();
        EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
        GenNumbers();
    }
    
    public static void OnEventSeven(DivBySevenEventArgs e)
    {
        if(EventSeven!=null)
            EventSeven(new object(),e);
    }    
    
    public static void GenNumbers()
    {
        for(int i=0;i<99;i++)
        {
            if(i%7==0)
            {
                DivBySevenEventArgs e1 = new DivBySevenEventArgs(i);
                OnEventSeven(e1);
            }
        }        
    }
        
}
//Output

F:\c#\events>1
divisible by seven event raised!!! the guilty party is 0
divisible by seven event raised!!! the guilty party is 7
divisible by seven event raised!!! the guilty party is 14
divisible by seven event raised!!! the guilty party is 21
divisible by seven event raised!!! the guilty party is 28
divisible by seven event raised!!! the guilty party is 35
divisible by seven event raised!!! the guilty party is 42
divisible by seven event raised!!! the guilty party is 49
divisible by seven event raised!!! the guilty party is 56
divisible by seven event raised!!! the guilty party is 63
divisible by seven event raised!!! the guilty party is 70
divisible by seven event raised!!! the guilty party is 77
divisible by seven event raised!!! the guilty party is 84
divisible by seven event raised!!! the guilty party is 91
divisible by seven event raised!!! the guilty party is 98

F:\c#\events>

Explanation

Okay. I presume you have taken a look at the above program and I bet you have all guessed it's purpose. We generate some numbers and every time we generate a number that is divisible by 7 we raise an event. The event handler will print out a message saying that the event was raised and it also prints out the number responsible for raising the event. I guess some of you are frowning and saying that's a stupid reason to raise an event. I know, I know alright. The program is not intended to be used for any useful purpose. It's only an attempt to make events comprehensible. I hope it served it's role out. :-)

Okay, so the first thing we did was to declare a delegate.

public delegate void DivBySevenHandler(object o, DivBySevenEventArgs e);

The delegate defines the parameters sent to the event handlers. Thus any class that wants to handle this event must have a handler method which has the same return type and argument list as this delegate. Here as you can see, the first parameter is an object. In real-world cases event handlers are normally passed a reference to the sending object. I am not doing that in this program. I am simply passing a new object() to the event handler. Normally you can pass a this reference. The second parameter is a System.EventArgs derived class. System.EventArgs is the base class for encapsulating event related data. We use it to send information regarding the event to its handler.

Now, we define the EventArgs derived class as follows:-

public class DivBySevenEventArgs : EventArgs
{
    public readonly int TheNumber;
	
    public DivBySevenEventArgs(int num)
    {
        TheNumber = num;
    }		
}

As you can see, it has a public read-only member which is used to store our generated number that is divisible by 7. Normally you should use properties but for the sake of simplicity I am using a public member variable.

Now we define our listener class which is the class that needs to be notified of the event.

public class DivBySevenListener
{
    public void ShowOnScreen(object o, DivBySevenEventArgs e)
    {
        Console.WriteLine(
            "divisible by seven event raised!!! the guilty party is {0}",
            e.TheNumber);
    }	
}

As you can see, it has a function ShowOnScreen that matches the delegate type we defined on top. You can see how we use the passed DivBySevenEventArgs object to print out the number that is divisible by seven.

Now, let's examine our Main() containing class. We first declare the event as follows:-

public static event DivBySevenHandler EventSeven;

An event is declared like a delegate type variable, except that the keyword event precedes the event declaration.

Now let's take a look at the function that invokes the event and thus notifies all clients.

public static void OnEventSeven(DivBySevenEventArgs e)
{
    if(EventSeven!=null)
        EventSeven(new object(),e);
}

EventSeven will be null if no client has hooked up a delegate to the event. We need to check if it is null unless you want to see an exception raised. If it's not null we invoke the event, passing a dummy object [I have explained above why I passed a dummy] and the passed DivBySevenEventArgs object. And all clients get notified.

Let's look at the function GenNumbers() now :-

public static void GenNumbers()
{
    for (int i=0;i<99;i++)
    {
        if(i%7==0)
        {
            DivBySevenEventArgs e1 = new DivBySevenEventArgs(i);
            OnEventSeven(e1);
        }
    }		
}

We use the for() loop to iterate from 0 to 98, and in each case we check for divisibility by 7. If we find that the number is divisible by 7 we create a DivBySevenEventArgs object passing the culprit number to the constructor. Now we call the OnEventSeven() function passing the DivBySevenEventArgs object we just created. 

Now lets go through Main()

public static void Main()
{
    DivBySevenListener dbsl = new DivBySevenListener();
    EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
    GenNumbers();
}

We first create a DivBySevenListener object. Then using the += operator we compose a delegate into the event field. Even though I haven't used the -= operator, you can use it to remove a delegate from an event. Once we've done all that we call GenNumbers(). Thus we've set everything up nice and proper. GenNumbers() will do its job like a faithful puppy by generating numbers from 0 to 98 just as we expected it to. Every time it generates a number divisible by 7 it will raise the event.

Conclusion

Well you have just seen how you can create events and event handlers. You must remember that events can be invoked only from the class that declared them. This causes problems with regard to inheritance. Thus if you have a class with an event you'd better make your OnWhateverEvent() function protected so that the derived classed can call it. Better still, make it virtual too, so they can override it in their class.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Nish Sivakumar

United States United States
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionThank youmemberMember 93582058-Mar-13 10:58 
Nish,
I got it running in Silverlight with Blend 4. Only had to add a static TextBlock in the class declaration space above MainPage to replace the Console output.
 
Thanks, I'm indebted.
GeneralMy vote of 5membernaymyohaen29-Nov-12 21:37 
thank a lot
QuestionWhy not use = instead of +=memberMember 14822626-May-12 13:56 
Why is it necessary to use += for your example? Your program only uses one event function. I thought the purpose of using += was to add several delegates that needed to be called consecutively.
 
Stephen
AnswerRe: Why not use = instead of +=memberVuNic11-Jun-12 21:10 
That is to show people they can add more subscriptions to the event.
Events are subscribed (+=) , or say event handlers are "added"
Not assigned (=).

Starting to think people post kid pics in their profiles because that was the last time they were cute - Jeremy.

AnswerRe: Why not use = instead of +=memberShameel13-Aug-12 23:50 
To add to VuNic's answer, using assignment (=) will remove existing methods in the delegate's invocation list. This is precisely the reason = is not allowed. You just add your method to the event without worrying about who is already hooked in to the event.
Questionstatic functionsmemberbjdodo5-Mar-12 0:51 
Hi
 
I just wonder why you have so many static functions etc. in your example? If someone is not careful and copies the event declaration from your article (which is also static), he might be suprised when events fire when she/he does not expect.
 
BR,
Jozsef
GeneralMy vote of 4memberManuel_Perez_II7-Aug-11 21:52 
nice article
GeneralMy vote of 4memberbindum316-Jul-10 2:23 
Thanks Nish.Your article helps me a lot to learn event handling in C#.
QuestionCan you talk more clearly about paramenter object and eventargs and more example for themmemberbinhcoolfreestyles14-Mar-09 18:24 
Can you talk more clearly about paramenter object and eventargs and more example for them
AnswerRe: Can you talk more clearly about paramenter object and eventargs and more example for themmemberMark McClure26-Apr-09 9:25 
I agree, this would helpful.
Generalhandle printer eventsmemberchienquang13-May-07 22:48 
i've already read your article carefully, it's really helpful to me but my problem is not solved. The fact is that i am using System.printing library in Net 3.0, now I want to handle OnJobAdd event which adds a new job to a PrintQueue.
Can you show your solution?
thanks in advanced!
 
Time wait for none but wait for...

GeneralDisable event temporarilymemberRomanskiSt21-Apr-06 1:46 
I would like to disable the ItemCheck event for a ListView item while assigning the Checked property manually:
 
listview.ItemCheck -= new ItemCheckEventHandler(listview_ItemCheck);
 
listview.Items[0].Checked = true;
 
listview.ItemCheck += new ItemCheckEventHandler(listview_ItemCheck);
 
Unfortunately the event still fires. However it doesn't fire if either the second or the third line is commented out.
 
I think this is what happens: when Checked = true is executed, C# schedules the ItemCheck event to be fired. It doesn't remember which handlers are currently assigned to it. My handler is added to the event immediately afterwards. When the event finally gets processed, all handlers are executed, including listview_ItemCheck.
 
So the question is this: is there any way at all to ensure listview_ItemCheck is called for _all_ ItemCheck events _except_ the one caused by this assignment? Because if not then this is a very serious limitation...
GeneralRe: Disable event temporarilymemberyfisaqt10-Nov-06 12:14 
I created this function to disable all events in a control collection:
 

private void DisableEvents(Control.ControlCollection cc)
{
    foreach (Control c in cc)
    {
        DisableEvents(c.Controls);
        FieldInfo pi = typeof(Component).GetField("events", BindingFlags.NonPublic | BindingFlags.Instance);
        if (pi != null)
        {
            pi.SetValue(c, null);
        }
    }
}

 
I hope it helps.
GeneralRe: Disable event temporarilymemberPaul French17-Apr-07 15:49 
RomanskiSt wrote:
I would like to disable the ItemCheck event for a ListView item while assigning the Checked property manually:
 
listview.ItemCheck -= new ItemCheckEventHandler(listview_ItemCheck);
 
listview.Items[0].Checked = true;
 
listview.ItemCheck += new ItemCheckEventHandler(listview_ItemCheck);
 

 
Try the following code instead:
 
listview.ItemCheck -= listview_ItemCheck;
 
listview.Items[0].Checked = true;
 
listview.ItemCheck += new ItemCheckEventHandler(listview_ItemCheck);

 
This works for me...
GeneralRe: Disable event temporarilymemberMember 47453551-Nov-10 17:02 
hi romanski
 
i got exactly the same problem. however i solve it with a little bit tricky.
inside the event handler, i put a variable that control the execution of the handler code.
then i just to reset the variable before i manually make some changes, anf put it set again after all manual jobs is done. it works fine for me.
 
private void listview1_ItemChecked(object sender, ItemCheckedEventArgs e)
if (!listviewBeingEdited)
{
//do handler here
}
 
in other place:
listviewBeingEdited=true;
//do some list modification manually here
listviewBeingEdited=false;
QuestionWould the event be create in a different process?sitebuilderAlbert Pascual6-Apr-06 15:49 
Otherwise is like calling a function, you cannot keep going until the event finish
 
My eMail control
My Blog
AnswerRe: Would the event be create in a different process?memberJames S. Taylor18-Sep-07 15:00 
I think you mean thread, not process.
GeneralEvents in C# to be handled in VBmemberindivp5-Jun-05 7:21 
With great interest i read your articles on delegates n events.
Also i read many other articles on these subjects, but none of them cover a problem i cant seem to conquer.
 
My sollution holds 2 projects:
1 business logic - in C#
2 UI in VB.
 
1. declared in C# classCS in projectCS:
public delegate void myDelegate(string myStr);
Public event MyDelegate MyEvent;
...
...
Private myC#Funtion(string MyFStr) {
MyEvent += MyDelegate(...'what function to name here to be exec when event is fired'...);
if(MyEvent!=Null) {
this.MyEvent(MyFStr)
}
}
 

2. declared in VB formF in projectVB
 
Private sub doMyEvent(ByVal pStr As String) Handles objClassCS.MyEvent
MsgBox(pStr)
End Sub
 
-------------------------------------------------------------
The Question here is: How can I connect a public event in C#
using a delegate, to a VB eventhandler.
 
It all works fine within 1 VB project or within 1 C# project,
But not between 2 projects (VB project has a reference to the C# project).
 
Using these projects all together is what sollutions are all about right?
Or maybe this is not possible at all???
 
Thanks already
Many greetz from JollyRoger (www.jollyroger.nl).

QuestionDelegates sufficient for event-handling?memberAndrew Phillips24-May-04 21:09 
OK, I understand delegates but there is a basic thing about events that no books, articles etc ever explain. What is the purpose of the "event" keyword. I have tried example code that demonstrates events and they all continue to work if you remove the "event" keyword from the declaration of the delegate.
 
It seems to me that delegates are sufficient for implementing event-handling code. I can't see the purpose of the "event" keyword. What am I missing?

 
Andrew Phillips

AnswerRe: Delegates sufficient for event-handling?staffNishant S24-May-04 21:37 
Hi Andrew
 
There is a subtle difference between using the event keyword and not using the event keyword.
 
When you access the Event member from outside the class, the "event" keyword prevents you from both invoking the event as well as from setting the event to null. But if you remove the event keyword, you can set the delegate to null or even invoke the delegate directly.
 
So essentially the event keyword protects the event from being invoked or set to null from outside of the class.
 
Hope you understood what I said.
 
Regards
Nish
 
p.s. Good question by the way. After 2+ years I am glad someone read my article well enough to want to ask such a question Smile | :)
 

Now with my own blog - void Nish(char* szBlog);
 
My MVP tips, tricks and essays web site - www.voidnish.com
GeneralRe: Delegates sufficient for event-handling?memberevildictaitor10-Sep-06 12:58 
Events also have two other major advantages. The first is readability. An event is an obvious verb or asyncronous action when shown in most code editors, whereas a delegate will appear the same as any other variable, and secondly it is by default always ran asyncronous to the main thread. This means that you can have applications which are constantly doing something but do not appear to hang.
 
A good example would be a instant messaging client. The client may have as properties a username and password, but will have the event "MessageRecieved" differently marked in the code editor, and when a message is recieved it is recieved along a different thread which means that your application will not take up a large amount of CPU or refuse to draw (aka. "hang") as would be the case in the case that a delegate were used on the same thread.
QuestionUsing a this reference doesn't work?memberAaron R>9-May-03 3:47 
Great article! I also enjoyed the one on setting up delegates indepth.
 
I wanted to modify your example to call a method in the BusterBoy class. When I tried to do -
 
EventSeven += new DivBySevenHandler(this.myShowOnScreen);
 
and then compile, I get the following error -
 
Keyword this is not valid in a static property, static method, or static field initializer
 
I don't understand whats going on. If I had a form with a button on it, I could do a -
 
button1.Click += new System.EventHandler(this.button1_Click);
 
Both EventSeven and the button are classes within another class. The Click for button is a property, if I understand right. It would have a get and set I'm assuming.
 
Can someone please explain how to get the same behavior?
 
Regards,
Aaron R>
AnswerRe: Using a this reference doesn't work?editorNishant S9-May-03 16:27 
Aaron R> wrote:
EventSeven += new DivBySevenHandler(this.myShowOnScreen);
 
and then compile, I get the following error -
 
Keyword this is not valid in a static property, static method, or static field initializer

 
Can you show how you have declared the myShowOnScreen method? Itlooks as if you have declared it as static.
 

Aaron R> wrote:
button1.Click += new System.EventHandler(this.button1_Click);
 
Both EventSeven and the button are classes within another class. The Click for button is a property, if I understand right. It would have a get and set I'm assuming.

 
Nope. Click is NOT a property, it's an event to which you assign a handler.
 
Regards,
Nish
 

"I'm a bit bored at the moment so I'm thinking about writing a new programming language" - Colin Davies
 
My book :- Summer Love and Some more Cricket [New Win]
Review by Shog9 Click here for review[NW]

GeneralRe: Using a this reference doesn't work?memberAaron R>12-May-03 6:17 
Thanks so much for the insight! You were right on the money. I was getting myself confused by looking at console apps and standard form apps.
 
In a form app all the code thats in console main gets moved to the constructor, for your example anyway. I had a similiar issue when looking at other examples from people. I hadn't noticed the static in front of main. Once I realized what the problem was, everything got much easier.
 
I've learned enough to be dangerous and want to now fire an event with call backs on different machines.
 
Here is what I have -
 
objectA - a remoting object, with two strings and an event
 
server - starts a service providing objectA as a singleton and tells objectA to fire its event
 
client1 - uses objectA and has an eventhandler objectA is supposed to call
client2 - also uses objectA and has its own eventhandler for objectA
 
I can access the two strings from the clients without a problem. The client and server are always in sync when I update the string.
 
When I try and add the eventhandler:
 
objectA.myEvent += new myHandler(this.updateText); (this is either a line in client1 or client2)
 
I get a System.IO.FileNotFoundException when I start the client and it hits the above line.
 
Is there a way to do this properly? Do I need a workaround of some sort?
 
Any help is greatly appreciated!
 
Regards,
Aaron R>

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 17 Oct 2001
Article Copyright 2001 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid