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

Extending .NET Events Functionality

, 14 Nov 2007
Rate this:
Please Sign up or sign in to vote.
This article explains how to extend the standard .NET event and delegate mechanism with extra features
Screenshot - sample1.jpg

Introduction

Anyone who has used .NET events and delegates understands its power and ease of use. Creating an event and subscribing onto it has become quite simple in C# or VB.NET classes. Moreover, it is a helpful mechanism to implement (for instance) a responsive user interface. When carefully designed you don't need to poll for an object's state, instead you signal its state by means of an event.

But despite its ease of use, some functionality is still missing. Sometimes it is not only desired to listen to an event, but it might be useful to trigger the event a first time when you subscribe onto it. Standard .NET events only allow you to subscribe without knowing the initial state/value, but as long as the event isn't triggered, you cannot display it correctly. Typically you need to get (poll) the value first with a separate method or property to know the object's current state/value.

In this article I will describe in C# how you can add this extra event functionality to your classes. With little effort you can convert this to VB.NET as well.

At the end of the article, I will also show you how you can send an event from you C# class and how you can subscribe onto it from JavaScript without your class having to be an ActiveX control. In normal circumstances you can only subscribe to ActiveX control objects from JavaScript running from HTML or ASP pages.

Background

At first sight the C# delegate class looks like any other ordinary class. But as soon as you try to derive from it, to extend its base functionality, the C# compiler will emit an error that it's not allowed to derive from the delegate class. The same applies to events. Delegates and events are special compiler generated classes and are treated in a special way. The only solution is to create a new class where we can implement our new event code.

A Look at the Code

Situation 1: Standard Event

Let us first look how you would use normal events in C#. First we create the Clocks.Clock class that contains the sample code like this.

private Clocks.Clock m_clock = new Clocks.Clock();

Next we subscribe to the standard C# TickEvent

m_clock.TickEvents += new Clocks.TickDelegate(Clock_Tick1);

where Clock_Tick1 is the function that will be called when the event is triggered. In order for the Clocks.Clock class to trigger the standard event it now calls

// Trigger standard event
if (TickEvent != null) TickEvent(ticks);

Situation 2: The Base ExtendedClockDelegate Class

As mentioned before we need to create a new delegate class to implement our new functionality. This is the ExtendedClockDelegate class. The first thing we do is adding the same standard event mechanism. This will allow us to subscribe to the event as follows:

m_clock.ExtendedClockEvents.TickEvents += new Clocks.TickDelegate(
   Clock_Tick2);

In order for the Clocks.Clock class to trigger the extended event it now calls:

// Trigger extended event 
m_extendedClockEvents.TriggerEvent(ticks);

Situation 3: Align the Base ExtendedClockDelegate Class Event with the Standard Event

If you look carefully at both previous code samples, you notice that there is an extra "indirection" to subscribe to the event via the ExtendedClockDelegate class. Can we somehow avoid this? The answer is yes. If we overload the operator + and operator - of the ExtendedClockDelegate we can write again (as in situation 1).

m_clock.ExtendedClockEvents += new Clocks.TickDelegate(Clock_Tick3);

Situation 4: Extending the ExtendedClockDelegate Class More

So far, we have not really added a lot of new functionality. Let us now add the extra AddEventHandler and RemoveEventHandler methods. These methods allow us to subscribe to the ExtendedClockDelegate class events, but we can now foresee extra method parameters that define how we want to subscribe. For instance, we can allow to subscribe to an event and have the event triggering our callback method immediately once we are subscribed. In our example we will send along the time value that was used when the last event was triggered. The code looks now as follows.

m_clock.ExtendedClockEvents.AddEventHandler(new Clocks.TickDelegate(
    Clock_Tick4), Clocks.EventCondition.FireToCurrentSubscriber);

Situation 5: Variation on Situation 4

In our last example, we changed the AddEventHandler and RemoveEventHandler methods behaviour. Now we allow the subscriber code to define what the time value of the callback method should be when it triggers at the moment we subscribe. The code looks now as follows.

m_clock.ExtendedClockEvents.AddEventHandler(new Clocks.TickDelegate(
    Clock_Tick5), Clocks.EventCondition.FireToCurrentSubscriber, 1234);

At the moment the subscription is finished, the callback method will be called instantly with the time value set to 1234.

Subscribing to C# Events from JavaScript

When running a JavaScript from HTML or ASP, it is only possible to subscribe to events when your object is a full ActiveX control, for example the button_onclick() event. But most of everyday classes are not controls. How can you subscribe then to an event from a non ActiveX control? Well, the Java runtime will translate the JavaScript callback function in a COM object with a default method (good old plain Automation feature). If you add a special Property to your C# class where you can set or get an object, you can assign the JavaScript callback method to your class Property (that I called ScriptCallbackObject). How can the ExtendedClockDelegate class know how to call the COM object's default method? The answer is: .NET's powerful feature reflection. Note that the C# classes involved need to be registered for COM interop, hence the "[System.Runtime.InteropServices.xxx]" attribute statements. The following code snippet shows how this is done.

From JavaScript code:

<script type="text/javascript" language="jscript">
  ...
  var clock = new ActiveXObject("Clocks.clock");
  var extendedClockEvents = clock.ExtendedClockEvents();
  // Here you assign (subscribe to) your callback method!
  extendedClockEvents.ScriptCallbackObject = clock_Callback; 
  ...
  function clock_Callback(time)
  {
    document.getElementById("text_tag").innerHTML = time;
  }
  ...
</script>

From C# code:

System.Type t = scriptCallbackObject.GetType();
t.InvokeMember("", System.Reflection.BindingFlags.InvokeMethod, null, 
    scriptCallbackObject, new object[] { time });

Note the empty string "" as the first parameter of InvokeMember. This will execute the default method of the COM object without having to know its name. The sample package includes an HTML page that shows how you can do this.

About the Sample Code

The sample program contains a C# library with a Clocks.Clock class. This class contains a standard .NET event and the Clocks.ExtendedClockDelegate class. The latter class contains all extensions to the standard delegate. The clock class will trigger by default an event every second. Depending on the extra parameters of the AddEventHandler or RemoveEventHandler method, the event can be triggered additionally on your command.

The Clock executable program demonstrates the use of the Clocks.Clock class. It will subscribe to its events in all possible ways.

The event.html file demonstrates how you can use C# events in JavaScript. Note that for this sample to work, you need to register clocks.dll for COM interop. The csproj file contains this command in the post build section.

Summary

I hope that this article has given you enough insight on how you can extend the standard C# (or VB.NET) event mechanism. The next picture shows the possibilities that are available in the source code. On top of this list you can also use the += and -= operator.

Screenshot - sample2.jpg

Of course, it's up to you, as a programmer, to extend its functionality even more.

License

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

About the Author

Werner Willemsens
Team Leader
Belgium Belgium
No Biography provided

Comments and Discussions

 
GeneralExcellent Pinmembermerlin98115-Nov-07 3:57 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 14 Nov 2007
Article Copyright 2007 by Werner Willemsens
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid