Click here to Skip to main content
15,881,089 members
Articles / Programming Languages / C#
Article

EventyList: The List with Events

Rate me:
Please Sign up or sign in to vote.
3.82/5 (9 votes)
28 Apr 2008GPL33 min read 83.4K   723   28   28
Generic list class based on Microsoft's implementation with few additional events to handle like BeforeAdd, OnAdd, BeforeRemove, etc.

Introduction

List and collection classes in the System.Collections namespace have one disadvantage: they do not support events for common tasks. Of course, I am not saying they should. I am well aware of the performance issues, etc. However, in everyday life when the size of our lists takes about 10-100 items, performance is not so important. So, events. I have written a nice class, EventyList, which has events for common methods such as Add, AddRange, Remove, RemoveRange, RemoveAt, get/set accessors, Clear and Insert.

All of these events have two versions: "Before" and "On", e.g. BeforeAdd and OnAdd. "Before" events are called before the given action is performed, but after any error checking. I mean that they fire "just before". They also have a "Cancel" field and so, for example, the Add method can be conditionally cancelled using the BeforeAdd event. BeforeAdd would pass the following arguments: EventyList, the item to add and "ref cancel".

Prerequisites

This article assumes that you know something about events. If you do not, I recommend the article written by AbhishekKhanna: Unravelling Delegates & Events. If you do not wish to have complete knowledge about delegates and events, but you are interested in using my class, please read the rest of this section.

The Event term is very intuitive. When an "event" happens, a signal is sent to all of its listeners. To become a listener of an event, you have to "handle" it using accurate syntax. There must be a specified method (procedure) that has to be executed when an event "fires". To handle events of EventyList or any other class, just write:

C#
// C#
// First declare and initialize EventyList somewhere
EventyList<int> myList = new EventyList<int>();

// Inside some method...
void Form1_Load()
{
    // ... handle an event this way:
    myList.OnAdd += new EventyList<int>.EventyListAfterEventHandler(
        myList_OnAdd);
    // (if you input "myList.OnAdd +=", Visual Studio will tell
    // you that you can press TAB - just do it.

    // Raise "OnAdd" by adding an item:
    myList.Add(123);

    // In C# 2.0 (Visual Studio 2005), you could also
    // write the same thing this way:
    // myList.OnAdd += myList_OnAdd;
    // (As we can read in C# specification: "C# compiler has
    // enough context to determine... blah blah")
}

// When OnAdd fires, this method will be executed
void myList_OnAdd(EventyList<int /> sender, int item)
{
    MessageBox.Show("OnAdd fired!");
}

I am sorry to say this, but I do not have VB installed. So, I cannot provide a suitable code sample for this language. This is actually everything that is needed, however.

Source Code

The list implementation itself is identical to that of Microsoft. I have copied the source code of System.Collections.Generic.List<T> to my class and then modified it for my usage. If you wonder where I got the MsCorLib source from, you can download it from the Shared Source Common Language Infrastructure 2.0 Release: Download Details or browse for it at Rotor BCL Documentation.

Using the Code

Well, the usage is the same as List's usage. Here we go:

C#
// C#
// Add these to a header of your source file
using Gajatko.Common;
using System.Windows.Forms;

