83

I have a Windows Forms application written in C#. The following function checks whenever printer is online or not:

public void isonline()
{
    PrinterSettings settings = new PrinterSettings();
    if (CheckPrinter(settings.PrinterName) == "offline")
    {
        pictureBox1.Image = pictureBox1.ErrorImage;
    }
}

and updates the image if the printer is offline. Now, how can I execute this function isonline() every 2 seconds so when I unplug the printer, the image displayed on the form (pictureBox1) turns into another one without relaunching the application or doing a manual check? (eg. by pressing "Refresh" button which runs the isonline() function)

FOX 9000
  • 123
  • 1
  • 1
  • 6
  • 6
    polling isn't a great idea. Better to listen for a state change notification if one exists. – David Heffernan May 29 '11 at 17:50
  • 2
    possible duplicate of [Execute an operation every x seconds for y minutes in c#](http://stackoverflow.com/questions/786672/execute-an-operation-every-x-seconds-for-y-minutes-in-c) – H H May 29 '11 at 17:50

7 Answers7

136

Use System.Windows.Forms.Timer.

private Timer timer1; 
public void InitTimer()
{
    timer1 = new Timer();
    timer1.Tick += new EventHandler(timer1_Tick);
    timer1.Interval = 2000; // in miliseconds
    timer1.Start();
}

private void timer1_Tick(object sender, EventArgs e)
{
    isonline();
}

You can call InitTimer() in Form1_Load().

IndustProg
  • 627
  • 1
  • 13
  • 33
Stecya
  • 22,896
  • 10
  • 72
  • 102
  • Will this be performed automatically after app start? It works not for me like that. Can you suggest some solution? Thank you – user2886091 Mar 19 '14 at 08:06
  • @user2886091: As long as timer is not disposed or stopped it should work – Stecya Mar 19 '14 at 10:21
  • Is the `new EventHandler` still necessary? – Nic Nov 27 '16 at 00:34
  • the event handler part comes up with the error in the http://prntscr.com/e1sl9t screenshot –  Jan 29 '17 at 13:19
  • 7
    The `Timer` object seems to have different configuration nowadays. I made it work using: `timer = new Timer(TimerCallback, null, 0, timeStepInMilliseconds)` + `private static void TimerCallback(Object stateInfo)`. – Xavier Peña Sep 04 '17 at 12:13
  • This works without affecting the UI Thread unlike System.Threading.Thread.Sleep(). Can anyone compare what the performance and resource utilization of this? – Neo Dec 02 '17 at 05:54
21

.NET 6 has added the PeriodicTimer class.

var periodicTimer= new PeriodicTimer(TimeSpan.FromSeconds(1));
while (await periodicTimer.WaitForNextTickAsync())
{
    // Place function in here..
    Console.WriteLine("Printing");
}

You can run it in the background with this:

async Task RunInBackground(TimeSpan timeSpan, Action action)
{
    var periodicTimer = new PeriodicTimer(timeSpan);
    while (await periodicTimer.WaitForNextTickAsync())
    {
        action();
    }
}

RunInBackground(TimeSpan.FromSeconds(1), () => Console.WriteLine("Printing"));

The main advantage PeriodicTimer has over a Timer.Delay loop is best observed when executing a slow task.

using System.Diagnostics;

var stopwatch = Stopwatch.StartNew();
// Uncomment to run this section
//while (true)
//{
//    await Task.Delay(1000);
//    Console.WriteLine($"Delay Time: {stopwatch.ElapsedMilliseconds}");
//    await SomeLongTask();
//}

//Delay Time: 1007
//Delay Time: 2535
//Delay Time: 4062
//Delay Time: 5584
//Delay Time: 7104

var periodicTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(1000));
while (await periodicTimer.WaitForNextTickAsync())
{
    Console.WriteLine($"Periodic Time: {stopwatch.ElapsedMilliseconds}");
    await SomeLongTask();
}

