首先,您的計時器間隔太短。你永遠不會從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
來完成。
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();
}
您在'Tick'稱爲'sec.Stop':例如,你可以使它看起來像這樣,而不是改變
StartCommand.Execute()
方法。這是否會觸發不止一次? – 2014-12-06 15:17:36你確實。但我希望它從5開始並停在零。 – GLviz 2014-12-06 15:26:48
'if(b> 0)b - ;'? – 2014-12-06 15:34:26