Skip navigation

Tag Archives: events

OK, this one is a bit more complex, so start here, then go there.

WHY C# events

An EVENT in C# is simply a means to create a CHAIN of functions that will execute when something “happens” to an object.

OBJECT with published event
subscriber 1 subscriber 2 subscriber 3 subscriber 4 subscriber 5

EVENTS in C# follow the observer design pattern. The object that CONTAINS the “event” is the “PUBLISHER”, while all the objects that “attach a function” to the event are “subscribers” to that event.

Understanding C# custom events

As a prerequisite to understanding this, you must have already worked with normal events in C#, and built a couple of simple windowed applications using .NET. If not, go read up on them, also try this.

In fact, let me summarize the prereqs to understanding this article here:

  • Worked with events in C# normally before (windows forms applications)
  • Understands delegates

If you don’t have those prereqs, RUN AWAY!! This will not make any sense to you, especially if you don’t understand delegates at all.

Custom events are great!

The point of defining custom events in C# is to provide an EASY MECHANISM to have a chain of functions that will execute in rapid succession on your command.

The idea is this. You have an object, say a BUTTON. WHEN THAT BUTTON IS CLICKED by the user, you want some function to run. But wait, not just ONE function. You want SEVERAL functions to run. In rapid succession, one after the other. AND YOU WANT THEM TO RUN WHEN THE BUTTON IS CLICKED!

So here’s what you do. You define an EVENT

using System;


public class Button
{
  // Define the EVENT here.
  public event Action Pushed ;

  // NOTICE how we specify the ACTION delegate
  // in this declaration.

  // The ACTION delegate use means that 
  // the "Pushed" event accepts a chain of
  // functions to be attached to it that all have
  // NO arguments, and return type VOID.

  // You can use ANY DELEGATE TYPE YOU WANT for
  // an event in C#.  You can define your own
  // delegate type if you want, but you will really
  // have to study and understand delegates if you
  // want to do that (not covered here!)



  // This is the function that triggers the event.
  public void PushButton()
  {
    // Print out a message before actually firing the
    // Pushed event.
    Console.WriteLine("Oh, you pushed me.");
    
    // !! VERY IMPORTANT LINE!!
    Pushed();  // "FIRE" EVENT:  CAUSES ALL FUNCTIONS THAT WERE ATTACHED
    // TO THE "Pushed" EVENT PREVIOUSLY TO EXECUTE IN
    // RAPID SUCCESSION, ONE AFTER THE OTHER!

    // The funny thing about Events is the WEIRD, WEIRD, WEIRD
    // syntax is uses.  At one moment, you're +='ing to it,
    // the next moment, you're calling it as if it were a function.

    // Strictly speaking, the Pushed event ISN'T a function.
    // Its a series of functions.. internally it must be some
    // kind of object with some kind of linked list inside.  But
    // in any case, this notation takes some getting used to,
    // but once you're used to it, it makes a whole lotta sense.

    // ALSO, take note that YOU MUST "FIRE" THIS EVENT FROM
    // WITHIN THE Button CLASS!

    // YOU __CANNOT__ "FIRE" AN EVENT OUTSIDE THE CLASS IN WHICH
    // THAT EVENT IS DECLARED.  I explain this in more detail
    // below.

    // Finally, print out another message after firing all
    // event functions.
    Console.WriteLine("Great, now my paint is all dented");
  }
}


public class Program
{
  // one function that will be attached
  // to the Button's Pushed event.  Notice
  // it can be attached because it has
  // no arguments and return type void.
  public static void ButtonPusher()
  {
    Console.WriteLine("HI THERE!!");
  }

  // another function that will be attached
  // to the button's Pushed event
  public static void func2()
  {
    Console.WriteLine("HELLO!!");
  }

  // notice because of our use of the ACTION delegate
  // type in the Pushed event declaratino, we CANNOT
  // attach THIS functino to the Pushed event.  THe
  // signature of functions you attach to the Pushed event
  // MUST MATCH the signature of the delegate type you used
  // in the declaration of the event.  In this case the Pushed
  // event uses the Action delegate type, which is defined
  // in the .NET framework to be a delegate of return type void,
  // and accepts no arguments.
  public static void sayHi( int howMany )
  {
    Console.WriteLine( howMany + " hi's to yoU!" ) ;
  }

