WPF倒计时太慢(DispatcherTimer)

时间:2014-07-27 21:22:42

标签: c# wpf countdown dispatchertimer

我目前正在编写一个WPF应用程序,我希望它有一个倒数计时器。这是我的CountDown类:

 internal class CountDown : INotifyPropertyChanged
{
    private readonly DispatcherTimer _timer;
    private string _currentTimeString;
    private TimeSpan _runTime;
    private TimeSpan _timeleft;

    public CountDown(TimeSpan runTime)
    {
        if (runTime == null) throw new ArgumentNullException("runTime");
        _runTime = runTime;

        _timer = new DispatcherTimer();
        _timer.Interval = new TimeSpan(0, 0, 0, 0, 10);

        _timer.Tick += Update;
    }

    public CountDown(TimeSpan runTime, TimeSpan interval)
    {
        if (runTime == null) throw new ArgumentNullException("runTime");
        _runTime = runTime;

        _timer = new DispatcherTimer();

        if (interval == null) throw new ArgumentNullException("interval");
        _timer.Interval = interval;

        _timer.Tick += Update;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public string CurrentTimeString
    {
        get { return _currentTimeString; }
        private set
        {
            _currentTimeString = value;
            NotifyPropertyChanged();
        }
    }

    public void Start()
    {
        var task = new Task(_timer.Start);
        _timeleft = _runTime;
        task.Start();
    }

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    private void Update(object sender, EventArgs e)
    {
        _timeleft -= _timer.Interval;

        DateTime newTime = new DateTime();
        newTime = DateTime.MinValue;
        newTime += _timeleft;

        CurrentTimeString = newTime.ToString("mm:ss:ff");
    }
}

作文根:

public MainWindow()
    {
        CountDown countDown = new CountDown(new TimeSpan(0, 1, 0));

        InitializeComponent();
        tb1.DataContext = countDown; //tb1 = TextBlock
        countDown.Start();
    }

除非我将间隔设置为10毫秒,然后它比实际秒数慢,否则一切正常。我该如何解决这个问题?

编辑:我还不能回答我自己的问题,所以在这里: 我没有使用任何计时器完全重写了我的课程。发现这些对我来说不够准确。

public class CountDown : INotifyPropertyChanged
{
    private string _currentTimeString;
    private TimeSpan _runTime;
    private bool _shouldStop;
    private DateTime _timeToStop;
    private TimeSpan _updateInterval;

    public CountDown(TimeSpan runTime)
    {
        if (runTime == null) throw new ArgumentNullException("runTime");
        _runTime = runTime;

        _updateInterval = new TimeSpan(0, 0, 0, 0, 10);

        Tick += Update;
    }

    public CountDown(TimeSpan runTime, TimeSpan updateInterval)
    {
        if (runTime == null) throw new ArgumentNullException("runTime");
        _runTime = runTime;

        if (updateInterval == null) throw new ArgumentNullException("updateInterval");
        _updateInterval = updateInterval;

        Tick += Update;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public event Action Tick;

    public string CurrentTimeString
    {
        get { return _currentTimeString; }
        set
        {
            _currentTimeString = value;
            NotifyPropertyChanged();
        }
    }
    public void Start()
    {
        _shouldStop = false;
        _timeToStop = DateTime.Now + _runTime;
        var task = new Task(GenerateTicks);
        task.Start();
    }

    public void Stop()
    {
        _shouldStop = true;
    }

    private void GenerateTicks()
    {
        while (_shouldStop == false)
        {
            if (Tick != null)
                Tick();

            Thread.Sleep(_updateInterval);
        }
    }

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private void Update()
    {
        var timeLeft = _timeToStop - DateTime.Now;

        if (timeLeft <= TimeSpan.Zero)
        {
            _shouldStop = true;
            return;
        }

        var timeLeftDate = DateTime.MinValue + timeLeft;

        CurrentTimeString = timeLeftDate.ToString("mm:ss:ff");
    }
}

2 个答案:

答案 0 :(得分:1)

首先,您不需要任务来完成倒计时。如果你使用一个每隔50ms滴答一次的计时器,你就不会阻止任何事情。比50分钟更快的嘀嗒声更有意义,因为我猜你的倒计时显示小时,分钟或秒。毫秒对于计时器来说有点太多了,不是吗?即使您想要显示ms范围,人眼也不会注意倒计时是每10或50毫秒更新一次。

接下来,如果您使用DateTime作为时基,则可能更容易处理。它可以更容易地计算实际剩余时间。

using System;
using System.Timers;

public class Countdown
{
    private readonly TimeSpan countdownTime;
    private readonly Timer timer;
    private DateTime startTime;

    public Countdown(TimeSpan countdownTime)
    {
        this.countdownTime = countdownTime;
        this.timer = new Timer(10);
    }

    public string RemainingTime { get; private set; }

    public void Start()
    {
        this.startTime = DateTime.Now;
        this.timer.Start();
    }

    private void Timer_Tick(object state)
    {
        var now = DateTime.Now;
        var difference = now - this.startTime;
        var remaining = this.countdownTime - difference;
        if (remaining < TimeSpan.Zero)
        {
            this.timer.Stop();
            // Raise Event or something
        }

        this.RemainingTime = remaining.ToString("mm:ss:fff");
    }
}

对于这种情况,异步倒计时会有点过分。但如果你需要它,它很容易升级。

答案 1 :(得分:0)

您的Countdown类在构造函数中有2个参数。在这种情况下,你没有调用你的单个参数构造函数(你有10ms的那个)。您在第二个参数中为间隔提供1s(新TimeSpan(0,0,1)),这是您在运行时在UI中看到的内容。