//Periodic Time: 1016
//Periodic Time: 2027
//Periodic Time: 3002
//Periodic Time: 4009
//Periodic Time: 5018

async Task SomeLongTask()
{
    await Task.Delay(500);
}

PeriodicTimer will attempt to invoke every n * delay seconds, whereas Timer.Delay will invoke every n * (delay + time for method to run) seconds, causing execution time to gradually move out of sync.

DaemonFire
  • 573
  • 6
  • 13
  • 1
    how is this different from having infinite loop with Task.Delay inside? – Adassko Jan 04 '22 at 16:45
  • 1
    apparently [Periodic Timer](https://github.com/dotnet/runtime/issues/31525) is more efficient than Task.Delay as it doesn't have repeat task or timer allocations [source](https://github.com/dotnet/runtime/issues/31525#issuecomment-787539443). it could be easier to read/understand and might standardize timer loops. – DaemonFire Jan 05 '22 at 00:50
11

The most beginner-friendly solution is:

Drag a Timer from the Toolbox, give it a Name, set your desired Interval, and set "Enabled" to True. Then double-click the Timer and Visual Studio (or whatever you are using) will write the following code for you:

private void wait_Tick(object sender, EventArgs e)
{
    refreshText(); // Add the method you want to call here.
}

No need to worry about pasting it into the wrong code block or something like that.

Lauren Rutledge
  • 1,195
  • 5
  • 18
  • 27
philx_x
  • 1,708
  • 16
  • 23
6

Threaded:

    /// <summary>
    /// Usage: var timer = SetIntervalThread(DoThis, 1000);
    /// UI Usage: BeginInvoke((Action)(() =>{ SetIntervalThread(DoThis, 1000); }));
    /// </summary>
    /// <returns>Returns a timer object which can be disposed.</returns>
    public static System.Threading.Timer SetIntervalThread(Action Act, int Interval)
    {
        TimerStateManager state = new TimerStateManager();
        System.Threading.Timer tmr = new System.Threading.Timer(new TimerCallback(_ => Act()), state, Interval, Interval);
        state.TimerObject = tmr;
        return tmr;
    }

Regular

    /// <summary>
    /// Usage: var timer = SetInterval(DoThis, 1000);
    /// UI Usage: BeginInvoke((Action)(() =>{ SetInterval(DoThis, 1000); }));
    /// </summary>
    /// <returns>Returns a timer object which can be stopped and disposed.</returns>
    public static System.Timers.Timer SetInterval(Action Act, int Interval)
    {
        System.Timers.Timer tmr = new System.Timers.Timer();
        tmr.Elapsed += (sender, args) => Act();
        tmr.AutoReset = true;
        tmr.Interval = Interval;
        tmr.Start();

        return tmr;
    }
paul-2011
  • 675
  • 10
  • 27
6

Things have changed a lot with time. You can use the solution below:

static void Main(string[] args)
{
    var timer = new Timer(Callback, null, 0, 2000);

    //Dispose the timer
    timer.Dispose();
}
static void Callback(object? state)
{
    //Your code here.
}
2

You can do this easily by adding a Timer to your form (from the designer) and setting it's Tick-function to run your isonline-function.

Alxandr
  • 12,345
  • 10
  • 59
  • 95
2
using System;
using System.Timers;
namespace SnirElgabsi
{
  class Program
  {
     private static Timer timer1;
     static void Main(string[] args)
     {
         timer1 = new Timer(); //new Timer(1000);
         timer1.Elpased += (sender,e) =>
         {
            MyFoo();
         }
         timer1.Interval = 1000;//miliseconds
         timer1.Start();
       
         Console.WriteLine("press any key to stop");
         Console.ReadKey();
     }

     private static void MyFoo()
     {
         Console.WriteLine(string.Format("{0}", DateTime.Now));
     }
  }
}
snir
  • 2,961
  • 1
  • 17
  • 10