  static void Main( string[] args )
  {
    Button b = new Button();

    // Attach 3 functions to execute
    // when the Pushed event fires
    // from within the button
    b.Pushed += new Action( ButtonPusher );

    b.Pushed += new Action( func2 );

    b.Pushed += new Action( ButtonPusher );

    // Illegal:  sayHi does not match the Action delegate type,
    // and the Pushed event requires that the functions chained on
    // match the Action delegate type.
    ////////b.Pushed += new Action( sayHi ) ;

    // now push the button, (which in turn,
    // FIRES THE EVENT)
    b.PushButton();


    // Finally, notice this is illegal
    /////b.Pushed() ;
    // The error is:
    // Error 1 The event 'Button.Pushed' can only appear on the
    // left hand side of += or -= (except when used from
    // within the type 'Button')
    
    // Reason:  basically the EVENT member itself
    // is kind of private to the class.  You're not
    // allowed to "invoke it" from outside the Button class.

    // But you can, as we did here, define a function
    // within the Button class that in turn, invokes/fires
    // the event.

  }

}


I think events are rooted in the observer pattern, but you don’t have to know the observer pattern to apply what we’re working on here.

Ah, so, declaring a custom event in C# is fun. To summarize, here’s what you need:

1. A public delegate, which effectively specifies the type of function (argument list, return type) that can be chained onto the event. In this example, we just used the .NET defined Action delegate, which has return type void and no arguments passed to it.
2. Declaration of the event itself, inside the class that the event can “happen to”
3. Lastly, you TRIGGER the event from within the class in which the event is declared. You MAY NOT trigger the event from outside the class in which it is declared (you may NOT type eventName() OUTSIDE THE CLASS, EVER.)

codeproject example

msdn tutorial / example

Modified msdn example, notes below

// events1.cs
using System;
using System.Collections;

namespace Example1
{
  // 1.  DECLARE A DELEGATE TYPE
  // that will be used to 
  public delegate void ChangedEventHandler( object sender, EventArgs e );



  public class ExtList : ArrayList
  {
    // 2.  DECLARE THE EVENT to be the type
    // of the delegate that you declared.
    public event ChangedEventHandler Changed;
    // An event IS an instance of a delegate
    // only several instances of the delegate
    // can be chained to execute in order.


    ///
    // Invoke the Changed event by explicitly
    // asking the Changed function to execute
    // (by a simple function call).
    protected virtual void OnChanged( EventArgs e )
    {
      if( Changed != null )
        Changed( this, e );
    }

    // Override some of the methods that can change the list;
    // invoke event after each
    public override int Add( object value )
    {
      int i = base.Add( value );

      // Trigger the OnChanged event.
      OnChanged( EventArgs.Empty );

      // This also could have been achieved by
      // simply invoking:
      //if( Changed != null )
      //  Changed( this, EventArgs.Empty ) ;
      // However the extra step above allows
      // a deriving class to actually add additional
      // functionality by overriding OnChanged
      // and then calling base.OnChanged( e ) when done.
      ////


      return i;
    }

    public override void Clear()
    {
      base.Clear();
      OnChanged( EventArgs.Empty );
    }

    public override object this[ int index ]
    {
      set
      {
        base[ index ] = value;
        OnChanged( EventArgs.Empty );
      }
    }
  }


  class Test
  {
    // This will be called whenever the list changes.
    private void ListChanged( object sender, EventArgs e )
    {
      Console.WriteLine( "This is called when the event fires." );
    }

    private void ListChanged2( object sender, EventArgs e )
    {
      Console.WriteLine( "SO WILL THIS, SUCKER." );
    }

    // Test the ExtList class.
    public static void Main()
    {
      // Create a new list.
      ExtList list = new ExtList();
      

      // Attach functions to execute.
      list.Changed += new ChangedEventHandler( list_Changed );
      list.Changed += new ChangedEventHandler( list_Changed2 );



      
      // Create a class that listens to the list's change event.
      Console.WriteLine("Adding an item");
      list.Add( "item 1" );
      
      // remove the first response function
      list.Changed -= new ChangedEventHandler( list_Changed ) ;


      Console.WriteLine( "Adding another item" ); 
      list.Add( "item 2" );
      

      list.Clear();
    }

    static void list_Changed2( object sender, EventArgs e )
    {
      Console.WriteLine( "INDEED IT HAS CHANGED!" );
    }

    static void list_Changed( object sender, EventArgs e )
    {
      Console.WriteLine( "THE LIST HAS CHANGED!" );
    }
  }
}

The point of event handlers is they are like a chain of functions that will execute in rapid succession on your command.

