如何使用C#将定时器计数器停止在零?

时间:2014-12-06 15:14:38

标签: c# wpf timer

我使用了以下代码:

DispatcherTimer sec = new DispatcherTimer();
sec.Interval = new TimeSpan(0, 0, 0, 1);

sec.Tick += delegate
{
     lblsec.Text = b--.ToString() + " Seconds.";
};

sec.Start();
c--;

此代码将显示从5开始的计数器,并将减少,它将变为负数。 我的问题是当它达到零时如何阻止它?

1 个答案:

答案 0 :(得分:1)

首先,您的计时器间隔太短。您永远不会从Windows获得单毫秒计时器间隔,并且出于UI目的,用户永远不会快速感知计时器更新。对于这样的事情,100ms或更长时间更合适。

其次,你不能指望计时器非常精确。例如,如果你指定一个100毫秒的间隔,你可能会在一秒钟内回调十次,但通常你不会。它取决于Windows线程调度程序的分辨率以及UI线程正在执行的其他活动。

考虑到这一点,并假设你在这里尝试做的是设置一个五秒计时器并向用户显示倒计时,这样的事情应该有效:

TimeSpan total = TimeSpan.FromSeconds(5);
DispatcherTimer timer = new DispatcherTimer();
Stopwatch sw = new Stopwatch();

timer.Interval = TimeSpan.FromMilliseconds(100);
timer.Tick += (sender, e) =>
{
    double secondsLeft = (total - sw.Elapsed).TotalSeconds;

    if (secondsLeft <= 0)
    {
        timer.Stop();
        secondsLeft = 0;
    }

    lblsec.Text = secondsLeft.ToString("0.0") + " Seconds";
};

sw.Start();
timer.Start();


附录:

这是一个完整的WPF程序,说明了如何使用上述代码:

<强> C#:

class TimerModel : INotifyPropertyChanged
{
    private TimeSpan _timeLeft;
    private readonly ICommand _startCommand;

    public TimeSpan TimeLeft
    {
        get { return _timeLeft; }
        set
        {
            if (value != _timeLeft)
            {
                _timeLeft = value;
                OnPropertyChanged();
            }
        }
    }

    public ICommand Start { get { return _startCommand; } }

    public TimerModel()
    {
        _startCommand = new StartCommand(this);
    }

    private class StartCommand : ICommand
    {
        private bool _running;
        private readonly TimerModel _timerModel;

        public bool CanExecute(object parameter)
        {
            return !_running;
        }

        public event EventHandler CanExecuteChanged;

        public StartCommand(TimerModel timerModel)
        {
            _timerModel = timerModel;
        }

        public void Execute(object parameter)
        {
            TimeSpan total = TimeSpan.FromSeconds(5);
            DispatcherTimer timer = new DispatcherTimer();
            Stopwatch sw = new Stopwatch();

            timer.Interval = TimeSpan.FromMilliseconds(100);
            timer.Tick += (sender, e) =>
            {
                TimeSpan timeLeft = total - sw.Elapsed;

                if (timeLeft <= TimeSpan.Zero)
                {
                    timer.Stop();
                    timeLeft = TimeSpan.Zero;
                    _running = false;
                    OnCanExecuteChanged();
                }

                _timerModel.TimeLeft = timeLeft;
            };

            sw.Start();
            timer.Start();
            _running = true;
            OnCanExecuteChanged();
        }

        private void OnCanExecuteChanged()
        {
            EventHandler handler = CanExecuteChanged;

            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

<强> XAML:

<Window x:Class="TestSO27333077CountdownTimer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:TestSO27333077CountdownTimer"
        Title="MainWindow" Height="350" Width="525">

  <Window.DataContext>
    <l:TimerModel/>
  </Window.DataContext>

  <StackPanel>
    <Button Content="Start" Command="{Binding Start}" HorizontalAlignment="Left"/>
    <TextBlock Text="{Binding TimeLeft.TotalSeconds, StringFormat={}{0:0.0} Seconds}"/>
  </StackPanel>
</Window>

请注意,也可以使用DateTime.UtcNow属性而不是Stopwatch来完成计时。例如,您可以更改StartCommand.Execute()方法,使其看起来像这样:

        public void Execute(object parameter)
        {
            DateTime finishTime = DateTime.UtcNow + TimeSpan.FromSeconds(5);
            DispatcherTimer timer = new DispatcherTimer();

            timer.Interval = TimeSpan.FromMilliseconds(100);
            timer.Tick += (sender, e) =>
            {
                TimeSpan timeLeft = finishTime - DateTime.UtcNow;

                if (timeLeft <= TimeSpan.Zero)
                {
                    timer.Stop();
                    timeLeft = TimeSpan.Zero;
                    _running = false;
                    OnCanExecuteChanged();
                }

                _timerModel.TimeLeft = timeLeft;
            };

            timer.Start();
            _running = true;
            OnCanExecuteChanged();
        }