Есть ли способ отложить обработчик события (скажем, в течение 1 секунды) в Windows Forms
Мне нужно уметь задерживать обработчики событий для некоторых элементов управления (например, кнопки), которые будут запущены, например, через 1 секунду фактического события (например, событие click).. возможно ли это с помощью .net framework?
Я использую таймер и вызываю свой код из события таймера, как показано ниже, но я не уверен, что это лучший подход!
void onButtonClick( ..)
{
timer1.Enabled = true;
}
void onTimerTick( ..)
{
timer.Enabled = false;
CallMyCodeNow();
}
Ответы
Ответ 1
Возможно, вы могли бы создать метод, который создает таймер?
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;
}
Ответ 2
Прежде чем перейти к вашему вопросу, просто прочитав сводный бит с главной страницы вопросов, таймер был именно тем, что я собирался предложить.
Это выглядит довольно чистым для меня. Это означает, что вы можете легко "отменить" задержанное событие, если вам нужно, например, отключив таймер. Он также делает все в пределах потока пользовательского интерфейса (но без повторной установки), что делает жизнь немного проще, чем другие альтернативы.
Ответ 3
Если вы делаете это только для одного элемента управления, подход таймера будет работать нормально. Более надежный подход, поддерживающий несколько элементов управления и типов событий, выглядит примерно так:
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;
}
}
Ответ 4
В моем решении используется 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;
}
}
Ответ 5
Для тех, кто ограничен .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();
}
Ответ 6
Использование реактивных расширений:
Сначала установите пакет 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)
);
}
Ответ 7
Если вы ищете более привлекательное решение, вы можете взглянуть на мой Reactive проект LINQ. Ссылка не показывает, как решить конкретную проблему, которую вы имеете, но ее можно было бы решить в довольно элегантном стиле, используя описанную там технику (во всей серии из 4 статей).
Ответ 8
Вы можете использовать:
Thread.Sleep(1000);
Это остановит текущий поток на одну секунду. Поэтому я бы сделал это...
С наилучшими пожеланиями!