Click here to Skip to main content
15,881,715 members
Articles / Web Development / ASP.NET
Article

Extending .NET Events Functionality

Rate me:
Please Sign up or sign in to vote.
4.48/5 (15 votes)
14 Nov 2007CPOL6 min read 49.9K   1.2K   32   7
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.

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

Next we subscribe to the standard C# TickEvent

C#
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

C#
// 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:

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

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

C#
// 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).

C#
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.

C#
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.

C#
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:

JavaScript
<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:

C#
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)


Written By
Team Leader
Belgium Belgium
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionLock? Pin
Arne Evertsson26-Mar-13 23:24
Arne Evertsson26-Mar-13 23:24 
AnswerRe: Lock? Pin
Werner Willemsens27-Mar-13 0:11
Werner Willemsens27-Mar-13 0:11 

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

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