Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generic Event Arguments

0.00/5 (No votes)
5 Oct 2009 1  
Save your keyboard and fingers, and write less code using generic event arguments.

Introduction

Let me start by saying that this idea isn't new, but I constantly find myself using this code and I couldn't find a decent example on CodeProject, so here it is for anyone that wants/needs it. Also, as the principle is very simple, this is not going to be the longest article in history!

Microsoft introduced a few generic delegates that are commonly used in .NET 2.0 such as EventHandler<TEventArgs>, but for some reason didn't include a generic version of EventArgs itself.

We are all familiar with event arguments that get passed by objects in our code. They are a very useful way of sending values between classes. The problem is that every time we want to pass a different type of value, we need to create a new class that derives from System.EventArgs (or a class that already does this).

Normal EventArgs

For example, if we wanted to pass an int from a class in our code, we would need to write a class such as this:

public class IntEventArgs : EventArgs
{
    public IntEventArgs(int value)
    {
        Value = value;
    }
    
    public int Value { get; private set; }
}

OK, so this is no problem and very trivial. If we need a similar class, but want to pass a string, then we have to write another class that is essentially the same, but using string in place of int. This is a waste of our time and exactly what Generics are there for!

Generic EventArgs

The same class using Generics looks like this:

EventArgs<T>

public class EventArgs<T> : EventArgs
{
    public EventArgs(T value)
    {
        Value = value;
    }

    public T Value { get; private set; }
}

Now we can pas in any type we like in place of the int, without needing to write another class ever!

In the real world, there are many types of event arguments that could be suitable for applying Generics to. Because all our generic versions will have the same property, I use a very simple interface that all my generic event argument classes implement.

IEventArgs<T>

public interface IEventArgs<T>
{
    T Value { get; }
}

So the declaration of the class above becomes:

public class EventArgs<T> : EventArgs, IEventArgs<T>
{
    // ...
}

The next obvious candidate is System.ComponentModel.CancelEventArgs. The generic version of this derives from CancelEventArgs and IEventArgs. This class is only slightly complicated by the need for more than one constructor and calls to the base constructor.

CancelEventArgs<T>

public class CancelEventArgs<T> : CancelEventArgs, IEventArgs<t>
{
    public CancelEventArgs(T value)
        : this(value, false)
    { }
    public CancelEventArgs(T value, bool cancel)
        : base(cancel)
    {
        Value = value;
    }

    public T Value { get; private set; }
}

I find the most common place I use cancel event arguments are in XxxChanging events, where it is useful to provide the current value, the new value, and the option to cancel, all in one event argument class instance. Instead of using a different generic EventArgs for this, it is just as easy to create a generic ChangingData class and use that as the type for our generic CancelEventArgs.

ChangingData<T>

public class ChangingData<T>
{
    public ChangingData(T oldData, T newData)
    {
        OldData = oldData;
        NewData = newData;
    }
    
    public T OldData { get; private set; }
    public T NewData { get; private set; }
}

In Use

In use, an event declaration might look like this if the value that was changing was an int:

public event EventHandler<CancelEventArgs<ChangingData<int>>> ValueChanging;

That's a generic event handler with a generic cancel event argument whose type is a generic class! The actual Value property implementation in such a class would probably look similar to this:

public int Value
{
    get { return _Value; }
    set
    {
        if (_Value != value)
        {
            /* Create a new generic CancelEventArgs instance using a
            * generic ChangingData instance for the type */
            CancelEventArgs<ChangingData<int>> e =
                new CancelEventArgs<ChangingData<int>>(
                    new ChangingData<int>(_Value, value));
            // Raise the ValueChanging event passing the generic CancelEventArgs instance
            OnValueChanging(e);
            // Check the Cancel state. If false, do nothing
            if (!e.Cancel)
            {
                // Update _Value and raise the ValueChanged event
                _Value = value;
                OnValueChanged(new EventArgs<int>(_Value));
            }
        }
    }
}

In fact, this is pasted straight out of the source code attached.

Conclusion

So that's it, a very simple way to reduce the amount of code we have to write when creating events, by using Generics. The attached source code has fully commented versions of all the classes above as well as a working demonstration of them in use. I hope you find them useful as I do.

History

  • V1. 5th October 2009: Initial version.
  • V2. 5th October 2009: Minor fixes.
  • V3. 5th October 2009: Fixed multiple <>.
  • V4. 5th October 2009: Fixed more <> - damn MS for picking these for Generics!
  • V5. 5th October 2009: And one more!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here