24

Is there a simple solution/idea/strategy to create a setTimeout equivalent function in a WinForms app. I'm primarily a web developer but am not sure how I'd go about this in a WinForms App. Basically, I have a textbox, and after each keystroke I want to run a task to populate a list (like an auto-complete type thingy) but want to be able to cancel (e.g. clearTimeout) if the user keeps entering characters...

My only guess is to perhaps use a BackGroundWorker and make it sleep initially, and while it is sleeping, it could be cancelled, if the user stops entering keys and the sleep period ends, it then goes and runs the task etc

(i don't care if an example is C# or Vb.Net)

davidsleeps
  • 9,393
  • 11
  • 59
  • 73
  • [Javascript's SetTimeout, SetInterval and ClearInterval equivalent in c#](https://stackoverflow.com/questions/40502596/javascripts-settimeout-setinterval-and-clearinterval-equivalent-in-c-sharp/40609390#40609390) – Koray Aug 03 '17 at 12:40

10 Answers10

58

I know this is an old question but an alternative solution would be to use Task.Delay(delay).ContinueWith((task) => { /* Code */ });.

Thread.Sleep vs Task.Delay?

or there is 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

Community
  • 1
  • 1
Robert
  • 3,328
  • 2
  • 24
  • 25
17

You can use a System.Timers.Timer: set AutoReset to false and use Start/Stop methods and create a handler for the Elapsed event.

Here's an example implementation in 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 
DarkTrick
  • 2,447
  • 1
  • 21
  • 39
Serguei
  • 2,910
  • 3
  • 24
  • 34
10
    public void setTimeout(Action TheAction, int Timeout)
    {
        Thread t = new Thread(
            () =>
            {
                Thread.Sleep(Timeout);
                TheAction.Invoke();
            }
        );
        t.Start();
    }
Rob Hardy
  • 133
  • 1
  • 2
  • 11
    I wouldn't recommend using this method. Creating a whole thread just for a single operation? Overkill. Use the Timer class provided in the other method for something more production-worthy. According to this answer, 1 MB of memory is allocated for each new thread. http://stackoverflow.com/a/2744464/881111 -- Remember, the OP said that he was going to run this after each keystroke... so consider 1 MB of memory AND a new thread for each keystroke. Bad idea. – marknuzz May 29 '14 at 05:09
  • Not only a bad idea. This probably has concurrency issues – Sarsaparilla Feb 05 '17 at 23:18
10

Timer Implementation:

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();
}
KodFun
  • 323
  • 3
  • 8
7

I can propose following

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();
Oleg
  • 71
  • 1
  • 2
2

You can use also:

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

Where Delay.Do is:

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

Note: When updating a control in the UI thread use Control.Invoke:

Delay.Do(2000, () => { lblOk.Invoke((MethodInvoker)(() => { lblOk.Visible = false; })); });
Amen Ayach
  • 4,288
  • 1
  • 23
  • 23
1

This is my way, use C# 7.0 syntax feature. Some differ with js, when timeout action execute then will can't be clear.

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();
    }
}
IlPADlI
  • 1,943
  • 18
  • 22
1

when using Task.Delay() and your action to edit/set winforms control. you have to add TaskScheduler.FromCurrentSynchronizationContext() or will get error Cross thread operation

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

SetTimeout(() => {
    myButton.Enabled = true;
}, 3000);  
uingtea
  • 6,002
  • 2
  • 26
  • 40
0
    public void setTimeout(Action act, int timeout)
    {
        Action action = () =>
        {
            Thread.Sleep(Timeout);
            act();
        };

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

I'd recommend using reactive programming for this. See https://github.com/Reactive-Extensions/Rx.NET for the Reactive Extensions for .NET and http://reactivex.io/ for the general information about Reactive programming.

I'm afraid I'm only familiar with the JavaScript reactive library, so I can't give you a C-Sharp example, but in JavaScript it'd work something like this:

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

Using a setup like this you can chain operators to map and merge all kinds of events from a source to a subscriber. The simple example above reacts to an event, a keyUp for instance, and waits until there is no new keyUp for 300 ms and then calls the eventHandler, but only if the new value (after 300ms) is different from the last emitted value.

Robba
  • 7,684
  • 12
  • 48
  • 76