Raising an Event

If you want your class to raise an event, you must provide the following three elements:

  • A class that provides event data.

  • An event delegate.

  • The class that raises the event.

Defining a Class to Provide Event Data

By convention in the .NET Framework, when an event is raised, it passes event data to its event handlers. The event data is provided by the System.EventArgs class or by a class that is derived from it.

Often, an event has no custom data; the fact that the event was fired provides all of the information that event handlers require. In this case, the event can pass an EventArgs object to its handlers. The EventArgs class has only a single member, Empty, that is not inherited from System.Object. It can be used to instantiate a new EventArgs class.

If an event does have custom data, it can pass an instance of a class derived from EventArgs to event handlers. Depending on the precise data the event passes to handlers, you may be able to use an existing event data class in the .NET Framework. For example, if your event handler allows the action associated with the event to be cancelled, you can use the CancelEventArgs class.

When you need to provide custom data to handlers and a existing class is not available, you can define your own event data class. It must derive from System.EventArgs. By convention, this class is named EventNameEventArgs. The following example illustrates such a custom event data class. It defines a class named AlarmEventArgs that provides two items of data to event handlers: the read-only Time property, which indicates when the alarm went off; and the Snooze property, which indicates whether the alarm should go off again after a designated interval or whether future alarms should be cancelled.

Public Class AlarmEventArgs : Inherits EventArgs
   Private alarmTime As Date
   Private snoozeOn As Boolean = True

   Public Sub New(time As Date)
      Me.alarmTime = time
   End Sub

   Public ReadOnly Property Time As Date
      Get
         Return Me.alarmTime
      End Get
   End Property

   Public Property Snooze As Boolean
      Get
         Return Me.snoozeOn
      End Get
      Set
         Me.snoozeOn = value
      End Set   
   End Property   
End Class
public class AlarmEventArgs : EventArgs
{
   private DateTime alarmTime;
   private bool snoozeOn = true;

   public AlarmEventArgs(DateTime time)
   {
      this.alarmTime = time;
   }

   public DateTime Time
   {
      get { return this.alarmTime; }
   }

   public bool Snooze
   {
      get { return this.snoozeOn; }
      set { this.snoozeOn = value; }
   }   
}
public ref class AlarmEventArgs : public EventArgs
{
private: 
   System::DateTime^ alarmTime;
   bool snoozeOn;

public:
   AlarmEventArgs(System::DateTime^ time) 
   {
      this->alarmTime = time;
      this->snoozeOn = true;
   }

   property DateTime^ Time 
   {
      System::DateTime^ get()
      { return this->alarmTime; }
   }

   property bool Snooze
   {
      bool get()
      { return this->snoozeOn; }
      void set(bool snooze)
      { this->snoozeOn = snooze; }
   }
};

Defining a Delegate for the Event

An event delegate is used to define the signature of the event. A particular event delegate typically corresponds to a particular event data class. By convention, events in the .NET Framework have the signature EventName(sender, e), where sender is an Object that provides a reference to the class or structure that fired the event, and e is an EventArgs object or an object derived from EventArgs that provides event data. The delegate definition then typically takes the form EventNameHandler(sender, e).

If you are using an event data class that is already defined in the .NET Framework class library or in a third-party library, it is likely that a corresponding event delegate is also defined in that library. For example, the EventHandler delegate can be used along with the EventArgs class. Similarly, the CancelEventHandler delegate can be used along with the CancelEventArgs class.

If you define a custom event data class, you can also define a custom delegate to define the event signature, or you can use the generic Action<T1, T2> delegate.

The following example defines an event delegate named AlarmEventHandler.

Public Delegate Sub AlarmEventHandler(sender As Object, e As AlarmEventArgs)
public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);
public delegate void AlarmEventHandler(System::Object^ sender, AlarmEventArgs^ e);

Defining a Class to Raise the Event

The class that raises the event must provide the event declaration and define a method that raises the event. In addition, it must provide some logic to raise the event in a class property or method.

You define an event member in your class using the event keyword in C# or the Event statement in Visual Basic. When the compiler encounters an event declaration in your class, it creates a private member such as:

private EventNameHandler eh = null;

The compiler also creates the two public methods, add_EventName and remove_EventName. These methods are event hooks that allow delegates to be combined or removed from the event delegate eh. The details are hidden from the programmer.

Note

In languages other than C# and Visual Basic 2005, the compiler might not automatically generate the code corresponding to an event member, and you might have to explicitly define the event hooks and the private delegate field.

The following example declares an event named AlarmEvent. It is excerpted from the example for a class named Alarm whose complete source code is shown below. Note that it has the signature of the AlarmEventHandler delegate.

Event AlarmEvent As AlarmEventHandler
public event AlarmEventHandler AlarmEvent;
public:
   event AlarmEventHandler^ AlarmEvent; 

Once you have defined your event implementation, you must determine when to raise the event. You raise the event by calling the protected OnEventName method in the class that defined the event, or in a derived class. The OnEventNamemethod then raises the event.

Note

The protected OnEventNamemethod also allows derived classes to override the event without attaching a delegate to it. A derived class must always call the OnEventNamemethod of the base class to ensure that registered delegates receive the event.

The following example defines the OnAlarmEvent method, which is responsible for raising the AlarmEvent event.

Protected Sub OnAlarmEvent(e As AlarmEventArgs)
   RaiseEvent AlarmEvent(Me, e)
End Sub  
protected void OnAlarmEvent(AlarmEventArgs e)
{
   AlarmEvent(this, e);
}  
protected:
   void OnAlarmEvent(AlarmEventArgs^ e)
   {
      AlarmEvent(this, e);
   }

