Provocar un evento

Si desea que su clase genere un evento, debe proporcionar los tres elementos siguientes:

  • Una clase que proporciona datos de eventos.

  • Un delegado de eventos.

  • La clase que genera el evento.

Definir una clase para proporcionar datos de eventos

Por convención, en .NET Framework, cuando se genera un evento, pasa los datos de eventos a sus controladores de eventos. Los datos de eventos los proporciona la clase System.EventArgs o una clase que se deriva de ella.

A menudo, un evento no tiene datos personalizados; el hecho de que se desencadenara el evento proporciona toda la información que los controladores de eventos necesitan. En este caso, el evento puede pasar un objeto EventArgs a sus controladores. La clase EventArgs solo tiene un único miembro, Empty, que no se hereda de System.Object. Se puede usar para crear instancias de una nueva clase EventArgs.

Si un evento tiene datos personalizados, puede pasar a los controladores de eventos una instancia de una clase derivada de EventArgs. Dependiendo de los datos precisos que el evento pase a los controladores, quizás pueda usar una clase de datos de eventos existente en .NET Framework. Por ejemplo, si su controlador de eventos permite la acción asociada al evento que se va a cancelar, puede usar la clase CancelEventArgs.

Cuando necesite proporcionar datos personalizados a los controladores y no haya disponible una clase existente, puede definir su propia clase de datos de eventos. Se debe derivar de System.EventArgs. Por convención, esta clase se denomina EventNameEventArgs. En el ejemplo siguiente se muestra ese tipo de clase de datos de eventos personalizada. Define una clase denominada AlarmEventArgs que proporciona dos elementos de datos a los controladores de eventos: la propiedad Time de solo lectura, que indica cuándo se desencadenó la alarma, y la propiedad Snooze, que indica si la alarma se debe activar de nuevo después de un intervalo designado o si se deben cancelar las alarmas futuras.

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; }
   }
};

Definir un delegado para el evento

Un delegado de eventos se usa para definir la firma del evento. Un delegado de eventos determinado corresponde normalmente a una clase de datos de eventos concreta. Por convención, los eventos de .NET Framework tienen la firma EventName(sender, e), donde sender es un Object que proporciona una referencia a la clase o estructura que desencadenó el evento, y e es un objeto EventArgs o un objeto derivado de EventArgs que proporciona datos de eventos. La definición de delegado suele tomar el formato EventNameHandler(sender, e).

Si está usando una clase de datos de eventos que ya está definida en la biblioteca de clases de .NET Framework o en una biblioteca de otro fabricante, es probable que en esa biblioteca también esté definido un delegado de eventos correspondiente. Por ejemplo, el delegado de EventHandler se puede usar junto con la clase EventArgs. Del mismo modo, el delegado de CancelEventHandler se puede usar junto con la clase CancelEventArgs.

Si define una clase de datos de eventos personalizada, también puede definir un delegado personalizado para definir la firma del evento o puede usar el delegado Action<T1, T2> genérico.

En el ejemplo siguiente se define un delegado de eventos denominado 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);

Definir una clase para generar el evento

La clase que genera el evento debe proporcionar la declaración de evento y definir un método que genera el evento. Además, debe proporcionar cierta lógica para generar el evento en una propiedad o en un método de clase.

Para definir un miembro de evento en su clase se emplea la palabra clave event en C# o la instrucción Event en Visual Basic. Cuando el compilador encuentra una declaración de evento en la clase, crea un miembro privado como:

private EventNameHandler eh = null;

El compilador también crea los dos métodos públicos add_EventName y remove_EventName. Estos métodos son enlaces de eventos que permiten combinar o quitar delegados del delegado de evento eh. El programador oculta los detalles.

NotaNota

En lenguajes que no sean C# y Visual Basic 2005, puede que el compilador no genere automáticamente el código correspondiente a un miembro de evento, y que se tengan que definir explícitamente los enlaces de eventos y el campo del delegado privado.

En el ejemplo siguiente se declara un evento denominado AlarmEvent. Se ha extraído del ejemplo para una clase denominada Alarm cuyo código fuente completo se muestra debajo. Observe que tiene la firma del delegado AlarmEventHandler.

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

Cuando haya definido su implementación de evento, deberá determinar cuándo provocar el evento. Para generar el evento se llama al método protegido OnEventName en la clase que definía el evento o en una clase derivada. A continuación, el método OnEventName genera el evento.

NotaNota

El método protegido OnEventName también permite que las clases derivadas reemplacen el evento sin adjuntarle un delegado.Una clase derivada siempre debe llamar al método OnEventName de la clase base para asegurarse de que los delegados registrados reciben el evento.

En el ejemplo siguiente se define el método OnAlarmEvent, que es responsable de generar el evento AlarmEvent.

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);
   }

En el ejemplo siguiente se define un método denominado Set que contiene la lógica para desencadenar el evento llamando al método OnAlarmEvent. Si las horas y los minutos de la hora de la alarma son iguales que las horas y los minutos de la hora actual, el método Set crea una instancia de un objeto AlarmEventArgs y le proporciona la hora a la que sonó la alarma. Una vez que se ejecutan los controladores de eventos, comprueba el valor de la propiedad Snooze. Si Snooze es false, no se va a generar ningún evento de alarma más, por lo que el método Set puede finalizar. Si Snooze es true, el valor de la propiedad Interval incrementa la hora a la que va a sonar la alarma.

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);
}

En el ejemplo siguiente se incluye todo el código fuente para la clase Alarm.

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);
   }
};

Vea también

Tareas

Cómo: Provocar y utilizar eventos

Cómo: Implementar eventos en una clase

Conceptos

Eventos y delegados

Otros recursos

Controlar y provocar eventos