Reputation: 3482
I am working on a very simple stopwatch using WPF but using the System.Diagnostics
stopwatch was very slow and not reliable at all, compared to my system clock every 1 second from my application was 3 seconds on an actual clock.
I did some search about stopwatch being slow, found lots of results but no solution to it, so I decided to come up with my own counter.
Here is an sample of what I came up with:
System.Windows.Threading.DispatcherTimer _update;
DateTime _started;
bool isRunning = false;
The update thread:
_update = new System.Windows.Threading.DispatcherTimer(new TimeSpan(0, 0, 0, 0, 1), System.Windows.Threading.DispatcherPriority.Normal, delegate
{
if (isRunning)
iTimer.Content = new DateTime((DateTime.Now - _started).Ticks).ToString("HH:mm:ss");
}, this.Dispatcher);
I have 2 buttons, bToggle which is resposible for starting, stopping and resuming it and another button called bReset.
private void bReset_Click(object sender, RoutedEventArgs e)
{
isRunning = false;
iTimer.Content = "00:00:00";
bToggle.Content = "Start";
}
private void bToggle_Click(object sender, RoutedEventArgs e)
{
if ((string)bToggle.Content == "Start")
{
isRunning = true;
_started = DateTime.Now;
bToggle.Content = "Stop";
}
else if ((string)bToggle.Content == "Resume")
{
isRunning = true;
bToggle.Content = "Stop";
}
else
{
isRunning = false;
bToggle.Content = "Resume";
}
}
It works fine to start and reset but since I am using the actual time if I stop and resume, it will jump the seconds until the actual time.
How could I solve this problem or is there an alternative to stopwatch that actually have a good accuracy on the current time ?
Upvotes: 6
Views: 4917
Reputation: 10350
Answering to the topic, as an alternative to the Stopwatch, you may use the MiniProfiler:
You may use the profiler step to get data to analyze, like:
var profiler = MiniProfiler.StartNew("My Profiler Name");
using (profiler.Step("Main Work"))
{
// Do some work...
}
There's separate UI for the profiler, but you can grab the data in the debug mode or write it to the text file.
Links:
Upvotes: 0
Reputation: 1728
Use System.Timers.Timer
You can start and stop this, and set a counter in the timers tick event.
This is a simple example:
public partial class MainWindow : Window
{
private Timer _timer;
private int _time;
public MainWindow()
{
InitializeComponent();
_time = 0;
_timer = new Timer(1000);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
}
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
Dispatcher.Invoke(new Action(() =>
{
_time++;
tbTime.Text = _time.ToString();
}));
}
private void btnStartStop_Click(object sender, RoutedEventArgs e)
{
if (_timer.Enabled)
{
_timer.Stop();
}
else
{
_timer.Start();
}
}
}
Xaml:
<Grid>
<StackPanel>
<Button Name="btnStartStop" Content="start/stop timer" Click="btnStartStop_Click" />
<TextBlock Name="tbTime" Text="00:00" />
</StackPanel>
</Grid>
Upvotes: 1
Reputation: 15867
I think this line is your problem:
_update = new System.Windows.Threading.DispatcherTimer(new TimeSpan(0, 0, 0, 0, 1), System.Windows.Threading.DispatcherPriority.Normal
It tells WPF that you want your delegate to execute 1000 times per second, and to schedule it at the second-highest priority. That's a higher priority than Render
and DataBind
, and I think both Render and DataBind are needed to actually display the updated value you put in iTimer.Content
.
You should set the DispatcherPriority to Background and use a lower frequency (e.g. 20 ms - humans can't see anything faster than 50 fps, anyway.)
Upvotes: 0
Reputation: 171178
You need to intruduce an additional variable TimeSpan accumulatedTime
in which you save the elapsed interval whenever someone clicks stop.
And:
iTimer.Content = (new DateTime((DateTime.Now - _started).Ticks) + accumulatedTime).ToString("HH:mm:ss");
Upvotes: 2