Есть ли способ отложить обработчик событий (скажем, на 1 секунду) в Windows Forms - PullRequest
13 голосов
/ 16 марта 2009

Мне нужно иметь возможность задерживать обработчики событий для некоторых элементов управления (например, кнопки), например, после 1 секунды фактического события (например, событие щелчка). Возможно ли это .NET Framework?

Я использую таймер и вызываю свой код из события отметки времени, как показано ниже, но я не уверен, что это лучший подход!

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

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

   CallMyCodeNow();
}

Ответы [ 8 ]

15 голосов
/ 16 марта 2009

Возможно, вы могли бы создать метод, который создает таймер?

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;
}
5 голосов
/ 16 марта 2009

Если вы делаете это только для одного элемента управления, подход по таймеру будет работать нормально. Более надежный подход с поддержкой нескольких элементов управления и типов событий выглядит примерно так:

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

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

Сохраняйте Queue<Event> в вашей форме, и события пользовательского интерфейса, которые необходимо отложить, добавляют их в очередь, например ::

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

Сделайте ваш таймер 10 раз в секунду или около того, и обработчик события Tick будет выглядеть так:

void onTimerTick()
{
   if (EventQueue.Any() && EventQueue.First().StartTime >= DateTime.Now)
   {
      Event e = EventQueue.Dequeue();
      e.Method;
   }
}
5 голосов
/ 16 марта 2009

Прежде чем перейти к вашему вопросу, просто прочитав краткую сводку со страницы основных вопросов, таймер был именно тем, что я собирался предложить.

Это выглядит довольно чисто для меня. Это означает, что вы можете легко «отменить» отложенное событие, если вам нужно, например, снова отключив таймер. Он также делает все в потоке пользовательского интерфейса (но без повторного входа), что делает жизнь немного проще, чем другие альтернативы.

4 голосов
/ 18 марта 2010

Мое решение использует 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;
    }
}
3 голосов
/ 31 марта 2010

Для тех, кто ограничен .NET 2.0, вот еще один вариант полезного решения 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 голос
/ 12 ноября 2014

Использование реактивных расширений:

Сначала установите пакет nuget

PM> Install-Package Rx-Main

Код:

    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)
            );
    }
0 голосов
/ 16 марта 2009

Если вы ищете более причудливое решение, вы можете взглянуть на мой проект Reactive LINQ . Ссылка не показывает, как решить конкретную проблему, которая у вас есть, но она должна быть решена в довольно элегантном стиле с использованием описанной там техники (во всей серии из 4 статей).

0 голосов
/ 16 марта 2009

Вы можете использовать:

Thread.Sleep(1000);

Это приостановит текущий поток на одну секунду. Так что я бы сделал это ...

С наилучшими пожеланиями!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...