Event Usage Guidelines

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 MouseEventArgs
       Inherits EventArgs 
       ' Code for the class goes here.
    End Class
    [C#]
    public class MouseEvent: EventArgs {}
    
  • Use a protected (Protected in Visual Basic) 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
       Private onClickHandler As ButtonClickHandler  
       Protected Overridable Sub OnClick(e As ClickEventArgs)
          ' Call the delegate if non-null.
          If Not (onClickHandler Is Nothing) Then
             onClickHandler(Me, e)
          End If 
       End Sub
    End Class
    
    [C#]
    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
       Private onClickHandler As ButtonClickHandler
       Protected Sub DoClick()
          ' Paint button in indented state.      
          PaintDown()
          Try
             ' Call event handler.
             OnClick()      
          Finally
             ' Window might be deleted in event handler.
             If Not (windowHandle Is Nothing) Then
                ' Paint button in normal state.
                PaintUp()
             End If 
          End Try
       End Sub
       Protected Overridable Sub OnClick(e As ClickEvent)
          If Not (onClickHandler Is Nothing) Then
             onClickHandler(Me, e)
          End If
       End Sub
    End Class
    [C#]
    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
       Inherits Form
       Private treeView1 As New TreeView() 
    
       Sub treeView1_BeforeLabelEdit(source As Object, e As NodeLabelEditEventArgs)
          e.CancelEdit = True
       End Sub
    End Class
    [C#]
    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
       Inherits Form
       Private edit1 As EditBox = New EditBox() 
    
       Sub TextChanging(source As Object, e As EventArgs)
          Throw New RuntimeException("Invalid edit")
       End Sub
    End Class
    [C#]
    public class Form1: Form 
    {
       EditBox edit1 = new EditBox();
    
       void TextChanging(object source, EventArgs e) 
       {
          throw new RuntimeException("Invalid edit");
       }
    

See Also

Design Guidelines for Class Library Developers | Event Naming Guidelines | Class Member Usage Guidelines