How to make a countdown timer that a custom control - vb.net

I am trying to create a count down timer control that I will be adding to a bigger project later. The control I am trying to make is a countdown timer that is given an initial value of 60 secs but also allows the user to change that value if needed. I am doing this in Visual Studio using Visual Basics.
Public Class UserControl1
Dim timeTick As Integer
Dim min As Integer
Dim setSecs As Integer = 60
Dim sec As Integer = 120
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Timer.Start()
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
sec -= 1
min = sec % 60
Label1.Text = min & " : " & sec
If sec < 60 Then
min = 1 + timeTick
Label1.Text = min & " : " & sec
End If
End Sub
Property HowLong As Integer
Get
Return setSecs
End Get
Set(value As Integer)
setSecs = value
End Set
End Property
End Class

Set your Timer Interval to something less than one second; I used 250.
Then store the time in the future that is XXX seconds away, representing your countdown duration.
At each tick, simply subtract the current time from the stored future time to get a TimeSpan. Update your label with the TimeSpan value using ToString().
When the HowLong property is changed, update the target time and restart your timer...easy peesy.
All together, it'd look something like this:
Public Class UserControl1
Private target As DateTime
Private setSecs As Integer = 60
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
target = DateTime.Now.AddSeconds(HowLong)
Timer.Start()
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
Dim ts As TimeSpan = target.Subtract(DateTime.Now)
If ts.TotalMilliseconds > 0 Then
Label1.Text = "-" & ts.ToString("mm\:ss")
Else
Label1.Text = "00:00"
Timer.Stop()
End If
End Sub
Property HowLong As Integer
Get
Return setSecs
End Get
Set(value As Integer)
setSecs = value
Timer.Stop()
target = DateTime.Now.AddSeconds(HowLong)
Timer.Start()
End Set
End Property
End Class
The authors response:
Technically your way will work to I will post my solution below I did
it slightly differently. – Thomas
From my comments on the authors own submission:
The problem with this type of approach is that the Timer control is
not accurate. It is only guaranteed to not fire before the interval
has transpired. In fact it will almost always fire after the interval
with some extra "slop". For short periods (seconds/minutes), you won't
notice. For longer periods (hours), you will, as the accumulated slop
becomes bigger as time passes. Whether this matters is completely
dependent upon your application. – Idle_Mind
Technically speaking, here's a quick example of how inaccurate simply incrementing/decrementing a counter using a 1 second Timer can be:
' Timer1.Interval was set to 1000 (timer fires every "second")
Private seconds As Integer = 0
Private start As DateTime = DateTime.Now
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
seconds = seconds + 1
Label1.Text = seconds
Label2.Text = DateTime.Now.Subtract(start).TotalSeconds
End Sub
After only 1 hour and 15 minutes, the counter method on the left is already off by 4 seconds from the actual time that has passed:
A key advantage of the DateTime/TimeSpan method is that the time calculation is independent from the Timer. That is to say that the frequency at which the Timer fires has no bearing on how accurate the time calculation is.

This code below is how I made a simple timer control that countdown from a set value. Also, it has 2 buttons that pause and resume the time. This control will not work if in design mode and the timer interval is set to 1000. If you have any questions about how it works just leave a comment.
Public Class UserControl1
Dim timeRemaing As Integer = 60
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Timer.Start()
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
Dim sec As Integer
Dim mins As Integer
Dim timerFormat As String
If Not Me.DesignMode Then
sec = timeRemaing Mod 60
mins = timeRemaing \ 60
If sec < 10 Then
timerFormat = mins.ToString + ":0" + sec.ToString
Else
timerFormat = mins.ToString + ":" + sec.ToString
End If
If timeRemaing > 0 Then
timeRemaing -= 1
lblTime.Text = timerFormat
Else
Timer.Stop()
lblTime.Text = "The Time has Stop!!!"
End If
End If
End Sub
Public Property HowLong As Integer
Get
Return timeRemaing
End Get
Set(value As Integer)
If value <= 0 Then
Timer.Stop()
ElseIf value > 0 Then
Timer.Start()
timeRemaing = value
End If
End Set
End Property
Private Sub btnPause_Click(sender As Object, e As EventArgs) Handles btnPause.Click
Timer.Stop()
End Sub
Private Sub btnResume_Click(sender As Object, e As EventArgs) Handles btnResume.Click
Timer.Start()
End Sub
End Class

Related

Stopclock with a For Loop continues past limit and counter seems broken

