【问题标题】:WPF Countdown too slow (DispatcherTimer)WPF 倒计时太慢(DispatcherTimer)
【发布时间】:2014-07-27 21:22:41
【问题描述】:

我目前正在编写一个 WPF 应用程序,我希望在其中包含一个倒数计时器。这是我的倒计时课:

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

【问题讨论】:

  • 计时器每秒只能滴答 64 次。 15.625毫秒,操作系统中的一个核心数,即时钟中断率。所以你的计时器会总是迟到。请改用挂钟时间,在启动计时器时存储 DateTime.UtcNow 的值。当它滴答作响时,再次使用 DateTime.UtcNow 并减去存储的值。
  • 关于编辑:为什么使用任务来生成刻度?为什么不直接使用已经存在的计时器呢?我不是那么喜欢线程,但我想这确实是一种糟糕的方法。这极大地启动了 CPU。 stackoverflow.com/questions/3886171/…
  • 我在 MSDN 中的某处读到过,定时器不能保证在应该引发事件时引发事件,只是他们不会在应该引发事件之前(!)引发事件。这就解释了为什么我的倒计时这么慢。 sleep 方法恰好在该时间停止线程。另外,我这个学期的教授曾经这样做过。编辑:在您链接的问题中,第二个答案中的人不是说它实际上并不那么贵吗?
  • 我视情况而定。使用计时器仍然是可取的。我还没有听说过不引发计时器事件。即使它错过了一个,你仍然不会注意到它。

标签: c# wpf countdown dispatchertimer


【解决方案1】:

首先,您不需要任务来完成倒计时。如果您使用每 50 毫秒计时一次的计时器,您将不会阻止任何内容。比 50 毫秒更快的滴答声没有意义,因为我猜你的倒计时显示的是小时、分钟或秒。毫秒对于计时器来说有点太多了,不是吗?即使您想显示毫秒范围,人眼也不会注意到倒计时是每 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");
    }
}

在这种情况下,异步倒计时有点过分了。但如果您需要,它很容易升级。

【讨论】:

  • 我已经知道如何在没有任何计时器的情况下做到这一点,但还是谢谢你!直到现在才编辑我的问题,因为我还不能回答我自己的问题。
  • 清除:倒计时显示分钟、秒和毫秒。我想要快速更新,因为这让我想起了小时候的秒表。
【解决方案2】:

您的 Countdown 类在构造函数中有 2 个参数。在这种情况下,不会调用您的单参数构造函数(您使用 10 毫秒的构造函数)。您正在为第二个参数中的间隔提供 1s (new TimeSpan(0,0,1)),这就是您在运行它时在 UI 中看到的内容。

【讨论】:

  • 是的,我知道,但这不是我的意思。应该改变这一点。当我让它以 10 毫秒工作时,它的计数比 1 秒慢。
猜你喜欢
  • 1970-01-01
  • 2015-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多