void EventyTest()
{
    // Declare EventyList
    EventyList<string> eve = new EventyList<string>();

    // Handle events
    eve.OnAction += eve_OnAction;
    eve.BeforeAdd += eve_BeforeAdd;
    // (Now look at eve_BeforeAdd and eve_OnAction below)
    // Of course there is an OnAdd event, but I want to
    // show how OnAction works

    // Add items
    eve.Add("Hello World");
    eve.Add("Hello Dog");
    eve.Add("Hello Chuck Norris");

    // Construct summary
    string summary = eve[0];
    for (int i = 1; i < eve.Count; i++)
    {
        summary += ", " + eve[i];
    }

    // Display summary in msgbox
    MessageBox.Show("Summary:" + Environment.NewLine + summary, "Summary",
    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

void eve_BeforeAdd(EventyList<string> sender,
    ref string item, ref bool cancel)
{
    // Do some validation of items.
    if (item.Contains("Norris"))
    {
        MessageBox.Show(
        "Cannot add item: \"" + item + "\". Personal data protection works!",
        "Access denied.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

        // Prohibit Norris.
        cancel = true;
    }
    if (item.Contains("Dog"))
    {
        MessageBox.Show("We do not like dogs", "Animal refusal.",
            MessageBoxButtons.OK,
            MessageBoxIcon.Exclamation);

        // Replace all dogs in the world with cats. Note that
        // we can modify data.
        item = item.Replace("Dog", "Cat");
    }
}

void eve_OnAction(EventyList<string> sender,
    EventyList<string />.ActionEventArgs e)
{
    // This is an OnAction demo - OnAction events fires always AFTER the
    // specialized one does.
    // Unfortunately, "BeforeAction" does not support changing data - only
    // cancelling is possible.
    if (e.Action == EventyList<string>.ListAction.Add)
        MessageBox.Show("The item \"" + e.Item + "\" was successfully added.",
            "Eventy message", MessageBoxButtons.OK,
            MessageBoxIcon.Exclamation);
}

As you can see, it's nothing special, but it works fine anyway.

Extending EventyList Class

All event raisers are marked as virtual, which makes extending the class very simple, even without modifying the original code. The following example shows how to make a list of integers that ignores all Add method calls. It also adds -1 after each AddRange invocation.

C#
public class IntegerEventyList : EventyList<int>
{
    protected override bool beforeAdd(ref int item)
    {
        return false;
    }
    protected override void onAddRange(IEnumerable<int> coll)
    {
        base.onAddRange(coll);
        Add(-1);
    }
}

After operations, the list shown below...

C#
IntegerEventyList list = new IntegerEventyList();
list.AddRange(new int[] {0, 556, 23, 88});
list.Add(5);
list.AddRange(new int[] { });

... would contain the following items:

0
556
23
88
-1
-1

Download

There is a source file available for download, eventylist.cs, as well as a compiled library, eventylist.dll. You can include the source file or DLL in your project. However, I recommend that you include only eventylist.cs instead of the DLL. Please inform me about any bugs found.

History

  • 4th July, 2007 -- Original version posted
  • 9th July, 2007 -- Improved thread safety and some other things
  • 25th April, 2008 -- Updated download files

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
Poland Poland
My name is Jacek. Currently, I am a Java/kotlin developer. I like C# and Monthy Python's sense of humour.

Comments and Discussions

 
GeneralMy vote of 5 Pin
JayantaChatterjee28-Jan-15 18:56
professionalJayantaChatterjee28-Jan-15 18:56 
Questionwindows events Pin
Mokhtar abdualmomien22-Jan-11 12:18
Mokhtar abdualmomien22-Jan-11 12:18 
AnswerRe: windows events Pin
Lutosław22-Jan-11 22:24
Lutosław22-Jan-11 22:24 
GeneralBug Found!! Pin
Member 223039725-Apr-08 0:24
Member 223039725-Apr-08 0:24 
GeneralRe: Bug Found!! Pin
Lutosław25-Apr-08 6:20
Lutosław25-Apr-08 6:20 
GeneralBeware the license! Pin
ggeurts12-Jul-07 14:19
ggeurts12-Jul-07 14:19 
GeneralRe: Beware the license! Pin
Lutosław13-Jul-07 3:30
Lutosław13-Jul-07 3:30 
QuestionWhy not C5? Pin
gerektoolhy11-Jul-07 20:56
gerektoolhy11-Jul-07 20:56 
AnswerRe: Why not C5? [modified] Pin
Lutosław12-Jul-07 0:19
Lutosław12-Jul-07 0:19 
GeneralRe: Why not C5? Pin
gerektoolhy12-Jul-07 4:31
gerektoolhy12-Jul-07 4:31 
GeneralRe: Why not C5? Pin
Lutosław12-Jul-07 4:57
Lutosław12-Jul-07 4:57 
GeneralBindingList Pin
Robert Ensor10-Jul-07 12:36
Robert Ensor10-Jul-07 12:36 
GeneralRe: BindingList Pin
Lutosław11-Jul-07 0:17
Lutosław11-Jul-07 0:17 
GeneralUse the standard EventHandler signature Pin
Obiwan Jacobi10-Jul-07 1:59
Obiwan Jacobi10-Jul-07 1:59 
GeneralRe: Use the standard EventHandler signature Pin
Lutosław10-Jul-07 3:30
Lutosław10-Jul-07 3:30 
GeneralMulti-threading Pin
mcarbenay4-Jul-07 6:03
mcarbenay4-Jul-07 6:03 
GeneralRe: Multi-threading Pin
Lutosław4-Jul-07 23:40
Lutosław4-Jul-07 23:40 
GeneralRe: Multi-threading Pin
Obiwan Jacobi10-Jul-07 1:56
Obiwan Jacobi10-Jul-07 1:56 
GeneralRe: Multi-threading Pin
mcarbenay10-Jul-07 12:32
mcarbenay10-Jul-07 12:32 
GeneralRe: Multi-threading Pin
Lutosław11-Jul-07 0:26
Lutosław11-Jul-07 0:26 
GeneralRe: Multi-threading Pin
mcarbenay11-Jul-07 2:16
mcarbenay11-Jul-07 2:16 
GeneralRe: Multi-threading Pin
Emile van Gerwen19-Jul-07 8:28
Emile van Gerwen19-Jul-07 8:28 
Michael,

you wrote:
if you're not on the same thread, you can't be sure that your command will be taken into account immediatly - it's something better than what was there before.

...and doesn't add new problems. Bottom line is that you always have to expect an event after unsubscribing in any multi-threaded or otherwise asynchronous communication pattern.

I agree with your explanation.

Djeez.
GeneralGood job, but a question Pin
FredericSivignonPro4-Jul-07 5:55
FredericSivignonPro4-Jul-07 5:55 
GeneralRe: Good job, but a question Pin
KellyLeahy4-Jul-07 7:58
KellyLeahy4-Jul-07 7:58 
GeneralRe: Good job, but a question Pin
Lutosław4-Jul-07 23:48
Lutosław4-Jul-07 23:48 

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.