Currently attempting a very simple private project of making a stopclock display a countdown after the user hits "start". Ideally, the clock is meant to count down to zero a number of times equal to TotalLap. It is also supposed to remove an amount of time (1 minute x current lap) from the timer before it starts again.
I have tried a While loop and now a For loop. In both cases what I call "the counter", which is labeled "CurrentLap" is going to the max value plus 1 (I added a label to show me the CurrentLap value and it's always 1 higher than the TotalLap after the first run, skipping all the other laps).
So my issues follow:
1) I have yet to find a good place to put the stop command for my timer
2) CurrentLap goes from 0 straight to 4 (according to my label for it)
Below I will include my code, which will include the commented-out previous attempts. Please keep in mind I'm relatively inexperienced with programming and know very little of the terminology (learning new skills!). Thanks in advance.
Edit: As an aside, Time is currently set to .25 for faster testing, it is normally an integer value.
Public Class Form1
Dim CurrentLap As Integer = 0
Dim TotalLap As Integer = 4
Dim Time As Decimal = 0.25
'Dim TimeReduction As Decimal = 0.25
'Dim StartTime As Decimal = (Time - TimeReduction)
Private TargetDT As DateTime
Private CountDownFrom As TimeSpan = TimeSpan.FromMinutes(Time)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
tmrCountdown.Interval = 1000
TargetDT = DateTime.Now.Add(CountDownFrom)
tmrCountdown.Start()
End Sub
Private Sub tmrCountdown_Tick(sender As Object, e As System.EventArgs) Handles tmrCountdown.Tick
Dim ts As TimeSpan = TargetDT.Subtract(DateTime.Now)
If ts.TotalMilliseconds > 0 Then
lblTime.Text = ts.ToString("mm\:ss")
Else
lblTime.Text = "00:00"
tmrCountdown.Dispose()
CurrentLap = CurrentLap + 1
For CurrentLap = 0 To TotalLap Step 1
tmrCountdown.Start()
TargetDT = DateTime.Now.Add(CountDownFrom)
If CurrentLap = TotalLap Then Exit For
Next CurrentLap
'While CurrentLap < TotalLap
'tmrCountdown.Start()
'TargetDT = DateTime.Now.Add(CountDownFrom)
'CurrentLap = CurrentLap + 1
'End While
'If CurrentLap < TotalLap Then
'CurrentLap = CurrentLap + 1
'TargetDT = DateTime.Now.Add(CountDownFrom)
'tmrCountdown.Start()
'Else
'tmrCountdown.Stop()
'MsgBox("Race Over")
'End If
End If
lblCurrentLap.Text = CurrentLap.ToString("")
End Sub
End Class

Winforms timer not accurate

I have an app that uses a System.Windows.Forms.Timer and am seeing issues with the timer interval. When I use an interval of 1000 for a duration of 180 seconds the elapsed time is consistently around 183 seconds. When I use an interval of 995, the elapsed time is accurate (i.e. 180 seconds). I have tried this on 3 different PCs with the same results on each. The code below uses 2 textboxes to display the start and end times and a label as a countdown timer.
Can anyone explain this behavior?
Public Class Form1
Private WithEvents tmr As New System.Windows.Forms.Timer
Private Duration As Integer = 180
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RunTimer()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
lblActivityTimer.Text = FormatCounter(Duration)
tmr.Interval = 1000
End Sub
Private Sub RunTimer()
tmr.Start()
TextBox1.Text = Now.ToString()
End Sub
Private Function FormatCounter(sec As Integer) As String
Dim Hours, Minutes, Seconds As Integer
Hours = sec \ 3600
Seconds = sec Mod 3600
Minutes = Seconds \ 60
Seconds = sec Mod 60
FormatCounter = Hours.ToString.PadLeft(2, "0"c) & ":" _
& Minutes.ToString.PadLeft(2, "0"c) _
& ":" _
& Seconds.ToString.PadLeft(2, "0"c)
End Function
Private Sub tmr_Tick(sender As Object, e As EventArgs) Handles tmr.Tick
Duration -= 1
lblActivityTimer.Text = FormatCounter(Duration)
If Duration = 0 Then
TextBox2.Text = Now.ToString()
tmr.Stop()
End If
End Sub
End Class
Use a Stop Watch, it provides a set of methods and properties that you can use to accurately measure elapsed time.
Shared Sub Main(ByVal args() As String)
Dim stopWatch As New Stopwatch()
stopWatch.Start()
Thread.Sleep(10000)
stopWatch.Stop()
' Get the elapsed time as a TimeSpan value.
Dim ts As TimeSpan = stopWatch.Elapsed
' Format and display the TimeSpan value.
Dim elapsedTime As String = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
Console.WriteLine( "RunTime " + elapsedTime)
End Sub 'Main
There are two readily available Timer object alternatives:
System.Threading.Timer (explained here, my first pick)
System.Timers.Timer (explained here)

ArgumentOutOFRangeException when comparing times

