2009-03-15 8 views
13

Potrzebuję być w stanie opóźnić obsługę zdarzeń dla niektórych elementów sterujących (takich jak przycisk), które mają zostać uruchomione, na przykład po upływie 1 sekundy faktycznego stanu zdarzenie (na przykład zdarzenie click) ... czy jest to możliwe w ramach .net?Czy istnieje sposób na opóźnienie obsługi zdarzeń (na przykład przez 1 sekundę) w systemie Windows Forms

Używam czasomierza i wywołuję mój kod z zdarzenia znacznika czasu, jak poniżej, ale nie jestem pewien, czy to jest najlepsze podejście!

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

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

    CallMyCodeNow(); 
} 
+2

Jest to prawdopodobnie najlepszy sposób na zrobienie tego. – configurator

Odpowiedz

5

Zanim przyszedłem na twoje pytanie, po przeczytaniu podsumowania z głównej strony z pytaniami, zegar był dokładnie tym, co zamierzałem zasugerować.

To dla mnie wygląda całkiem nieźle. Oznacza to, że możesz łatwo "anulować" opóźnione zdarzenie, na przykład, wyłączając ponownie timer. Robi to również wszystko w obrębie wątku UI (ale bez reentrancy), co sprawia, że ​​życie jest trochę prostsze niż inne alternatywy.

-3

Można użyć:

Thread.Sleep(1000); 

To będzie wstrzymać bieżącego wątku na jedną sekundę. Więc zrobiłbym to ...

Pozdrawiam!

+1

To spowodowałoby zawieszenie wątku interfejsu użytkownika, co nigdy nie jest dobre. Korzystanie z licznika czasu pozwoli na przetworzenie pompy komunikatów. –

15

Być może mógłbyś stworzyć metodę, która tworzy zegar?

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

Naprawiono możliwą literówkę. Pleas wycofuje się, jeśli popełniłem błąd. – strager

+0

Wycofane, nie można użyć tej samej nazwy dla parametru lambda. – Bengt

+0

Ah, racja. Przepraszam. xD – strager

5

Jeśli robisz to tylko dla jednego elementu sterującego, metoda licznika czasu będzie działać poprawnie. Bardziej zdecydowane podejście obsługi wielu kontroli i rodzaje zdarzeń wygląda mniej więcej tak:

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

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

utrzymać Queue<Event> w swojej formie i mieć zdarzenia UI, które muszą być opóźnione dodać je do kolejki, np:

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

Nakręć Tick timerów 10 razy na sekundę lub tak, i jego obsługi zdarzeń Tick wyglądać następująco:

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

Myślę, że użycie 'MinHeap' lub' SortedList' będzie lepsze niż użycie kolejki, ponieważ czasy opóźnień zdarzeń niekoniecznie muszą być takie same. W przeciwnym razie użycie zwykłej "Listy" i iteracja wszystkich zdarzeń w niej zawartych będzie następną opcją. –

0

Jeśli szukasz bardziej wyszukane rozwiązanie, możesz rzucić okiem na mój Reactive LINQ project. Łącze nie pokazuje, jak rozwiązać dany problem, ale powinno być możliwe rozwiązanie w dość eleganckim stylu przy użyciu techniki tam opisanej (w całej serii 4-artykułów).

4

Moje rozwiązanie wykorzystuje 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

Dla tych, ogranicza się do .NET 2.0, tu jest inny wziąć na Bengt pomocne rozwiązanie:

/// <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

zastosowanie reaktywnych rozszerzeń:

Najpierw zainstaluj pakiet nuget

PM> Install-Package Rx-Main 

Kod:

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) 
      ); 
    } 
Powiązane problemy