daily001
daily001

Reputation: 23

Show only relevant numbers from stopwatch

While attempting to make a stopwatch, I noticed that it would display all groups of numbers related to the timer (in this case "00:00:00:00")

In order to show only relevant numbers, and not show the minutes column when a minute hadn't passed, I came up with this code:

Public Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Dim sw As New Stopwatch
    Dim elapsed As TimeSpan = Me.stopwatch.Elapsed
    Label1.Text = String.Format("{0:00}:{1:00}",
                  Math.Floor(elapsed.Seconds),
                  elapsed.Milliseconds)
    If Label1.Text = "60:999" Then
        Label1.Text = String.Format("{0:00}:{1:00}:{2:00}",
                      Math.Floor(elapsed.Minutes),
                      elapsed.TotalSeconds, elapsed.TotalMilliseconds)
    End If
End Sub

When this code is active, the timer will only show the seconds and milliseconds column until it hits a full minute, in which case it will just loop back to 0 seconds and repeat. I'm assuming that the timer just can't detect exactly when label1's text is exactly 60.999, but I'm not sure. What is my logic missing?

Upvotes: 1

Views: 96

Answers (2)

Nate Barbettini
Nate Barbettini

Reputation: 53610

There's a few problems with your code:

  1. You are declaring a new Stopwatch inside of the Tick event. This is unnecessary.
  2. You're using string comparisons to do something that should be done with math. Your hunch is correct that the exact moment when the label's text is "60:999" is being missed by the timer.

Instead of comparing the label's text value, just look at how many milliseconds (or seconds) have elapsed!

Public Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Dim elapsed As TimeSpan = Me.stopwatch.Elapsed

    If elapsed.TotalSeconds >= 60 Then
        Label1.Text = String.Format("{0:00}:{1:00}:{2:00}",
            Math.Floor(elapsed.Minutes),
            Math.Floor(elapsed.Seconds), 
            elapsed.Milliseconds)
    Else
        Label1.Text = String.Format("{0:00}:{1:00}",
            Math.Floor(elapsed.Seconds),
            elapsed.Milliseconds)
    End If
End Sub

Upvotes: 0

Dai
Dai

Reputation: 155270

Timers are not guaranteed to fire exactly at their interval. I'm assuming your timer is set to go off every 1ms, however when you run your program you'll find it will probably by raised every 3-16ms with a lot of jitter, this is also not helped by the fact the WinForms Timer (which I assume you're using) goes through the Win32 window message pump, rather than its own dedicated (and real-time) thread.

Anyway, the fix is to not compare strings, instead compare the actual time values:

If elapsed.TotalSeconds < 60 Then
    label1.Text = String.Format("{0:00}:{1:00}", Math.Floor(elapsed.Seconds), elapsed.Miliseconds)
Else
    label1.Text = String.Format("{0:00}:{1:00}:{2:00}", Math.Floor(elapsed.TotalMinutes), Math.Floor(elapsed.Seconds), elapsed.Miliseconds)
End If

Note you shouldn't be displaying TotalSeconds if you're already displaying Minutes.

Upvotes: 2

Related Questions