Use System.Threading.Timer to make a countdown timer in C#

[example]

The post Make a countdown timer in C# uses a Timer control to count down the time until some deadline. This example does something similar but it uses a Timer object from the System.Threading namespace.

The basic idea is similar: use the timer to periodically see how long it is until the deadline and then display the time remaining. The first part is straightforward. Unfortunately this kind of timer’s periodic event does not occur in the UI thread so its code cannot directly update the form’s labels. Instead it must call some control’s Invoke method to execute some other method on the UI thread.

The following code executes when the program starts.

// The timer.
System.Threading.Timer TheTimer = null;

// Initialize information about the event.
private DateTime EventDate = new DateTime(2017, 4, 1);

private void Form1_Load(object sender, EventArgs e)
{
    // Make the timer start now and tick every 500 ms.
    TheTimer = new System.Threading.Timer(
        this.Tick, null, 0, 500);
}

This code declares two variables at the class level. First it declares a System.Threading.Timer object. It then creates a DateTime to hold the event date.

The form’s Load event handler creates the Timer object. The constructor’s parameters are:

  • this.Tick – The callback method that the Timer should invoke
  • null – An object to pass into the callback method
  • 0 – The amount of time the Timer should wait before its first call to the callback method
  • 500 – The number of milliseconds between calls to the callback method

The following code shows the Tick callback method.

// The timer ticked.
public void Tick(object info)
{
    this.Invoke((Action)this.UpdateCountdown);
}

In the earlier version of this program, the Timer control’s Tick event handler updates the program’s labels. As I mentioned above, this new Tick method isn’t running on the same thread that created the program’s user interface. That means it is not allowed to update the control’s in the user interface, in particular the labels.

To work around that, the code calls the form’s Invoke method, passing it the this.UpdateCountdown method. The (Action) in front of the method name converts the method into a delegate — a variable that contains the address of the method.

The Invoke method queues the call to this.UpdateCountdown so it will run on the UI thread. Because it runs on the UI thread, that method is allowed to update the form’s labels.

The following code shows the UpdateCountdown method.

// Update the countdown on the UI thread.
private void UpdateCountdown()
{
    TimeSpan remaining = EventDate - DateTime.Now;
    if (remaining.TotalSeconds < 1)
    {
        TheTimer.Dispose();
        this.WindowState = FormWindowState.Maximized;
        this.TopMost = true;

        foreach (Control ctl in this.Controls)
            ctl.Visible = (ctl == lblFinished);

        using (SoundPlayer player = new SoundPlayer(
            Properties.Resources.tada))
        {
            player.Play();
        }
    }
    else
    {
        lblDays.Text = remaining.Days + " days";
        lblHours.Text = remaining.Hours + " hours";
        lblMinutes.Text = remaining.Minutes + " minutes";
        lblSeconds.Text = remaining.Seconds + " seconds";
    }
}

This code subtracts the current time from the event date to get the amount of time between now and the event.

If less than one second remains until the event, the code disposes of the Timer, maximizes the form, and makes the form topmost. It then hides all of the form’s controls except lblFinished, which displays a message saying that the event has arrived. This branch of the if statement finishes by playing a “ta da” sound resource.

If more than one second remains until the event, the code updates the form’s labels to show the number of days, hours, minutes, and seconds remaining.

Theer’s one more odd detail. If you close the form, the form is destroyed but before the Timer is destroyed it might call its callback method. If that happens, the method tries to update controls that are already destroyed so the program throws an exception. Depending on the exact timing, this problem may not happen, so the program may run correctly many times and only crash occasionally and figuring out what went wrong can be difficult.

To prevent that, the program uses the following FormClosing event handler.

// Stop the timer.
private void Form1_FormClosing(object sender,
    FormClosingEventArgs e)
{
    if (TheTimer != null) TheTimer.Dispose();
}

This code simply disposes of the Timer if it was created.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.

This entry was posted in miscellany, system and tagged , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *