2009-03-15 16 views
13

Devo essere in grado di ritardare i gestori di eventi per alcuni controlli (come un pulsante) da far scattare per esempio dopo 1 secondo del reale evento (clicca evento per esempio) .. è questo possibile dal framework .net?C'è un modo per ritardare un gestore di eventi (diciamo per 1 secondo) in Windows Form

Io uso un timer e chiamo il mio codice dall'evento del timer del timer come di seguito, ma non sono sicuro che questo sia l'approccio migliore!

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

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

    CallMyCodeNow(); 
} 
+2

Questo è probabilmente il modo giusto per farlo. – configurator

risposta

5

Prima di venire alla tua domanda, solo dopo aver letto il bit di riepilogo dalla pagina principale domande, un timer era esattamente quello che stavo per suggerire.

Questo mi sembra abbastanza pulito. Significa che puoi facilmente "annullare" l'evento in ritardo, se necessario, disattivando nuovamente il timer, ad esempio. Inoltre fa tutto all'interno del thread UI (ma senza rientranza), il che rende la vita un po 'più semplice rispetto ad altre alternative.

-3

È possibile utilizzare:

Thread.Sleep(1000); 

Che farà una pausa il thread corrente per un secondo. Quindi lo farei ...

I migliori saluti!

+1

Ciò causerebbe il blocco del thread dell'interfaccia utente, il che non è mai una buona cosa. L'utilizzo di un timer consentirà l'elaborazione della pompa dei messaggi. –

15

Forse potresti creare un metodo per creare il timer?

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

Risolto un possibile errore di battitura. I sospiri si annullano se ho fatto un errore. – strager

+0

Rilasciato, non è possibile utilizzare lo stesso nome per il parametro lambda. – Bengt

+0

Ah, giusto. Scusate. xD – strager

5

Se si esegue questa operazione solo per un controllo, l'approccio del timer funzionerà correttamente. Un approccio più robusto supporto più controlli e tipi di eventi simile a questa:

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

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

Mantenere un Queue<Event> nel modulo ed avere eventi dell'interfaccia utente che devono essere ritardata aggiungerli alla coda, per esempio:

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

Fai la tua timer tick 10 volte al secondo o giù di lì, e avere il suo gestore di eventi Tick simile a questa:

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

Penso che usare un 'MinHeap' o un' SortedList' sarà meglio che usare una Queue poiché i tempi di ritardo degli eventi potrebbero non essere necessariamente gli stessi. In caso contrario, l'utilizzo di un normale 'List' e l'iterazione di tutti gli eventi al suo interno costituirà l'opzione successiva. –

0

Se siete alla ricerca di un più fantasia soluzione, si consiglia di dare un'occhiata al mio Reactive LINQ project. Il collegamento non mostra come risolvere il particolare problema che si sta avendo, ma dovrebbe essere possibile risolvere in uno stile abbastanza elegante usando la tecnica descritta qui (in tutta la serie di 4 articoli).

4

mia soluzione utilizza 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

Per coloro limitato a .NET 2.0, ecco un altro assumere soluzione utile di 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

utilizzando le estensioni reattive:

Innanzitutto, installa il pacchetto nuget

PM> Install-Package Rx-Main 

Codice:

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