I'm working on a simple project to use at work (logistics company) for my colleagues and me.
Let me explain a little to make my question a bit easier.
Each Route represents a country that has a deadline. In this example I use Route 114. Route 114 represents the Netherlands and the orders should be finished at xx:xx:xx local time.
I'm using a DateTimePicker so the user can select the deadline and receive a warning if the ProgressBar reaches 70% (in this case a label turns red).
The code I have works so far, but sometimes it throws out an error saying:
Value of '-4758' is not valid for 'Maximum'. 'Maximum' must be greater
than or equal to 0. Parameter name: Maximum
I'm an amateur, but it looks like the time is counting backwards in some cases and thus results in a negative Value.
Public Class Deadlines
Private Route114Deadline As Boolean = False
Public Function GetTimeDifference(ByVal EndTime As DateTime, ByVal StartTime As DateTime) As Integer
Dim span As TimeSpan = EndTime.TimeOfDay - StartTime.TimeOfDay
Dim result As Integer = CInt(span.TotalSeconds)
Return result
End Function
Private Sub tm114_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tm114.Tick
' ROUTE 114 '
Dim value114 As Integer = pb114.Maximum - GetTimeDifference(DateTimePicker1.Value, DateTime.Now)
If value114 > pb114.Maximum Then
tm114.Stop()
End If
If value114 < pb114.Minimum Then
tm114.Stop()
Exit Sub
End If
pb114.Value = value114
If Not Route114Deadline AndAlso pb114.Value >= pb114.Maximum * 0.7 Then
Route114Deadline = True
lb114.ForeColor = Color.Red
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
pb114.Minimum = 0
pb114.Maximum = GetTimeDifference(DateTimePicker1.Value, DateTime.Now)
tm114.Start()
End Sub
End Class
Found the solution with some help!
Public Class Form1
Private Route114Deadline As Boolean = False
Public Function GetTimeDifference(ByVal EndTime As DateTime, ByVal StartTime As DateTime) As Integer
Dim span As TimeSpan = EndTime - StartTime
Dim result As Integer = CInt(span.TotalSeconds)
Return result
End Function
Private Sub tm114_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tm114.Tick
' ROUTE 114 '
'get the seconds from now to end
Dim value114 As Integer = GetTimeDifference(DateTimePicker1.Value, DateTime.Now)
'check if the seconds are less than 0
If value114 < 0 Then
tm114.Stop() 'stop the timer
pb114.Value = 0 'set the progressbar to 0 just in case the last value was 1 second and is now less than 1
MessageBox.Show("Time for (114) is up!!!") 'show a message or change the text of a label to allert that time is up
Exit Sub
End If
pb114.Value = value114 'set the progressbar to new amount of seconds
If Not Route114Deadline AndAlso pb114.Value <= pb114.Maximum * 0.7 Then
Route114Deadline = True
lb114.ForeColor = Color.Red
End If
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
'get the number of seconds from now to end
Dim secs As Integer = GetTimeDifference(DateTimePicker1.Value, DateTime.Now)
'make sure there is at least 1 second before setting the progressbar maximum and starting the timer
If secs > 0 Then
pb114.Maximum = secs
tm114.Interval = 500
tm114.Start()
Else
MessageBox.Show("The chosen deadline time has already passed")
End If
End Sub
End Class

Issue with Progressbar in VB2012

I am trying to add a ProgressBar to my program. The program basically compares two values of time and when the values are equal a MessageBox appears to indicate that time is up. I need the ProgressBar to load based on the time difference of the two values. One of the values in a clock and the other is input by the user (similar to an alarm).
My code:
Imports System.Net.Mime.MediaTypeNames
Public Class Form1
Private hour As Integer = 0
Private minute As Integer = 0
Private second As Integer = 0
Public Sub show_time()
second += 1
If second = 59 Then
second = 0
minute += 1
If minute = 59 Then
minute += 1
hour += 1
End If
End If
Label3PrgressStdPC.Text = hour.ToString.PadLeft(2, "0") & ":"
Label3PrgressStdPC.Text &= minute.ToString.PadLeft(2, "0") & ":"
Label3PrgressStdPC.Text &= second.ToString.PadLeft(2, "0")
Label3PrgressStdPC.Refresh()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
show_time()
If TextBox1.SelectedText = TextBox1.Text Then Exit Sub
If TextBox1.Text = Label3PrgressStdPC.Text Then
Timer1.Stop()
MsgBox("time is up")
End If
End Sub
Private Sub Bn_start_St01_Click(sender As Object, e As EventArgs) Handles Bn_start_St01.Click
Timer1.Start()
Timer1.Enabled = True
Timer2.Start()
Timer2.Enabled = True
End Sub
**Private Sub ProgressBar1_Click(sender As Object, e As EventArgs) Handles ProgressBar1.Click
ProgressBar1.Maximum = , the max progrssbr will be determine by user input
ProgressBar1.Minimum = 0**
End Sub
**Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
progresbar1.value = ,Not so sure how to write the logic here**
End Sub
End Class
Can anyone help me out i am really getting frustrated.....thanks
How about something like this...
Private Ticker As Timer = New Timer 'Create a timer
Private Start As DateTime 'Store when we start
Private Expire As DateTime 'and when we end
'Call this to get things going
Sub Begin(EndHour As Integer, EndMinute As Integer, EndSecond As Integer)
Start = DateTime.Now
'If input is a time today ...
Expire = DateTime.Now.Date.Add(New TimeSpan(EndHour, EndMinute, EndSecond))
'or just a number of hours/mins/secs from now...
Expire = DateTime.Now.Add(New TimeSpan(EndHour, EndMinute, EndSecond))
'When the timer fires, call Tick()
AddHandler Ticker.Elapsed, Sub() Tick()
Ticker.Enabled = True
Ticker.Interval = 1000
Ticker.Start
End Sub
Private Sub Tick()
If DateTime.Now < Expire Then
'Not Finished
Dim Elapsed = DateTime.Now.Subtract(Start)
Dim TotalMillis = Expire.Subtract(Start).TotalMilliseconds
Dim ProgressDouble = Elapsed.TotalMilliseconds / TotalMillis
'Me.Invoke is used here as the timer Tick() occurs on a different thread to the
'one used to create the UI. This passes a message to the UI telling it to
'update the progress bar.
Me.Invoke(Sub()
ProgressBar1.Value = CInt(ProgressDouble * ProgressBar1.Maximum)
Label3PrgressStdPC.Text = Elapsed.ToString
End Sub)
Else
'Done
MessageBox.Show("Done")
Ticker.Stop
End If
End Sub
See VB.NET Delegates and Invoke - can somebody explain these to me? for more information on Invoking.

VB.NET Timer Interval 1 = 1 millisecond?

I Got a Timer With Interval = 1
Interval 1 = 1 millisecond ?
if Interval 1 is not 1 millisecond ,So tell me which control interval = 1ms
code:
Imports System.Globalization
Public Class Form1
'Default Time To Start From
Dim time As String = "00:00:00,000" 'Start From Here
'Label
Private Sub Label1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Label1.Click
Timer1.Start() 'Run The Timer On Click
End Sub
'TIMER
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
'Timer Interval = 1
Dim ci = CultureInfo.InvariantCulture
Dim original As TimeSpan = TimeSpan.ParseExact(time, "hh\:mm\:ss\,fff", ci) 'ParseExact
Dim difference As TimeSpan = TimeSpan.FromMilliseconds(1) ' = 1 Millisecond
Dim final = original
final = original + difference ' connect between original to difference !(00:00:00,000 + 1 MS = 00:00:00,001)!
Dim output As String = final.ToString("hh\:mm\:ss\,fff", ci) 'convert to the format ( = 00:00:00,001 ) (Back It To The Right Format)
time = output '!!Update!! the Time String from 00:00:00,000 To 00:00:00,001 |||| And in the Next Time 00:00:00,001 + 1 = 00:00:00,002
Label1.Text = time 'Show the Time String in the label
End Sub
End Class
As you see - Im Useing Regular Timer With Interval 1 , But i think that it worng because timer not counting milliseconds
if you got advice ,tell me.
The description of the interval property from MSDN is:
Gets or sets the time, in milliseconds, before the Tick event is
raised relative to the last occurrence of the Tick event.
However (as Steve pointed out in his comment):
The Windows Forms Timer component is single-threaded, and is limited
to an accuracy of 55 milliseconds. If you require a multithreaded
timer with greater accuracy, use the Timer class in the System.Timers
namespace
Taken from http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
The System.Timers.Timer class referred to is described here: http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
Imports System.Globalization
Public Class Form1
Dim time As String = "00:00:00:00"
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Timer1.Start()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim ci = CultureInfo.InvariantCulture
Dim original As TimeSpan = TimeSpan.ParseExact(time, "dd\:hh\:mm\:ss", ci) 'ParseExact
Dim difference As TimeSpan = TimeSpan.FromSeconds(1) ' = 1 Millisecond
Dim final = original
final = original + difference ' connect between original to difference !(00:00:00,000 + 1 MS = 00:00:00,001)!
Dim output As String = final.ToString("dd\:hh\:mm\:ss", ci) 'convert to the format ( = 00:00:00,001 ) (Back It To The Right Format)
time = output '!!Update!! the Time String from 00:00:00,000 To 00:00:00,001 |||| And in the Next Time 00:00:00,001 + 1 = 00:00:00,002
Label1.Text = time 'Show the Time String in the label
End Sub
End Class
Here's working script. IDK why it works but it works! :)