VB.Net Countdown - Timer has delay - vb.net

So I just created a Countdown which updates each timer tick but using a real countdown to compare the one I coded has a small delay. After 1 Minute its like 3 Seconds slower than a normal timer.
The weird thing is that it works fine if I set the Interval to something above 1000 = update each second.
But everything below 1000 has a delay and I want a timer with milliseconds, update each 0.1 secon = Interval 100.
Thats the code I have so far (It looks messy because it switches the color of the label once it reaches a certain amount of time left)
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Label1.Text = "Time left" & count
If count >= 12 Then
If change = False Then
Label1.ForeColor = Color.Chartreuse
change = True
End If
Timer1.Interval = 2000
count = count - 2
ElseIf count <= 11.5 And count >= 7.5 Then
If playaudio = False Then
playaudio = True
Label1.ForeColor = Color.Yellow
End If
Timer1.Interval = 100
count = count - 0.1
ElseIf count <= 7.5 And count >= 0 Then
count = count - 0.1
If changes = False Then
Label1.ForeColor = Color.Red
changes = True
End If
ElseIf count <= 0 Then
Timer1.Stop()
Timer2.Enabled = True
Timer2.Start()
playaudio = False
changes = False
change = False
count = 100
End If
End Sub
Is there any ohter way that the timer doesnt delay?

I think it's because the actions you take are delaying the next timer.
One way to work around the problem would be to set the timer to something like 10ms. At each tick, you check if the previous refresh is older than the desired delay.
Or you can set the timer in another thread, which will not be affected by the execution time.

Forms timers are notoriously inaccurate. Use a stopwatch to measure time and the Timer to update the display.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Stpw.Start()
Timer1.Start() 'I have it set to 100ms.
End Sub
Private CountDown As TimeSpan = TimeSpan.FromSeconds(10.0#)
Private Stpw As New Stopwatch
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If Stpw.Elapsed >= CountDown Then
Timer1.Stop()
Stpw.Stop()
Label1.Text = "Blast Off"
Else
Dim ToGo As TimeSpan = CountDown - Stpw.Elapsed
Label1.Text = ToGo.ToString("hh\:mm\:ss\.f")
Select Case ToGo.TotalSeconds
End Select
End If
End Sub
End Class
Start with this and you'll see how it works. Your color coding can probably go in the Select.

Here's a bit of a restructuring of your code. It's like dbasnett's stopwatch, except it uses a class you're more likely to be familiar with, to introduce the concept that "instead of using a timer and measuring time passage by adding up (or taking away) every time it ticks (which is subject to cumulative errors), pick a moment in time and regularly calculate how far away it is":
'code that starts a minute countdown
'class level property noting the end time
Private _endDateTime as DateTime
'the end time is a minute away
_endDateTime = DateTime.UtcNow.AddMinutes(1)
'we only need one timer
timer1.Start()
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim count = (_endDateTime - DateTime.UtcNow).TotalSeconds 'how far away is the end time?
Label1.Text = $"Time left {count:0.0}s"
If count <= 0 Then 'reset
Timer1.Stop()
Label1.ForeColor = Color.Chartreuse
playaudio = False
Else If count <= 7.5 Then
Label1.ForeColor = Color.Red
ElseIf count <= 11.5 Then
playaudio = True
Label1.ForeColor = Color.Yellow
End If
End Sub
There is a lot of redundancy that can be removed in your code; we'll use a single timer, it can tick on whatever interval you like; if there is a label that is counting down with 0.1 second precision then we should maybe make it tick every 50ms
If you swap your ifs around you won't need to make them a range; if you're measuring less than, put the test for the smallest number first. Mostly these ifs won't enter at all, then when there is less than 11.5 seconds to go, the last if will start activating etc..
Nothing happens if you set a label color to be the same as it already is so you don't need that complicated Boolean setup with change/changes to make sure you only set the color once
The crucial thing is we have a fixed end point in time and every time we tick we work out how far away that is. We could tick 1000 times a second or once every 10 seconds, it doesn't matter; the end time is the end time is the end time

Related

Dynamically updating a progress bar in VB

Hello I am a novice / mediocre VB programmer and I made a timer that simply need to update it self every second.
I started having doubts about weather or not it was working so I applied a msg box to my timers code it went off every second updating it self but the progress bar wont? why?
Dim power As PowerStatus = SystemInformation.PowerStatus
Dim percent As Single = power.BatteryLifePercent
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
ProgressBar1.Value = percent * 100
Label1.Text = percent * 100
End Sub
I have a power status and percent that takes the status and turn into a usable percentage then the progress bar uses that percentage but isnt updating like the msgBOX does why?
Well, your timer is probably working well and all, but you don't show where/how you get the updated value of SystemInformation.PowerStatus.BatteryLifePercent.
Maybe you're doing this somewhere else in your code, but as it is posted, you're always displaying the same value, so of course the progressbar is never going to change.
From what I can see from your question, everything should be working ok but I cant see that you have added Timer1.Interval = 1000 however you may have but this with the designer and not the coding side, however nevertheless here is how I did this project so you could see my working example just so you can be sure, If you have any problems let me know and i will do my best to help you out :)
Public Class Form1
Dim Timer1 As New Timer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Timer1.Enabled = True
Timer1.Interval = 1000
AddHandler Timer1.Tick, AddressOf Timer1_Tick
Timer1.Start()
End Sub
Private Sub Timer1_Tick()
Dim POWER As PowerStatus = SystemInformation.PowerStatus
Dim PERCENT As Single = POWER.BatteryLifePercent
Dim ONCHARGE As PowerStatus = SystemInformation.PowerStatus
ProgressBar1.Value = PERCENT * 100
Label1.Text = "Power Remaining: " & PERCENT * 100 & "%"
If ONCHARGE.PowerLineStatus = PowerLineStatus.Online Then
Label2.Text = "Currently: Charging"
Else
Label2.Text = "Currently: Not Charging"
End If
End Sub
End Class
I added a Progressbar and two labels to the form, one of which is the computer battery and another of which will tell the user if the cable is unplugged in or not, i know you didnt ask for the cable status but i added it just incase you needed it :)
Happy Coding

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?
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.
There's a few problems with your code:
You are declaring a new Stopwatch inside of the Tick event. This is unnecessary.
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

