【发布时间】: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