Winforms 相当于 javascript setTimeout

问题描述 投票:0回答:11

是否有一个简单的解决方案/想法/策略来在 WinForms 应用程序中创建 setTimeout 等效函数。我主要是一名 Web 开发人员,但不确定如何在 WinForms 应用程序中进行此操作。基本上,我有一个文本框,每次击键后我想运行一个任务来填充列表(如自动完成类型的东西),但希望能够取消(例如clearTimeout)如果用户继续输入字符......

我唯一的猜测是也许使用 BackGroundWorker 并使其最初休眠,当它休眠时,它可以被取消,如果用户停止输入密钥并且休眠期结束,它就会继续运行任务等

(我不在乎示例是 C# 还是 Vb.Net)

javascript .net winforms settimeout
11个回答
58
投票

我知道这是一个老问题,但替代解决方案是使用

Task.Delay(delay).ContinueWith((task) => { /* Code */ });

Thread.Sleep 与 Task.Delay?

或者有

await Task.Delay(delay);

https://social.msdn.microsoft.com/Forums/vstudio/en-US/345f0402-3af0-4f96-a501-073674883ba3/building-an-async-settimeout-function?forum=csharpgeneral


17
投票

您可以使用 System.Timers.Timer:将 AutoReset 设置为 false 并使用 Start/Stop 方法并为 Elapsed 事件创建处理程序。

这是

vb.net
中的示例实现:

  Public Sub SetTimeout(act As Action, timeout as Integer)
    Dim aTimer As System.Timers.Timer
    aTimer = New System.Timers.Timer(1)
    ' Hook up the Elapsed event for the timer. 
    AddHandler aTimer.Elapsed, Sub () act
    aTimer.AutoReset = False
    aTimer.Enabled = True
  End Sub 

12
投票

定时器实现:

public void SetTimeout(Action action, int timeout)
{
    var timer = new System.Windows.Forms.Timer();
    timer.Interval = timeout;
    timer.Tick += delegate (object sender, EventArgs args)
    {
        action();
        timer.Stop();
    };
    timer.Start();
}

11
投票
    public void setTimeout(Action TheAction, int Timeout)
    {
        Thread t = new Thread(
            () =>
            {
                Thread.Sleep(Timeout);
                TheAction.Invoke();
            }
        );
        t.Start();
    }

7
投票

我可以提出以下建议

internal class Timeout : System.Timers.Timer
{
    public Timeout (Action action, double delay)
    {
        this.AutoReset = false;
        this.Interval = delay;
        this.Elapsed += (sender, args) => action();
        this.Start();
    }
}
// Example
var timeout = new Timeout(() => {
    Console.WriteLine("init 1");
}, 500);
timeout.Stop();

2
投票

这是我的方式,使用C# 7.0语法功能。 有些和js不一样,超时动作执行的时候会看不清楚。

internal static class JsStyleTimeout
{
    private static readonly ConcurrentDictionary<int, Thread> InnerDic;

    private static int _handle;

    static JsStyleTimeout()
    {
        InnerDic = new ConcurrentDictionary<int, Thread>();
    }

    public static int Set(Action action, int delayMs)
    {
        var handle = Interlocked.Increment(ref _handle);

        var thread = new Thread(new ThreadStart(delegate
        {
            Thread.Sleep(delayMs);
            InnerDic.TryRemove(handle, out var _);
            Task.Factory.StartNew(action);
        }));
        InnerDic.TryAdd(handle, thread);

        thread.Start();
        return handle;
    }

    public static void Clear(int handle)
    {
        if (InnerDic.TryRemove(handle, out var thread))
            thread.Abort();
    }
}

2
投票

您还可以使用:

Delay.Do(3000 /*in ms*/, () => { /* Do somthing */ });

哪里

Delay.Do
是:

using System;
using System.Timers;

public class Delay
{
    public static void Do(int after, Action action)
    {
        if (after <= 0 || action == null) return;

        var timer = new Timer { Interval = after, Enabled = false };

        timer.Elapsed += (sender, e) =>
        {
            timer.Stop();
            action.Invoke();
            timer.Dispose();
            GC.SuppressFinalize(timer);
        };

        timer.Start();
    }
}

注意:在 UI 线程中更新控件时使用

Control.Invoke

Delay.Do(2000, () => { lblOk.Invoke((MethodInvoker)(() => { lblOk.Visible = false; })); });

2
投票

当使用

Task.Delay()
和您的操作来编辑/设置 winforms 控件时。您必须添加
TaskScheduler.FromCurrentSynchronizationContext()
否则会出现错误
Cross thread operation

void SetTimeout(Action action, int ms)
{
    Task.Delay(ms).ContinueWith((task) =>
    {
        action();
    }, TaskScheduler.FromCurrentSynchronizationContext());
}           

SetTimeout(() => {
    myButton.Enabled = true;
}, 3000);  

0
投票
    public void setTimeout(Action act, int timeout)
    {
        Action action = () =>
        {
            Thread.Sleep(Timeout);
            act();
        };

        new Thread(() => Invoke(action)).Start();
    }

0
投票

我建议为此使用反应式编程。请参阅 https://github.com/Reactive-Extensions/Rx.NET 了解 .NET 的响应式扩展,并参阅 http://reactivex.io/ 了解有关响应式编程的一般信息。

恐怕我只熟悉 JavaScript 反应式库,所以我无法给你一个 C-Sharp 示例,但在 JavaScript 中它的工作原理如下:

Rx.Observable.fromEvent(..eventdetails..)
    .debounceTime(300)
    .distinctUntilChanged()
    .subscribe(eventHandler);

使用这样的设置,您可以链接运算符来映射和合并从源到订阅者的各种事件。上面的简单示例对事件(例如 keyUp)做出反应,并等待 300 毫秒内没有新的 keyUp,然后调用 eventHandler,但前提是新值(300 毫秒后)与上次发出的值不同。


0
投票

从处理我们的时间事件的静态类开始是个好主意。我们可以将其命名为Timeman。 然后为每个以延迟开始的操作创建一个包。我们可以将其命名为TimemanEvent。 这是一个使用示例: // 注册一个定时事件 var id = Timeman.SetTimeout(() => { Console.log("代码已执行"); }, 3000);
// 或者 var id = Timeman.SetTimeout(MyMethod, 3000); // 销毁事件 Timeman.ClearTimeout(id);

这是代码源:

/// <summary> Time Manager Utility </summary>
public class Timeman {

    private static readonly ConcurrentDictionary<int, TimemanEvent> InnerDic;

    static Timeman() {
        InnerDic = new ConcurrentDictionary<int, TimemanEvent>();
    }

    /// <summary> Generate Unique Dictionary Id </summary>
    private static int GetUniqueId() {
        // If dict is empty take zero
        if (InnerDic.Keys.Count == 0) return 0;
        // If there is only one, take next
        if (InnerDic.Keys.Count == 1) return InnerDic.Keys.First() + 1;
        // Get all id numbers
        var allKeys = InnerDic.Keys.ToList();
        // Check the missing numbers in order.
        allKeys.Sort();
        var firstNumber = allKeys.First();
        var lastNumber = allKeys.Last();
        var missingNumbers = Enumerable.Range(firstNumber, lastNumber).Except(allKeys);
        // If missing numbers are found, take the first one
        if (missingNumbers.Count() > 0) return missingNumbers.First();
        // Take next
        return lastNumber + 1;
    }

    /// <summary> 
    /// Runs the method after a time(ms) has elapsed.
    /// <code>
    /// var id = Timeman.SetTimeout(() => {
    ///     Console.log("Code was executed");
    /// }, 3000);  
    /// OR
    /// var id = Timeman.SetTimeout(MyMethod, 3000);
    /// </code>
    /// </summary>
    public static int SetTimeout(Action action, int delayMs) {

        var uniqueId = GetUniqueId();
        var te = new TimemanEvent(uniqueId, action, delayMs);
        InnerDic.TryAdd(uniqueId, te);
        te.Start();
        return uniqueId;
    }

    /// <summary> 
    /// Removes the timeout before completion.
    /// <code>
    /// Timeman.ClearTimeout(id);
    /// </code>
    /// </summary>
    public static void ClearTimeout(int id) {
        if (InnerDic.TryRemove(id, out var tEvent))
            tEvent?.Dispose();
    }
}
internal class TimemanEvent {

    private readonly Action action;
    private readonly Timer timer;
    private readonly int id;

    public TimemanEvent(int id, Action action, int delayMs) {
        
        this.id = id;
        this.action = action;
        this.timer = new Timer
        {
            Interval = delayMs
        };
        timer.Tick += OnTimerTick;
    }

    private void OnTimerTick(object sender, EventArgs e) {
        
        action();
        Timeman.ClearTimeout(id);
    }

    /// <summary> Stop timer and unregister events </summary>
    internal void Dispose() {

        timer.Tick -= OnTimerTick;
        Stop();
    }

    internal void Start() => timer.Start();

    internal void Stop() => timer.Stop();
}
© www.soinside.com 2019 - 2024. All rights reserved.