The following example defines a method named Set that contains the logic to fire the event by calling the OnAlarmEvent method. If the hours and the minutes of the alarm time equal the hours and minutes of the current time, the Set method instantiates an AlarmEventArgs object and provides it with the time that the alarm went off. After the event handlers execute, it checks the value of the Snooze property. If Snooze is false, no more alarm events are to be raised, so the Set method can end. If Snooze is true, the time the alarm is to go off is incremented by the value of the Interval property.

Public Sub [Set]()
   Do
      System.Threading.Thread.Sleep(2000)
      Dim currentTime As DateTime = Date.Now
      ' Test whether it is time for the alarm to go off.
      If currentTime.Hour = alarmTime.Hour And _
         currentTime.Minute = AlarmTime.Minute Then
         Dim args As New AlarmEventArgs(currentTime)
         OnAlarmEvent(args)
         If args.Snooze = False Then 
            Exit Sub
         Else
            Me.alarmTime = Me.alarmTime.AddMinutes(Me.interval)
         End If      
      End If          
   Loop
End Sub 
public void Set()
{
   while (true) {
      System.Threading.Thread.Sleep(2000);
      DateTime currentTime = DateTime.Now;
      // Test whether it is time for the alarm to go off.
      if (currentTime.Hour == alarmTime.Hour && 
          currentTime.Minute == alarmTime.Minute)
      {    
         AlarmEventArgs args = new AlarmEventArgs(currentTime);
         OnAlarmEvent(args);
         if (! args.Snooze) 
            return;
         else
            this.alarmTime = this.alarmTime.AddMinutes(this.interval);
      }
   }
} 
void Set()
{
   do {
      Thread::Sleep(2000);
      System::DateTime^ currentTime = DateTime::Now;
      // Test whether it's time for the alarm to go off.
      if (currentTime->Hour == alarmTime->Hour && currentTime->Minute == alarmTime->Minute)
      {
         AlarmEventArgs^ args = gcnew AlarmEventArgs(currentTime);
         OnAlarmEvent(args);
         if (args->Snooze == false)
            return;
         else
            this->alarmTime = this->alarmTime->AddMinutes(this->interval);
      }
   } while (true);
}

The following example includes all of the source code for the Alarm class.

Public Class Alarm
   Private alarmTime As Date
   Private interval As Integer = 10

   Event AlarmEvent As AlarmEventHandler

   Public Sub New(time As Date)
      Me.New(time, 10)
   End Sub

   Public Sub New(time As Date, interval As Integer)
      Me.alarmTime = time
      Me.interval = interval
   End Sub

   Public Sub [Set]()
      Do
         System.Threading.Thread.Sleep(2000)
         Dim currentTime As DateTime = Date.Now
         ' Test whether it is time for the alarm to go off.
         If currentTime.Hour = alarmTime.Hour And _
            currentTime.Minute = AlarmTime.Minute Then
            Dim args As New AlarmEventArgs(currentTime)
            OnAlarmEvent(args)
            If args.Snooze = False Then 
               Exit Sub
            Else
               Me.alarmTime = Me.alarmTime.AddMinutes(Me.interval)
            End If      
         End If          
      Loop
   End Sub 

   Protected Sub OnAlarmEvent(e As AlarmEventArgs)
      RaiseEvent AlarmEvent(Me, e)
   End Sub  
End Class
public class Alarm
{
   private DateTime alarmTime;
   private int interval = 10;

   public event AlarmEventHandler AlarmEvent;

   public Alarm(DateTime time) : this(time, 10)
   {
   }

   public Alarm(DateTime time, int interval)
   {
      this.alarmTime = time;
      this.interval = interval;
   }

   public void Set()
   {
      while (true) {
         System.Threading.Thread.Sleep(2000);
         DateTime currentTime = DateTime.Now;
         // Test whether it is time for the alarm to go off.
         if (currentTime.Hour == alarmTime.Hour && 
             currentTime.Minute == alarmTime.Minute)
         {    
            AlarmEventArgs args = new AlarmEventArgs(currentTime);
            OnAlarmEvent(args);
            if (! args.Snooze) 
               return;
            else
               this.alarmTime = this.alarmTime.AddMinutes(this.interval);
         }
      }
   } 

   protected void OnAlarmEvent(AlarmEventArgs e)
   {
      AlarmEvent(this, e);
   }  
}
public ref class Alarm 
{
private:
   System::DateTime^ alarmTime;
   int interval;

public:
   event AlarmEventHandler^ AlarmEvent; 
   Alarm(System::DateTime^ time) : alarmTime(time), interval(10) { };
   Alarm(System::DateTime^ time, int interval) : alarmTime(time), interval(interval) {};

   void Set()
   {
      do {
         Thread::Sleep(2000);
         System::DateTime^ currentTime = DateTime::Now;
         // Test whether it's time for the alarm to go off.
         if (currentTime->Hour == alarmTime->Hour && currentTime->Minute == alarmTime->Minute)
         {
            AlarmEventArgs^ args = gcnew AlarmEventArgs(currentTime);
            OnAlarmEvent(args);
            if (args->Snooze == false)
               return;
            else
               this->alarmTime = this->alarmTime->AddMinutes(this->interval);
         }
      } while (true);
   }

protected:
   void OnAlarmEvent(AlarmEventArgs^ e)
   {
      AlarmEvent(this, e);
   }
};

See Also

Tasks

How to: Raise and Consume Events

How to: Implement Events in Your Class

Concepts

Events and Delegates

Other Resources

Handling and Raising Events