The msdn example is pretty good. In their example, they take a normal ArrayList and extend it with a Changed event. Now anytime the ArrayList is changed (through an addition, or a deletion, or totally clearing the list) the Changed event is TRIGGERED (basically, the Changed event is CALLED like a function would be), and so the SERIES of functions that were attached to the Changed event are executed immediately, in rapid succession. If there are no functions attached to the Changed event, then simply nothing happens.

What’s the difference between a C# event and just a normal function, you ask? Why can’t we just define a private function Changed() that executes on addition, on deletion, on clearing the list? WE COULD. But then we would have to MANUALLY provide some kind of List object that would be able to hold a series of delegates… it would just be more cumbersome.

But you are right. Events and event handlers don’t provide anything special above and beyond normal functions. The only thing good about them is how they provide a REALLY NICE mechanism to cause a set of functions to execute when certain things happen.

Creation of a custom event requires TWO things:

  1. A public delegate, which effectively specifies the type of function (argument list, return type) that can be chained onto the event
  2. Declaration of the event itself, inside the class that the event can “happen to”

The above two items look like this:

// Declare the delegate outside so any class can see it:
public delegate void ChangedEventHandler( object sender, EventArgs e );

public class ExtList : ArrayList
{
// DECLARE THE EVENT to be the type
// of the delegate that you want to be
// “attachable” this event.
public event ChangedEventHandler Changed;

// the WHOLE POINT of this “Changed” variable
// is that it will be like a queue that can accept
// a series of ChangedEventHandler objects
// (which are really going to be references to functions!)
}

So again, an event … is like a CHAIN OF FUNCTIONS.

A programmer attaches ANY NUMBER of functions to line up for execution simply by using += syntax:

class Test
{
// This will be called whenever the list changes.
private void ListChanged( object sender, EventArgs e )
{
Console.WriteLine( “This is called when the event fires.” );
}

private void ListChanged2( object sender, EventArgs e )
{
Console.WriteLine( “SO WILL THIS, SUCKER.” );
}

// Test the ExtList class.
public static void Main()
{
// Create a new list.
ExtList list = new ExtList();

// Attach functions to execute.
list.Changed += new ChangedEventHandler( list_Changed );
list.Changed += new ChangedEventHandler( list_Changed2 );

// now trigger their execution by adding an item:
Console.WriteLine(“Adding an item”);
list.Add( “item 1″ ); // we see output from BOTH ListChanged
// and ListChanged2(), in the order that we attached them.
}
}

So in the above, WHEN list.Changed() gets EXECUTED, what will automatically happen is, list_Changed (which is a function) will execute first, followed by list_Changed2.

OK OK, so now HOW DID WE TRIGGER THE EVENT TO FIRE?

You trigger execution of that “queue” of functions (first one chained on gets executed first.. all the way to the last one you chained on there) by INVOKING THE NAME OF THE event:

Changed() ;

HOWEVER, CALLS TO YOUR event MUST BE WITHIN THE CLASS IN WHICH THE event IS DEFINED

So, re-emphasizing this point:

Changed() ;

CANNOT BE CALLED OUTSIDE THE CLASS. This is when you get an error of the form

Error 1 The event ‘Example1.ExtList.Changed’ can only appear on the left hand side of += or -= (except when used from within the type ‘Example1.ExtList’)

The whole thing about “appearing only on the left hand side of += or -=” is that, TO EXTERNAL CLASSES, an event CANNOT BE DIRECTLY INVOKED.

An EVENT, what constitutes FIRING of that event is meant to be completely INTERNAL to the class that declares the event. Any public function of the class may fire the event, but code from outside the class MAY NEVER invoke the event directly (unless you actually provide a public function, that all it does, is invoke the event). There is an example of this in System.Windows.Forms.Button: button1.PerformClick(). All it does is invoke the Click event handler, which you are NOT free to do because you are NOT inside the Button class, ever.

What you’re supposed to do is provide “ins” to allow the event to be invoked.. a public function that, upon invokation, will FIRE the event.

The user of your class isn’t really supposed to think about FIRING the event though. They’re supposed to use your class like normal.. and when certain things happen (something was added to the list), the Changed() event, in this example, fires as a kind of SIDE EFFECT.

If a class does not expose a function to trigger an event, then you simply cannot cause that event to trigger yourself, unless you inherit that class and write a function in the derived class that invokes the event.

Whew. That is all I have to say about events.

Follow

Get every new post delivered to your Inbox.

Join 43 other followers