12

I'm trying to make a countdown using C# and show the time in format:

hour:minutes:seconds

I've tried this:

 var minutes = 3; //countdown time
  var start = DateTime.Now;
  var end = DateTime.Now.AddMinutes(minutes);
  Thread.Sleep(1800);
  if (??) // I tried DateTime.Now > end not works
  {
       //... show time
      label1.Text = "..."; 
  } 
  else 
  {
     //done 
      label1.Text = "Done!"; 
  }

Different ways to solve this problem also appeared. Thanks in advance

yoozer8
  • 7,361
  • 7
  • 58
  • 93
The Mask
  • 17,007
  • 37
  • 111
  • 185
  • If you want to periodically update the label, you'll need something like a loop. – svick Nov 01 '11 at 17:54
  • 2
    @javasocute I he did, I'd have my doubts about his sanity. This is Winforms, and .net has perfectly fine mechanisms for dealing with such stuff. No need to bring in javascript for such a little thing. – CodesInChaos Nov 01 '11 at 18:05

4 Answers4

30

You should not use Thread.Sleep here. Thread.Sleep on the UI thread blocks the UI, and using it on another thread leads to additional complexity due to thread synchronization.

If you have C# 5 or the async CTP you probably can write code very similar to what you did, since you then get a continuation based equivalent of Thread.Sleep that doesn't block the UI.

In standard C# 4 I'd use a System.Windows.Forms.Timer.

To start the countdown:

var minutes = 3; //countdown time
var start = DateTime.UtcNow; // Use UtcNow instead of Now
endTime = start.AddMinutes(minutes); //endTime is a member, not a local variable
timer1.Enabled = true;

In the timer handler you write:

TimeSpan remainingTime=endTime-DateTime.UtcNow;
if(remainingTime<TimeSpan.Zero)
{
   label1.Text = "Done!";
   timer1.Enabled=false; 
}
else
{
  label1.Text = remainingTime.ToString();
}

For other formatting options see Standard TimeSpan Format Strings.

One issue that remains with this code is that it will not work correctly if the system clock changes.

When using DateTime.Now instead of DateTime.UtcNow it will also break when switching from/to daylight saving or changing the timezone. Since you want to identify a certain point in time (and not a display time) you should use UTC instead of local time.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • Thanks very much! +1 for good answer, but I have one problem in statament: `Error:Operator '<' cannot be applied to operands of type 'System.TimeSpan' and 'int'` how I fix it? – The Mask Nov 01 '11 at 19:22
  • @TheMask Use `TimeSpan.Zero` instead of `0` – CodesInChaos Nov 01 '11 at 19:27
  • 1
    CodeInChaos:The guy that things really work. Works perfect. Thanks very much. :) – The Mask Nov 01 '11 at 19:43
  • +1 for the working code. A quick question though, you mentioned not to use Now, instead use UtcNow. I have checked it with both the options, and it works well both ways. Any specific reason why you would prefer UtcNow over Now? – Rahul Soni Nov 26 '11 at 07:25
  • 2
    @Rahul My last paragraph already addresses that. If your code is running when daylight saving switches on/off `DateTime.Now` will jump by an hour. And then there is the more philosophical reason that I think the design of `DateTime` isn't very good when working with local times, so I avoid them whenever possible. – CodesInChaos Nov 26 '11 at 08:40
  • Thanks @CodeInChaos. Totally missed that part the first time around! It makes sense. – Rahul Soni Nov 27 '11 at 04:59
  • The if does not work for me. I used this: if (remainingTime.TotalSeconds <= 0) { do stuff} – Rob Jan 23 '12 at 13:52
  • @Rob Strange. Comparing timespans directly works for me. Can you explain what exactly didn't work? – CodesInChaos Jan 23 '12 at 14:01
  • I run Visual Studio 2010 Free edition with C# here, it did not understand TimeSpan.Zero. So that did not work whereas the code I mentioned does work for me. – Rob Jan 24 '12 at 18:07
  • Use this article: http://stackoverflow.com/questions/6191576/seconds-countdown-timer – Chagbert Dec 03 '15 at 07:16
6

I would use a timer something like this. First a couple of instance variables.

private int _countDown = 30; // Seconds
private Timer _timer;

and in the constructor or load event

_timer = new Timer();
_timer.Tick += new EventHandler(timer_Tick);
_timer.Interval = 1000;
_timer.Start();

and then finally the event handler

void timer_Tick(object sender, EventArgs e)
{
    _countDown--;
    if (_countDown < 1)
    {
        _countDown = 30;
    }
    lblCountDown.Text = _countDown.ToString();
}
Jonas Elfström
  • 30,834
  • 6
  • 70
  • 106
0

You could also use a Timer, as this would handle all the problems like UI-locking. You can use the System.Windows.Forms.Timer-Timer. In the MSDN library you can find samples of the use of it.

The WinForms-Timer handles also the invoking across the Timer-thread and the UI-thread.

- SeriTools

seri
  • 324
  • 2
  • 11
-1

Your code sets up the variables then goes to sleep for 3 minutes so the if-statement isn't executed until it leaves the sleep state. Either set up a new thread to update the UI or do something like this...

while (DateTime.now < end) {
  label1.Text = "...";
  Thread.Sleep(#); //Pick a second, or 5 or whatever
}

label1.Text = "Done!";

With a 2nd thread you can still do stuff in your program while it works. "Done!" will appear once it finishes.

Grambot
  • 4,370
  • 5
  • 28
  • 43