Таймер обратного отсчета С#

Я пытаюсь сделать обратный отсчет с помощью С# и показать время в формате:

hour:minutes:seconds

Я пробовал это:

 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!"; 
  }

Появились и другие способы решения этой проблемы. Спасибо заранее

Ответ 1

Здесь вы не должны использовать Thread.Sleep. Thread.Sleep в потоке пользовательского интерфейса блокирует пользовательский интерфейс, а его использование в другом потоке приводит к дополнительной сложности из-за синхронизации потоков.

Если у вас есть С# 5 или async CTP, вы, вероятно, можете написать код, очень похожий на то, что вы сделали, поскольку затем вы получаете эквивалент Thread.Sleep на основе продолжения, который не блокирует пользовательский интерфейс.

В стандартном С# 4 я бы использовал System.Windows.Forms.Timer.

Чтобы начать обратный отсчет:

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;

В обработчике таймера вы пишете:

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

Для других параметров форматирования см. Стандартные строки формата TimeSpan.

Одна проблема, которая остается с этим кодом, заключается в том, что она не будет работать корректно, если системные часы будут изменены.

При использовании DateTime.Now вместо DateTime.UtcNow он также ломается при переключении с/на летнее или изменение часового пояса. Поскольку вы хотите определить определенный момент времени (а не время отображения), вы должны использовать UTC вместо локального времени.

Ответ 2

Я бы использовал таймер примерно так. Сначала пара переменных экземпляра.

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

и в событии конструктора или загрузки

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

а затем, наконец, обработчик события

void timer_Tick(object sender, EventArgs e)
{
    _countDown--;
    if (_countDown < 1)
    {
        _countDown = 30;
    }
    lblCountDown.Text = _countDown.ToString();
}

Ответ 3

Вы также можете использовать таймер, так как он будет обрабатывать все проблемы, такие как UI-блокировка. Вы можете использовать System.Windows.Forms.Timer -Timer. В библиотеке MSDN вы можете найти образцы его использования.

WinForms-Timer также обрабатывает вызов через поток Timer и UI-поток.

- SeriTools

Ответ 4

Ваш код устанавливает переменные, а затем переходит в спящий режим на 3 минуты, поэтому оператор if не выполняется до тех пор, пока он не покинет состояние ожидания. Либо настройте новый поток для обновления пользовательского интерфейса, либо выполните что-то вроде этого...

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

label1.Text = "Done!";

Со вторым потоком вы все равно можете делать что-то в своей программе, пока оно работает. "Готово!" появится после завершения.