This project has moved. For the latest updates, please go here.
The following rules outline the usage guidelines for events:
  • Choose a name for your event based on the recommended Event Naming Guidelines.
  • When you refer to events in documentation, use the phrase, "an event was raised" instead of "an event was fired" or "an event was triggered."
  • In languages that support the void keyword, use a return type of void for event handlers, as shown in the following C# code example.

      public delegate void MouseEventHandler ( object sender, MouseEventArgs e );
  • Use strongly typed event data classes when an event conveys meaningful data, such as the coordinates of a mouse click.
  • Event classes should extend the System.EventArgs Class, as shown in the following example.

public class MouseEvent: EventArgs { }

Use a protected virtual method to raise each event. This technique is not appropriate for sealed classes, because classes cannot be derived from them. The purpose of the method is to provide a way for a derived class to handle the event using an override. This is more natural than using delegates in situations where the developer is creating a derived class. The name of the method takes the form OnEventName, where EventName is the name of the event being raised. For example:

public class Button {
   ButtonClickHandler onClickHandler;
      
   protected virtual void OnClick ( ClickEventArgs e ) {
      // Call the delegate if non-null.
      if ( onClickHandler != null ) {
            onClickHandler ( this, e );
      }      
   }
}
The derived class can choose not to call the base class during the processing of OnEventName. Be prepared for this by not including any processing in the OnEventName method that is required for the base class to work correctly.
  • You should assume that an event handler could contain any code. Classes should be ready for the event handler to perform almost any operation, and in all cases the object should be left in an appropriate state after the event has been raised. Consider using a try/finally block at the point in code where the event is raised. Since the developer can perform a callback function on the object to perform other actions, do not assume anything about the object state when control returns to the point at which the event was raised. For example:
public class Button {
   ButtonClickHandler onClickHandler;

   protected void DoClick ( ) {
      // Paint button in indented state.
      PaintDown ( );
         try {
         // Call event handler.
         OnClick ( );         
      }
      finally {
         // Window might be deleted in event handler.
         if ( windowHandle != null ) {
            // Paint button in normal state.
            PaintUp ( );
         }
      }
   }

   protected virtual void OnClick ( ClickEvent e ) {
      if ( onClickHandler != null ) {
         onClickHandler ( this, e );
      }
   }
}
  • Use or extend the System.ComponentModel.CancelEventArgs Class to allow the developer to control the events of an object. For example, the TreeView control raises a BeforeLabelEdit when the user is about to edit a node label. The following code example illustrates how a developer can use this event to prevent a node from being edited.

public class Form1: Form {
   TreeView treeView1 = new TreeView ( );

   void treeView1_BeforeLabelEdit ( object source, NodeLabelEditEventArgs e ) {
      e.CancelEdit = true;
   }
}

Note that in this case, no error is generated to the user. The label is read-only.

Cancel events are not appropriate in cases where the developer would cancel the operation and return an exception. In these cases, you should raise an exception inside of the event handler in order to cancel. For example, the user might want to write validation logic in an edit control as shown.

public class Form1: Form {
   EditBox edit1 = new EditBox ( );

   void TextChanging ( object source, EventArgs e ) {
      throw new RuntimeException ( "Invalid edit" );
   }
}

Last edited Jul 19, 2010 at 11:41 AM by camalot, version 2