VB.NET loop timer, multiple "IF" conditions

Writing the title to try and explain my query I think was harder than the problem I'm actually facing :) - Anyway on to the question.
So I have a 20 second timer but I want two different things to happen on the first and second 10 seconds. Specifically to change the active tab.
So I thought to myself I'll just write an if Statement in the timer tick event that if it = 10 seconds to change to the second tab and when it hits 0 to switch back to the first, then to restart the timer.
Below is my code but nothing happens, I think the problem lies with reading the current remaining time.
Private timeLeft2 As Integer
Private Sub timerCountdown2()
timeLeft2 = 20
End Sub
Private Sub tabTimer_Tick(sender As Object, e As EventArgs) Handles tabTimer.Tick
If timeLeft2 = 10 Then
TabControlVertical1.SelectTab(1)
End If
If timeLeft2 = 0 Then
TabControlVertical1.SelectTab(0)
tabTimer.Stop()
tabTimer.Start()
End If
End Sub
The properties of my timer are enabled = true and Interval = 1000
What am I doing wrong?
You should set the timer to trigger the Tick event every 10 seconds, not every 20 (or 1 as by your edit above).
Every time the Tick event is triggered, you look at the value of a global boolean variable.
If this variable is true you execute the code reserved for the first 10 seconds and invert the value of the boolean. When the timer triggers again, you execute the code for the second case and invert again the value of the boolean
So, somewhere in your code or in the designer set the tabTimer interval to 10 seconds
tabTimer.Interval = 10000
and declare a global boolean variable (In the same forms class probably)
Private tabSwitcher as Boolean = True
Now the Tick event could be written as:
(no need to stop the timer if this process needs to continue)
Private Sub tabTimer_Tick(sender As Object, e As EventArgs) Handles tabTimer.Tick
If tabSwitcher = True Then
TabControlVertical1.SelectTab(1)
else
TabControlVertical1.SelectTab(0)
End If
tabSwitcher = Not tabSwitcher
End Sub
This is what I think you are asking:
do something in 10 timer ticks - timer set to 1000
do something else 10 timer ticks later
repeat
Try this
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Static ct As Integer = 0
ct += 1
If ct = 10 Then
'do 10 stuff here
Debug.WriteLine("10")
ElseIf ct = 20 Then
'do 20 stuff here
Debug.WriteLine("20")
'then reset ct <<<<<<<<<<<<
ct = 0
End If
End Sub
Friend Class timerCtrl : Inherits Timer
Private ReadOnly tickFunc As EventHandler = Nothing
Friend Sub New(ByRef theFunc As EventHandler, ByVal theInterval As Integer, Optional ByVal autoStart As Boolean = True)
tickFunc = theFunc
Interval = theInterval
AddHandler Tick, tickFunc
If (autoStart) Then Start()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
RemoveHandler Tick, tickFunc
End If
MyBase.Dispose(disposing)
End Sub
End Class
Friend Class TabClass
Private timerStep As Boolean = False
Private timerTabs As timerCtrl = Nothing
Friend Sub New()
timerTabs = New timerCtrl(AddressOf timerTabsTick, 10000)
End Sub
Private Sub timerTabsTick(ByVal sender As Object, ByVal e As EventArgs)
timerStep = Not timerStep
If timerStep Then
' condition 1
Else
' condition 2
End If
End Sub
End Class
a simple timer helper class for abstraction. to kill the timer, not just .Stop it, use timerTabs.Dispose(). eliminates the need to detach the event handler separately.
Seems to me that your timer is never getting to the value you are asking for in the if statements due to the fact that you have set the timer to the value of 20.
Also, I've use visual basics and am not to sure but doens't it need to be timeleft2.value?
Also, by stoping and starting the timer, it isn't actually restarting the timer, when you stop it say on 15 secs, and the restart, the timer restarts from 15 secs.
Try this.
If timeLeft2.Value = 10 Then
TabControlVertical1.SelectTab(1)
else if timeLeft2.Value = 0 Then
TabControlVertical1.SelectTab(0)
tabTimer.Stop()
timeLeft2.value = 0
tabTimer.Start()
End If

