简体   繁体   English

有没有一种方法可以延迟Windows窗体中的事件处理程序(例如1秒钟)

[英]Is there a way to delay an event handler (say for 1 sec) in Windows Forms

I need to be able to delay the event handlers for some controls (like a button) to be fired for example after 1 sec of the actual event (click event for example) .. is this possible by the .net framework ? 我需要能够延迟某些控件(例如按钮)的事件处理程序,例如在实际事件(例如click事件)的1秒后触发。.net框架可以吗?

I use a timer and call my code from the timer's tick event as below but I am not sure if this is the best approach ! 我使用计时器,并从计时器的tick事件中调用我的代码,如下所示,但我不确定这是否是最佳方法!

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

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

   CallMyCodeNow();
}

Perhaps you could make a method that creates the 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;
}

Before coming to your question, just having read the summary bit from the main questions page, a timer was exactly what I was going to suggest. 在提出您的问题之前,只要阅读了主要问题页面的摘要部分,计时器就是我所建议的。

This looks pretty clean to me. 这对我来说看起来很干净。 It means you can easily "cancel" the delayed event if you need to, by disabling the timer again, for example. 这意味着您可以根据需要轻松地“取消”延迟的事件,例如,再次禁用计时器。 It also does everything within the UI thread (but without reentrancy), which makes life a bit simpler than other alternatives might be. 它还可以执行UI线程内的所有操作(但无需重新输入),这使生活比其他替代方案要简单一些。

If you're only doing this for one control, the timer approach will work fine. 如果仅对一个控件执行此操作,则计时器方法会正常工作。 A more robust approach supporting multiple controls and types of events looks something like this: 支持多种控件和事件类型的更强大的方法如下所示:

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

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

Maintain a Queue<Event> in your form and have UI events that need to be delayed add them to the queue, eg: 在表单中维护Queue<Event>并让需要延迟的UI事件将它们添加到队列中,例如:

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

Make your timer tick 10 times a second or so, and have its Tick event handler look like this: 使您的计时器每秒滴答10次,并使它的Tick事件处理程序如下所示:

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

My solution uses System.Threading.Timer: 我的解决方案使用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;
    }
}

For those limited to .NET 2.0, here is another take on Bengt's helpful solution: 对于仅限于.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();
}

Using Reactive Extensions: 使用反应性扩展:

First, install the nuget package 首先,安装nuget软件包

PM> Install-Package Rx-Main

Code: 码:

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

If you're looking for a more fancy solution, you may want to take a look at my Reactive LINQ project . 如果您正在寻找更高级的解决方案,则可能需要看一下我的Reactive LINQ项目 The link doesn't show how to solve the particular problem you're having, but it should be possible to solve in quite an elegant style using the technique described there (in the whole 4-article series). 该链接没有显示如何解决您遇到的特定问题,但是应该可以使用此处描述的技术(在整个第4条文章中)以一种非常优雅的风格来解决。

You can use: 您可以使用:

Thread.Sleep(1000);

That will pause the current Thread for one second. 这会将当前线程暂停一秒钟。 So I would do that... 所以我会做...

Best Regards! 最好的祝福!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM