Recipe9.6.Passing Specialized Parameters to and from an Event


Recipe 9.6. Passing Specialized Parameters to and from an Event

Problem

You have implemented Recipe 9.5, but you want to allow an event listener to cancel an action that raised a particular event. For example, if a class attempts to create a new directory, you want to be able to verify that the directory is being created in the correct location. If the directory is not being created in the correct location (perhaps an insecure location), you want to be able to prevent the directory's creation.

Solution

Use a class derived from EventArgs as the second parameter to the event handler. In this example, you use CancelEventArgs, a class defined in the .NET Framework Class Library. The Solution for Recipe 9.5 has been modified to include an event that is raised before the Create method of the DirectoryInfoNotify object actually creates a new path. An object of type CancelEventArgs is passed to this new event to allow any listeners of this event to cancel the Create method action. The modified class is shown in Example 9-6 with the modifications highlighted.

Example 9-6. Passing specialized parameters to and from an event

 using System;  using System.ComponentModel;  using System.IO; public class DirectoryInfoNotify {      public DirectoryInfoNotify(string path)      {         internalDirInfo = new DirectoryInfo(path);     }     private DirectoryInfo internalDirInfo = null;     public event CancelEventHandler BeforeCreate;     public event EventHandler AfterCreate;     public event EventHandler AfterCreateSubDir;     public event EventHandler AfterDelete;     public event EventHandler AfterMoveTo;          protected virtual void OnBeforeCreate(CancelEventArgs e)     {          CancelEventHandler beforeCreate = BeforeCreate;          if (beforeCreate != null)          {             beforeCreate (this, e);         }     }     protected virtual void OnAfterCreate( )     {         EventHandler afterCreate = AfterCreate;         if (afterCreate!= null)         {             afterCreate (this, new EventArgs( ));         }     }     protected virtual void OnAfterCreateSubDir( )     {          EventHandler afterCreateSubDir = AfterCreateSubDir;          if (afterCreateSubDir != null)          {             afterCreateSubDir(this, new EventArgs( ));          }      }     protected virtual void OnAfterDelete( )     {         EventHandler afterDelete = AfterDelete;         if (afterDelete != null)         {             afterDelete(this, new EventArgs( ));          }     }     protected virtual void OnAfterMoveTo( )     {         EventHandler afterMoveTo = AfterMoveTo;         if (afterMoveTo != null)         {             afterMoveTo(this, new EventArgs( ));         }     }     // Event firing members     public void Create( )     {         CancelEventArgs args = new CancelEventArgs(false);         OnBeforeCreate(args);                  if (!args.Cancel)         {             internalDirInfo.Create( );             OnAfterCreate( );         }     }     public DirectoryInfoNotify CreateSubdirectory(string path)     {          DirectoryInfo subDirInfo = internalDirInfo.CreateSubdirectory(path);          OnAfterCreateSubDir( );         return (new DirectoryInfoNotify(subDirInfo.FullName));     }     public void Delete(bool recursive)     {         internalDirInfo.Delete(recursive);         OnAfterDelete( );     }     public void Delete( )     {         internalDirInfo.Delete( );         OnAfterDelete( );     }     public void MoveTo(string destDirName)     {         internalDirInfo.MoveTo(destDirName);         OnAfterMoveTo( );     }     // Nonevent firing members     public virtual string FullName     {         get {return (internalDirInfo.FullName);}     }     public string Name     {         get {return (internalDirInfo.Name);}     }     public DirectoryInfoNotify Parent     {         get {return (new DirectoryInfoNotify(internalDirInfo.Parent.FullName));}     }     public DirectoryInfoNotify Root     {         get {return (new DirectoryInfoNotify(internalDirInfo.Root.FullName));}     }     public override string ToString( )      {          return (internalDirInfo.ToString( ));      }  } 

The DirectoryInfoObserver class contains each of the event listeners and is shown in Example 9-7 with the modifications highlighted.

Example 9-7. Modified DirectoryInfoOberver class

 public class DirectoryInfoObserver  {      public DirectoryInfoObserver( ) {}     public void Register(DirectoryInfoNotify dirInfo)     {                  dirInfo.BeforeCreate += new CancelEventHandler(BeforeCreateListener);         dirInfo.AfterCreate += new EventHandler(AfterCreateListener);         dirInfo.AfterCreateSubDir +=                 new EventHandler(AfterCreateSubDirListener);         dirInfo.AfterMoveTo += new EventHandler(AfterMoveToListener);         dirInfo.AfterDelete += new EventHandler(AfterDeleteListener);     }     public void UnRegister(DirectoryInfoNotify dirInfo)     {                  dirInfo.BeforeCreate -= new CancelEventHandler(BeforeCreateListener);         dirInfo.AfterCreate -= new EventHandler(AfterCreateListener);         dirInfo.AfterCreateSubDir -=                 new EventHandler(AfterCreateSubDirListener);         dirInfo.AfterMoveTo -= new EventHandler(AfterMoveToListener);         dirInfo.AfterDelete -= new EventHandler(AfterDeleteListener);     }          public void BeforeCreateListener(object sender, CancelEventArgs e)     {         if (!e.Cancel)         {             if (!((DirectoryInfoNotify)sender).Root.FullName.Equals(@"d:\"))              {                 e.Cancel = true;             }             else             {                 Console.WriteLine(                            "Notified BEFORE creation of directory--sender: " +                            ((DirectoryInfoNotify)sender).FullName);             }         }     }     public void AfterCreateListener(object sender, EventArgs e)     {         Console.WriteLine("Notified after creation of directory--sender: " +                            ((DirectoryInfoNotify)sender).FullName);     }     public void AfterCreateSubDirListener(object sender, EventArgs e)     {         Console.WriteLine("Notified after creation of SUB-directory--sender: " +                            ((DirectoryInfoNotify)sender).FullName);     }     public void AfterMoveToListener(object sender, EventArgs e)     {         Console.WriteLine("Notified of directory move--sender: " +                            ((DirectoryInfoNotify)sender).FullName);     }     public void AfterDeleteListener(object sender, EventArgs e)      {          Console.WriteLine("Notified of directory deletion--sender: " +                             ((DirectoryInfoNotify)sender).FullName);      }  } 

Discussion

The code for the modified DirectoryInfoNotify class contains a new event called BeforeCreate, which is raised from the OnBeforeCreate method. The OnBeforeCreate method is initially called by the Create method immediately before calling the Create method of the wrapped DirectoryInfo object. This setup allows the event listener for the BeforeCreate event to decide whether the directory creation operation should be canceled.

The DirectoryInfoObserver class contains a new method, BeforeCreateListener, which listens for the BeforeCreate event. In addition, the Register and UnRegister methods of this class contain logic to add/remove this event to/from the list of events that will be listened for on any registered DirectoryInfoNotify objects.

The OnBeforeCreate method of the DirectoryInfoNotify class is passed a parameter of a type called CancelEventArgs, which exists in the .NET FCL. This type derives from EventArgs and contains one useful property, called Cancel. This property will be used by the BeforeCreateListener method of the DirectoryInfoObserver class to determine whether the Create method should be canceled before it has a chance to create a new directory.

The CancelEventArgs object will be created in a DirectoryInfoNotify object, and when the BeforeCreate event is raised, the CancelEventArgs object will be passed to the BeforeCreateListener method on the DirectoryInfoObserver object. This method will then determine whether the creation of the directory should proceed or be canceled. The determination is made by comparing the root drive of the directory to see if it is anything but the D:\ drive; if so, the operation is canceled. This prevents any registered DirectoryInfoNotify objects from creating a directory on any drive other than the D:\ drive.

If multiple DirectoryInfoObserver objects are listening to the BeforeCreate event and one of those observer objects decides to cancel the operation, the entire operation is canceled unless you take some action to prevent this, as shown in Recipe 9.1.

The same CancelEventArgs object is referenced by each observer as well as each object that raised the event. This allows you to read the value of the Cancel property on the returned CancelEventArgs object in the Create method of the DirectoryInfoNotify object. If this property returns true, the operation cannot proceed; otherwise, the operation is permitted.

You are not confined to merely passing EventArgs objects or any of its subclasses found in the FCL; you can subclass the EventArgs class to create a specialized EventArgs type. This would be beneficial if the object passed in to the sender parameter of the event does not include all of the information that the XxxListener methods will need. For example, you could create the following specialized EventArgs class:

 public class UserEventArgs : EventArgs {     public UserEventArgs(string userName)     {         this.userName = userName;     }     private string userName = "";          public string UserName     {         get {return (userName);}     } } 

This class passes the name of the logged-on user to the XxxListener methods to allow them to determine whether the operation should continue based on that user's privileges. This is just one example of creating a specialized EventArgs class. You can create others to pass in whatever information your listeners need.

See Also

See Recipe 9.5; see the "Event Keyword," "EventHandler Delegate," and "Handling and Raising Events" 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