VB.net - Timer isn't updating

I'm trying to add an uptime counter so that from the moment my application launches it starts a timer that increments by the second until the application is closed or I stop it on purpose.
Currently the timer counts the first second and then stops. This might be me not understanding the tick function? I assume that the interval I set for the timer will refresh or loop the code within the tick sub? (Could me massively wrong).
I have timer1 and I've set it to "Enabled" and the interval to "1000" for one second.
In my Timer1_Tick Sub I have this:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim seconds, minutes, hours As Integer
If seconds = 60 Then
seconds = 0
minutes = minutes + 1
End If
If minutes = 60 Then
If seconds = 60 Then
seconds = 0
minutes = 0
hours = hours + 1
End If
End If
seconds = seconds + 1
Label44.Text = Format(hours, "00") & "." & Format(minutes, "00") & "." & Format(seconds, "00")
End Sub
In Form1_Load I have Timer1.Start()
Please can you tell me what I'm missing? Thanks.
For up time in my applications I just log the time and date it was started, then use a label to show difference in time since the time was logged. It's a lot simpler than running a time ticking all the time.
The approaches given are highly inaccurate because they assume that the tick event fires exactly at the specified interval, and that doesn't happen.
The tick event should be used only to update the label from a more precise time measurement. In the code below a stopwatch is used.
Dim appruntime As Stopwatch = Stopwatch.StartNew
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Label1.Text = appruntime.Elapsed.ToString("d\ hh\:mm\:ss")
End Sub
You need to declare the variables within Form1.
Public Class Form1
Private seconds, minutes, hours As Integer
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
If seconds = 60 Then
seconds = 0
minutes = minutes + 1
End If
If minutes = 60 Then
If seconds = 60 Then
seconds = 0
minutes = 0
hours = hours + 1
End If
End If
seconds = seconds + 1
Label44.Text = Format(hours, "00") & "." & Format(minutes, "00") & "." & Format(seconds, "00")
End Sub
End Class

Create a simple timer to count seconds, minutes and hours

I'm trying to create a pretty simple program that basically is a timer.
I have three sets of labels, lbl_seconds, lbl_minutes and lbl_hours.
These labels have the default value of 00:00 and I want the timer to change that for each label. I have googled this but I cannot seem to find any good info on it.
Do I need three separate timers? I have also noticed that the timers have their own tick event handler. I guess it's in this that I need to change the value of the label. How to do just that?
Here is an example of this
Dim timercount As Integer = 60 'The number of seconds
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Timer1.Interval = 1000 'The number of miliseconds in a second
Timer1.Enabled = True 'Start the timer
End Sub
Private Sub btnReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReset.Click
Timer1.Enabled = False 'Stop the timer
timercount = 60 'Reset to 60 seconds
lblOutput.Text = timercount.ToString() 'Reset the output display to 60
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
lblOutput.Text = timercount.ToString() 'show the countdown in the label
If timercount = 0 Then 'Check to see if it has reached 0, if yes then stop timer and display done
Timer1.Enabled = False
lblOutput.Text = "Done"
Else 'If timercount is higher then 0 then subtract one from it
timercount -= 1
End If
End Sub
I think you need something of this sort
Public Function GetTime(Time as Integer) As String
Dim Hrs As Integer 'number of hours '
Dim Min As Integer 'number of Minutes '
Dim Sec As Integer 'number of Sec '
'Seconds'
Sec = Time Mod 60
'Minutes'
Min = ((Time - Sec) / 60) Mod 60
'Hours'
Hrs = ((Time - (Sec + (Min * 60))) / 3600) Mod 60
Return Format(Hrs, "00") & ":" & Format(Min, "00") & ":" & Format(Sec, "00")
End Function
You pass the time (in seconds) you'd like to display on the label's text and the time will be formatted as you like it.
e.g.
lblTime.Text = GetTime(90)
This will display 00:01:30 on the label.
For reference, you can see this project I submitted on FreeVBCode some time ago. The only caveat is the project is in VB6. You should be able to open it in Visual Studio though.
Start off by adding a timer. Call it whatever you like, in this example I will be keeping it as Timer1. Add a label and set the text as: 00:00.
In the code after the class has been set (usually it is Public Class Form1) make a variable as a stopwatch: Dim stopwatch As New Stopwatch
In the timer tick event code, put the following: (Please note that my 00:00 label is called Label1)
Label1.Text = String.Format("{0}:{1}:{2}", watch.Elapsed.Hours.ToString("00"), watch.Elapsed.Minutes.ToString("00"), watch.Elapsed.Seconds.ToString("00"))
Use one timer and in event sub change value of your labels.
You need one timer and three counter for seconds, minutes and hours.
Count minutes, then modulo minutes / 60, if return 0 then start count minutes.
Modulo minutes/60, if return 0 then start count hours.