tags:

views:

327

answers:

4

I have a number of C# custom events that are associated with items being selected in a tree and then a context menu being used to trigger some action on the selected items. I started creating a separate EventArgs-based class for each custom event, to package the necessary data for the event.

In hind-sight though, I realize that most (maybe all) of my custom events will need to pass at least the list of underlying objects represented by the tree selection. Some events will likely need additional data as well.

With that in mind, I wonder if either of the following is an acceptable practice?

  • Use the same custom EventArgs-based class for multiple events (those that just need the object list to be passed). Obviously, that should work, but seems to break away from some of the recommended naming conventions used for wiring the event machinery.

  • Create a base class that wraps up my often-needed object list, and then derive additional classes from it as additional data is needed.

  • Maybe something else entirely?

Currently, I only have a handful of custom events, but need to add more. Since I see a pattern emerging with regard to the data needed by each event, I'd like to have a better plan before continuing.

Thanks for any advice.

+1  A: 

I would go for both one and two. Create a base class that you can use for the majority of your events and then derive from that class for the events that need something else. If you look at the .NET libraries that's the way that they've been built.

Just make sure that you use an appropriate naming convention. The base class should not include the name of a specific event, only a class that is being used in only one event should reference the name of the event. The base class would probably include the name of your control.

Jeff Hornby
Thanks Jeff. That's good naming convention advice and I'll definitely use it.
Jeff Godfrey
+1  A: 

You mean something like

public class EventArgs<TValue> : EventArgs
{
    public TValue Value { get; }

    /* ... */
}

?

Where I can use as Value whatever I like?

Yes, I'm happily using such a thing. I don't think it's a problem.

herzmeister der welten
+4  A: 

I've been done this road before. I went with your second idea of having a base event class that wraps common data. If no event will directly use it, make the base class internal to avoid exposing unnecessary objects to users. Having the base class also allows you to add more event data at a later time. Since the class is internal, users of it are outwardly unaffected by changes.

Matthew Ferreira
The abstract base class scenario works nicely in this situation because you may wish to add more custom events later, and they may also have the same requirement.
Mike Hofer
+1  A: 

There are many examples of this in the .NET framework. It would be quite consistent to do something along the same lines. Here are a couple:

  • System.ComponentModel.ProgressChangedEventArgs (provides ProgressPercentage and UserState to derived types)
    • System.Net.DownloadProgressChangedEventArgs
    • System.Net.UploadProgressChangedEventArgs
    • System.Windows.Documents.Serialization.WritingProgressChangedEventArgs
  • System.ComponentModel.CancelEventArgs (provides the Cancel member to derived types)
    • Microsoft.VisualBasic.ApplicationServices.StartupEventArgs
    • System.ComponentModel.DoWorkEventArgs
    • System.Configuration.SettingChangingEventArgs
    • System.Drawing.Printing.PrintEventArgs
    • System.Web.UI.WebControls.DetailsViewDeleteEventArgs
    • System.Web.UI.WebControls.DetailsViewInsertEventArgs
    • System.Web.UI.WebControls.DetailsViewModeEventArgs
    • System.Web.UI.WebControls.DetailsViewPageEventArgs
    • System.Web.UI.WebControls.DetailsViewUpdateEventArgs
    • System.Web.UI.WebControls.EntityDataSourceChangingEventArgs
    • Literally 50 more of these

Edit:

I might use a class like this (not abstract).

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

public class CollectionEventArgs<T> : EventArgs
{
    public static readonly CollectionEventArgs<T> Empty = new CollectionEventArgs<T>();

    public CollectionEventArgs(params T[] items)
    {
        this.Items = new ReadOnlyCollection<T>(items);
    }

    public CollectionEventArgs(IEnumerable<T> items)
    {
        this.Items = new ReadOnlyCollection<T>(items.ToArray());
    }

    public ReadOnlyCollection<T> Items
    {
        get;
        private set;
    }
}
280Z28