2009-03-15 10 views
13

Necesito poder retrasar los controladores de eventos para que algunos controles (como un botón) sean disparados, por ejemplo, después de 1 segundo de la real evento (haga clic en evento por ejemplo) ... ¿es esto posible por .net framework?¿Hay alguna manera de retrasar un controlador de eventos (por ejemplo durante 1 segundo) en Windows Forms

Utilizo un temporizador y llamo a mi código desde el evento tick del temporizador, como el siguiente, pero no estoy seguro de si este es el mejor enfoque.

void onButtonClick(..) 
{ 
    timer1.Enabled = true; 
} 

void onTimerTick(..) 
{ 
    timer.Enabled = false; 

    CallMyCodeNow(); 
} 
+2

Esta es probablemente la forma correcta de hacerlo. – configurator

Respuesta

5

Antes de llegar a su pregunta, justo después de haber leído el bit de resumen de la página principal de preguntas, un temporizador era exactamente lo que iba a sugerir.

Esto se ve bastante limpio para mí. Significa que puede "cancelar" fácilmente el evento retrasado si lo necesita, al desactivar el temporizador nuevamente, por ejemplo. También hace todo dentro del hilo de la interfaz de usuario (pero sin reentrada), lo que hace que la vida sea un poco más simple de lo que podrían ser otras alternativas.

-3

Se puede utilizar:

Thread.Sleep(1000); 

Eso hará una pausa en el flujo actual durante un segundo. Así que haría eso ...

¡Atentamente!

+1

Eso haría que el subproceso de la interfaz de usuario se bloquee, lo que nunca es algo realmente bueno. El uso de un temporizador permitirá procesar la bomba de mensajes. –

15

Tal vez podría hacer un método que crea el temporizador?

void onButtonClick(object sender, EventArgs e) 
{ 
    Delay(1000, (o,a) => MessageBox.Show("Test")); 
} 

static void Delay(int ms, EventHandler action) 
{ 
    var tmp = new Timer {Interval = ms}; 
    tmp.Tick += new EventHandler((o, e) => tmp.Enabled = false); 
    tmp.Tick += action; 
    tmp.Enabled = true; 
} 
+0

Se corrigió un posible error tipográfico. Pleas vueltas atrás si cometí un error. – strager

+0

Revertido, no puede usar el mismo nombre para el parámetro lambda. – Bengt

+0

Ah, a la derecha. Lo siento. xD – strager

5

Si solo hace esto para un control, el enfoque del temporizador funcionará bien. Un enfoque más robusto que soporta múltiples controles y tipos de eventos es como la siguiente:

class Event 
{ 
    public DateTime StartTime { get; set; } 
    public Action Method { get; set; } 

    public Event(Action method) 
    { 
     Method = method; 
     StartTime = DateTime.Now + TimeSpan.FromSeconds(1); 
    } 
} 

Mantener un Queue<Event> en su forma y tener eventos de interfaz de usuario que necesitan ser retrasada añadirlos a la cola, por ejemplo:

void onButtonClick(..) 
{ 
    EventQueue.Enqueue(new Event(MethodToCall)); 
} 

Haga su señal de temporizador 10 veces por segundo o así, y tener su controlador de eventos Tick aspecto:

void onTimerTick() 
{ 
    if (EventQueue.Any() && EventQueue.First().StartTime >= DateTime.Now) 
    { 
     Event e = EventQueue.Dequeue(); 
     e.Method; 
    } 
} 
+0

Creo que usar 'MinHeap' o' SortedList' será mejor que usar una cola, ya que los tiempos de demora del evento pueden no ser necesariamente los mismos. De lo contrario, el uso de una 'Lista' regular y la iteración de todos los eventos allí será una próxima opción. –

0

Si usted está buscando un más elegante solución, es posible que desee echar un vistazo a mi Reactive LINQ project. El enlace no muestra cómo resolver el problema particular que está teniendo, pero debería ser posible resolverlo con un estilo bastante elegante utilizando la técnica descrita allí (en la serie completa de 4 artículos).

4

Mi solución utiliza System.Threading.Timer:

public static class ExecuteWithDelay 
{ 
    class TimerState 
    { 
     public Timer Timer; 
    } 

    public static Timer Do(Action action, int dueTime) 
    { 
     var state = new TimerState(); 
     state.Timer = new Timer(o => 
     { 
      action(); 
      lock (o) // The locking should prevent the timer callback from trying to free the timer prior to the Timer field having been set. 
      { 
       ((TimerState)o).Timer.Dispose(); 
      } 
     }, state, dueTime, -1); 
     return state.Timer; 
    } 
} 
2

Para aquellos que están limitados a .NET 2.0, aquí es otra toma en solución útil de Bengt:

/// <summary> 
/// Executes the specified method in a delayed context by utilizing 
/// a temporary timer. 
/// </summary> 
/// <param name="millisecondsToDelay">The milliseconds to delay.</param> 
/// <param name="methodToExecute">The method to execute.</param> 
public static void DelayedExecute(int millisecondsToDelay, MethodInvoker methodToExecute) 
{ 
    Timer timer = new Timer(); 
    timer.Interval = millisecondsToDelay; 
    timer.Tick += delegate 
         { 
          // This will be executed on a single (UI) thread, so lock is not necessary 
          // but multiple ticks may have been queued, so check for enabled. 
          if (timer.Enabled) 
          { 
           timer.Stop(); 

           methodToExecute.Invoke(); 

           timer.Dispose(); 
          } 
         }; 

    timer.Start(); 
} 
1

El uso de extensiones reactivas:

Primero, instale el paquete nuget

PM> Install-Package Rx-Main 

Código:

private void CallMyCodeNow() 
    { 
     label1.Text = "reactivated!"; 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     var o = Observable.FromEventPattern<EventHandler, EventArgs>(
      handler => button1.Click += handler 
      , handler => button1.Click -= handler 
      ) 
      .Delay(TimeSpan.FromSeconds(5)) 
      .ObserveOn(SynchronizationContext.Current) // ensure event fires on UI thread 
      .Subscribe(
       ev => CallMyCodeNow() 
       , ex => MessageBox.Show(ex.Message) 
      ); 
    } 
Cuestiones relacionadas