Recipe9.13.Set up Event Handlers Without the Mess


Recipe 9.13. Set up Event Handlers Without the Mess

Problem

In versions of the .NET Framework previous to 2.0, the System.EventHandler delegate could be used on events in which the arguments were always of type System.EventArgs. This was great if you really didn't care about any data that went along with an event. But as you are all fine programmers and can see the possibilities of passing data along with the event, you had to set up a delegate and an event for every event you wanted. Example 9-14 demonstrates an old newspaper class that sends news to subscribers using the pre-.NET 2.0 event and event-handling methodology.

Example 9-14. Using pre-.NET 2.0 event and event-handling methods

 class IWannaKnowThen {     // Show the client talking to the newspaper class.     public static void TryMe()     {         // Make a newspaper class.         OldNewsPaper DailyPaperFlash = new OldNewsPaper();         // Hook up the news event to our handler (StaleNews).         DailyPaperFlash.NewsEvent +=             new OldNewsPaper.NewsEventHandler(StaleNews);         // Send news.         DailyPaperFlash.TransmitNews("Patriots win first super bowl!");         DailyPaperFlash.TransmitNews("W takes office amongst recount.");         DailyPaperFlash.TransmitNews("VS2003 is sooo passe");     }     // Write out news to debug stream     private static void StaleNews(object src, OldNewsEventArgs nea)     {         System.Diagnostics.Debug.WriteLine(nea.LatestNews);     } } // EventArgs derived class to hold our news data public class OldNewsEventArgs : EventArgs {     private string _latestNews;          public OldNewsEventArgs(string latestNews)     {         _latestNews = latestNews;     }     public string LatestNews     {         get { return _latestNews; }     } } // OldNewsPaper class public class OldNewsPaper {     // Allow clients to get the news.     public delegate void NewsEventHandler(Object sender, OldNewsEventArgs nea);     public event NewsEventHandler NewsEvent;          // Provide nice wrapper for sending news to clients.     public void TransmitNews(string news)     {         NewsEventHandler newsEvent = NewsEvent;         if (newsEvent != null)             newsEvent(this, new OldNewsEventArgs(news));     } } 

This code sets up an event that will report the news to subscribers as it comes in. It passes them the news data as an argument of type OldNewsEventArgs that has a LatestNews property.

As you can see from this example, whenever you had to set up multiple event handlers, it became an exercise in copy-and-paste and changing the event argument class type over and over again. It would be nice to not have to define lots of delegates and events just to change the event arguments, as all events (and corresponding handlers) are supposed to look like this:

 void [EventHandler](object sender, [EventArgs] args) {      // Do something about this event firing. } 

Solution

EventHandler<T> takes a type parameter that represents the type of the System.EventArgs derived class to use in your event handlers. The beauty of this is that you no longer have to keep creating a delegate and an event for every event you wish to publish from your class. Even better, the Framework has to have only one event delegate instead of one for every event that passes custom data! Using the example shown in the Problem section, you can now rewrite the declaration of the event handler like this:

 // Old way public delegate void NewsEventHandler(Object sender, OldNewsEventArgs nea); public event NewsEventHandler NewsEvent; // New way public event EventHandler<OldNewsEventArgs> NewsEvent; 

Now you set up the nice wrapper function to allow the user to easily trigger the event:

 // Old way public void TransmitNews(string news) {     // Copy to a temporary variable to be thread-safe.     NewsEventHandler newsEvent = NewsEvent;     if (newsEvent != null)         newsEvent(this, new NewsEventArgs(news)); } // New way public void TransmitNews(string news) {     // Copy to a temporary variable to be thread-safe.     EventHandler<OldNewsEventArgs> oldNews = NewsEvent;     if (oldNews != null)         oldNews(this, new OldNewsEventArgs(news)); } 

The client can then hook up to the OldNewsPaper class like this:

 // Old way class IWannaKnowThen {     // Show the client talking to the newspaper class.     public static void TryMe()     {         // Make a newspaper class.         OldNewsPaper DailyPaperFlash = new OldNewsPaper();         // Hook up the news event to our handler (StaleNews).         DailyPaperFlash.NewsEvent +=              new OldNewsPaper.NewsEventHandler(StaleNews);                  // Send news.         DailyPaperFlash.TransmitNews("Patriots win first super bowl!");         DailyPaperFlash.TransmitNews("W takes office amongst recount.");         DailyPaperFlash.TransmitNews("VS2003 is sooo passe");     }     // Write out news to debug stream.     private static void StaleNews(object src, OldNewsEventArgs nea)     {         System.Diagnostics.Debug.WriteLine(nea.LatestNews);     } } // New way class IWannaKnowNow {     public static void TryMe()     {         // Make a newspaper class.         OldNewsPaper DailyPaperFlash = new OldNewsPaper();         // Hook up the news event to our handler (BreakingNews).         DailyPaperFlash.NewsEvent +=             new EventHandler<OldNewsEventArgs>(BreakingNews);         // Send news.         DailyPaperFlash.TransmitNews("Patriots win again!");         DailyPaperFlash.TransmitNews("4 more years for W.");         DailyPaperFlash.TransmitNews("VS2005 & .NET 2.0 Rocks LA");     }     private static void BreakingNews(object src, NewsEventArgs nea)     {         System.Diagnostics.Debug.WriteLine(nea.LatestNews);     } } 

Discussion

The main benefit of using the generic EventHandler instead of System.EventHandler is that you write less code. Being able to declare a generic delegate allows you to have one delegate definition for multiple types. Why is this interesting, you might ask? Previously, when a delegate or event was declared by a class that wanted to publish information and allow multiple client classes to subscribe to it, if any data were to be passed along to the client classes, the convention was that a new class that derived from System.EventArgs had to be created. Then the class would be instantiated, filled with the data, and passed to the client. If the publishing class had only one event to notify people of, this wasn't too bad. If the publishing class had a lot of events, say like a class derived from a UserControl, there would have to be a separate class derived from System.EventArgs and a separate event defined for every event that needed different data passed to it. Now with a generic delegate, you can simply declare one delegate/event for each list of parameters you deal with, then declare the type-specific events you need. Since events are supposed to have this signature:

 void eventname( object sender, System.EventArgs args) 

the kind folks at Microsoft gave you System.EventHandler<T> to deal with the case of most events. If your code does have events defined that have more than two parameters, there would need to be a new delegate created to be the base of those events. Since most events do not have more than two parameters, this is a bit nonstandard but not out of the question.

See Also

See Recipes 9.5, 9.6, and 9.9; see the "Generic EventHandler" and "System. EventHandler" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net