I am trying to create a timer that will countdown from the specified time.
The user enters a time and clicks a button.
The button click opens a second form that has a timer in it.
Every time the timer ticks, the time decreases and the time left is displayed in a textbox on form2 (textbox.text = timeLeft).
However, the textbox will never actually update. It remains blank, and the only time that assigning a new value to the .text property will actually work is if I raise an event (for example clicking a button that will change the .text property of the textbox)
*Here is the code for the timer class
Public Class CountdownTimer
Private timeAtStart As Integer
Private timeLeft As Integer
Public Sub StartTimer(ByVal time As Integer)
timeAtStart = time
timeLeft = timeAtStart
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
If timeLeft > 0 Then
timeLeft = timeLeft - 1
txtTimeLeft.Text = timeLeft.ToString
Else
Timer1.Stop()
txtTimeRemaining.Text = "Time!"
txtTimeRemaining.ForeColor = Color.Red
End If
End Sub
End Class
And here is how I call it:
Dim timer As New CountdownTimer
timer.Show()
CountdownTimer.StartTimer(CInt(txtSetTime.Text))
Your code is calling the (form) class not the instance, and I cant see where Timer1 is properly referenced for an independant reusable class. Here is one way to implement a CountDown class that will work with other forms....
Friend Class CountdownTimer
Private timeAtStart As Integer
Private timeLeft As Integer
Private WithEvents Timer1 As New Timer
Private txtTimeLeft as TextBox
Public Sub New(TargetTB as TextBox)
txtTimeLeft= TargetTB
End Sub
Public Sub StartTimer(ByVal time As Integer, timeLength as Integer)
timeAtStart = time
timeLeft = timeLength
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs)_
Handles Timer1.Tick
' just dislaying time left
If timeLeft > 0 Then
timeLeft = timeLeft - 1
txtTimeLeft.Text = timeLeft.ToString
Else
Timer1.Stop()
txtTimeLeft.Text = "Time!"
txtTimeLeft.ForeColor = Color.Red
End If
End Sub
End Class
How to use it:
Dim CountDn As New CountdownTimer(frm.TextBoxToUse)
' use the INSTANCE name not the class name!!!!
'CountdownTimer.StartTimer(CInt(txtSetTime.Text))
CountDn.StartTimer(CInt(txtSetTime.Text))
If it displays the result after the timer has completed, i think you should use the
Application.DoEvents()
method to see the update immediately. It actually works with Windows Forms. What have you tried, so i can help further
You do realize that when you are counting down you are setting a different textbox than when it is complete, right?
txtTimeLeft.Text
VS
txtTimeRemaining.Text
Note: Timers run on the same thread as the UI so if you computer (or program) gets busy, the timer will NOT tick at exact intervals. If you are worried about small variances in your timer, you should compare the difference of your computer time during each tick event to determine how much time had passed.
Dim TS = TimeSpan = Now.Subtract(StartingTime)
Try refreshing the text boxes after each update:
So after
txtTimeLeft.Text = timeLeft.ToString
Add
txtTimeLeft.Refresh
This is your problem:
Dim timer As New CountdownTimer
timer.Show()
CountdownTimer.StartTimer(CInt(txtSetTime.Text))
You instantiate a new object called timer, but then start the timer on the CountdownTimer object
You need to change your code to this:
Dim timer As New CountdownTimer
timer.Show()
timer.StartTimer(CInt(txtSetTime.Text))
Related
I have a small media player application I've programmed which works really well.
I've even managed an on screen remote control (obviously just a Form with Buttons made to look like a virtual remote control) and yes I've also integrated an Arduino to be able to use a real remote control, but forget the Arduino remote control just for now my question is about the Virtual remote control I'm trying to build.
Programming a Button is quite elementary: it's one of the first things we learn in Vb.net, but that's just in a single click scenario.
If I look at a real TV remote, I can either click (most common with volume buttons) the volume up or down and the response is either that the volume goes up or down by one unit, or I can hold the same button down and it repeats the process changing the unit several times up or down depending on how long before I release the button again.
How would I achieve this?
I have tried googling this but everything I get back as a search result talks about a physical mouse button as apposed to what I need which is a Button control.
An example of an UserControl that provides the functionality of Up/Down spin buttons.
It uses two standard Buttons, a Label and a Timer.
To set it up, add a new UserControl to the Project:
Set its BackColor to Color.Transparent
Add two Buttons, select both and add event handlers to the MouseDown, MouseUp, KeyDown and KeyUp events (selecting both, you'll add 4 method)
Two Unicode chars (U+25B2 and U+25BC) are used to show the arrows. Setup the Buttons' Font size and Color as required (the sample UC uses Segoe UI as Font).
Anchor the upper Button to Left/Top/Right
Anchor the lower Button to Left/Bottom/Right
Anchor the Label to Left/Right
A Timer is created in the UC constructor, its Interval value set to 300. This value represents the initial speed of the increment when a mouse Button or keyboard key is held down. It's decremented each time the Timer ticks until it reaches a threshold defined by the UC's Speed public property (which is internally limited to the (1:10) range).
The maximum and minimum increment are defined by the UC's Min and Max public properties
The Value public property gets or sets the current increment.
The Timer is started when a Mouse Button or a Keyboard key are pressed and stopped when they're released. When the Timer.Tick event is raised, the Timer.Interval is decreased by 25ms until the max Speed value is reached. Since the initial value is set to 300ms and the maximum Speed is limited to 10, the minimum Interval value is 50ms, which is close to the System.Windows.Form.Timer official resolution.
The minimum Interval needs to be considered if these values are changed, to avoid overlapping Tick events.
This is how it works:
Imports System.Windows.Forms
Public Class SpinButtons
Private buttonsTimer As Timer = Nothing
Private timerThrottle As Integer = 0
Private timerIncrement As Integer = 0
Private m_Speed As Integer = 10
Private m_Value As Integer = 0
Public Sub New()
InitializeComponent()
Me.components = New System.ComponentModel.Container()
buttonsTimer = New Timer With {.Interval = 300}
Me.components.Add(buttonsTimer)
End Sub
Public Property Max As Integer = 100
Public Property Min As Integer = 0
Public Property Speed As Integer
Get
Return m_Speed
End Get
Set
m_Speed = Math.Max(Math.Min(Value, 10), 1)
End Set
End Property
Public Property Value As Integer
Get
Return m_Value
End Get
Set
m_Value = Value
SetIncrementValue()
End Set
End Property
Private Sub buttonsTimer_Tick(sender As Object, e As EventArgs)
SetIncrementValue()
If timerThrottle <= m_Speed Then
timerThrottle += 1
buttonsTimer.Interval -= 25
End If
End Sub
Private Sub btnUp_MouseDown(sender As Object, e As MouseEventArgs) Handles btnUp.MouseDown, btnDown.MouseDown
ButtonPressed(DirectCast(sender, Button))
SetIncrementValue()
End Sub
Private Sub btnUp_MouseUp(sender As Object, e As MouseEventArgs) Handles btnUp.MouseUp, btnDown.MouseUp
ButtonReleased(DirectCast(sender, Button))
End Sub
Private Sub btnUp_KeyDown(sender As Object, e As KeyEventArgs) Handles btnUp.KeyDown, btnDown.KeyDown
ButtonPressed(DirectCast(sender, Button))
SetIncrementValue()
End Sub
Private Sub btnUp_KeyUp(sender As Object, e As KeyEventArgs) Handles btnUp.KeyUp, btnDown.KeyUp
ButtonReleased(DirectCast(sender, Button))
End Sub
Private Sub SetIncrementValue()
m_Value += timerIncrement
m_Value = Math.Max(Math.Min(m_Value, Max), Min)
lblCounter.Text = m_Value.ToString()
End Sub
Private Sub ButtonPressed(btn As Button)
btn.ForeColor = Color.LawnGreen
timerIncrement = If(btn Is btnUp, 1, -1)
buttonsTimer.Enabled = True
End Sub
Private Sub ButtonReleased(btn As Button)
buttonsTimer.Enabled = False
buttonsTimer.Interval = 300
timerThrottle = 0
timerIncrement = 0
btn.ForeColor = Color.White
End Sub
Protected Overrides Sub OnFontChanged(e As EventArgs)
MyBase.OnFontChanged(e)
Me.btnUp.Font = Me.Font
Me.btnDown.Font = Me.Font
End Sub
Protected Overrides Sub OnResize(e As EventArgs)
MyBase.OnResize(e)
Me.MinimumSize = New Size(CInt(Me.Font.Size * 2), Me.btnUp.Height + Me.btnDown.Height + lblCounter.Height)
End Sub
End Class
I created a class that inherits a timer class because I want to customize the Tick function, and I want to use this specific function in many classes without the need to change the function in all the timers every time.
Public Class FadeInTimer
Inherits Timer
Public Sub New()
MyBase.New()
Me.Enabled = False
Me.Interval = 75
End Sub
Private Sub FadeInTimer_Tick(sender As Object, e As EventArgs) Handles Me.Tick
Dim workingAreaWidth As Integer = Screen.PrimaryScreen.WorkingArea.Width - Me.Width
Me.Opacity += 0.1
If Not Me.Location.X <= workingAreaWidth Then
Me.Location = New Point(Me.Location.X - 30, Me.Location.Y)
End If
Me.Refresh()
If Me.Opacity = 1 Then
Me.Stop()
End If
End Sub
End Class
The purpose of this function is to make a simple fade in when the form is created. The problem is I can't use "Me." because I am in the Timer class, so, how can I make changes to the form from this class.
The first thing to do is to pass an instance of the form to be faded in inside the constructor of the custom timer, save that instance in a global class variable and add the tick handler with AddHandler like so
Public Class FadeInTimer
Inherits System.Windows.Forms.Timer
Dim parent As Form
Public Sub New(p As Form)
MyBase.New()
parent = p
AddHandler MyBase.Tick, AddressOf FadeInTimer_Tick
End Sub
Now, when you need to refer to the 'parent' form you use the parent variable and not the Me statement. Also, every time you need to refer to the timer, you should use MyBase statement
Private Sub FadeInTimer_Tick(sender As Object, e As EventArgs)
Dim workingAreaWidth As Integer = Screen.PrimaryScreen.WorkingArea.Width - Parent.Width
parent.Opacity += 0.1
If Not parent.Location.X <= workingAreaWidth Then
parent.Location = New Point(parent.Location.X - 30, parent.Location.Y)
End If
parent.Refresh()
If parent.Opacity = 1 Then
MyBase.Stop()
End If
End Sub
This could be tested in LinqPad using this code
Sub Main
Dim f As Form = New Form()
Dim t As FadeInTimer = New FadeInTimer(f)
f.Opacity = 0
t.Interval = 150
t.Start()
f.ShowDialog()
End Sub
I'm trying to make a "rotator" on a form that cycles through a series of urls and displays the url in the WebBrowser control. The following code displays my form, but the form remains white/blank and then the last url in the array appears after a while. When I put a MessageBox in-between each url, to create a stop, it works and each url appears. I've tried putting a Sleep in place of the MessageBox, but that didn't work. I've also tried increasing the Sleep time, but that didn't work either. How can I make it work correctly?
Sub Rotate()
Dim Urls() As String = {"www.stackoverflow.com", "www.google.com", "www.yahoo.com"}
Dim counter As Integer = 0
Form3.Show()
Do Until counter = 3
Form3.WebBrowser1.ScriptErrorsSuppressed = True
Form3.WebBrowser1.Navigate(Urls(counter))
'MessageBox.Show("Next")
counter = counter + 1
System.Threading.Thread.Sleep(2000)
Loop
End Sub
You can call Application.DoEvents after changing the URL so that the control gets the chance to redraw itself.
However, a better approach would be to use a timer which fires every 2 seconds and then change the URL in the event handler so that your UI keeps responsive.
For example setup a new field myTimer in your form, init it in your form's loading event and in the Tick event you call your Rotate method. As Rotate is now called several times, we have to move the counter variable out of the method and make it a field so that we keep its value between the invocations. I usually write C# so hopefully I did not make some typos below :)
Private WithEvents myTimer As System.Windows.Forms.Timer
Private counter As Integer
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
' ... your existing code ...
counter = 0
myTimer = New System.Windows.Forms.Timer
myTimer.Interval = 2000
myTimer.Enabled = True
myTimer.Start()
End Sub
Private Sub myTimerTick() Handles myTimer.Tick
Rotate()
End Sub
Sub Rotate()
Dim Urls() As String = {"www.stackoverflow.com", "www.google.com", "www.yahoo.com"}
WebBrowser1.ScriptErrorsSuppressed = True
WebBrowser1.Navigate(Urls(counter))
counter = counter + 1
If counter > 3 Then myTimer.Stop()
End Sub
I want to specify in a text field how many timers I want to add to my form and specify the code that should be into the timer.
For instance: My textbox says "2" and then I click a button and it creates two timers and adds a specific source code for both timers.
I have tried different codes and while they worked, I wasn't able to specify the number of controls on a form to create.
How can I achieve this efficiently?
Thanks
Just to create one timer
Public Class Form1
private _timer as Windows.Forms.Timer
...
Public Sub New()
...
_timer = New Timer(Me)
_timer.Interval = 1000 'Timer will trigger one second after start
AddHandler _timer.tick, AddressOf Timer_tick 'Timer will call this sub when done
End Sub
Sub Button_click(sender as Object, e as EventArgs)
_timer.Start() 'Start the timer
...
End Sub
Private Sub Timer_tick(sender as Object, e as EventArgs)
MessageBox.Show("Timerrr!!")
End Sub
...
End Class
Now if you want to create more than one timer, you can use an array of Timer.
In this case, I used a form conatining a NumericUpDown controll element, a button and a label, plus two labels which only contain text.See this picture
To create the timers, I use the function add_timers(timercount), which looks like this:
Function add_timers(timercount As Integer)
'Using a loop to creat <timercount> timers
For g As Integer = 1 To timercount
'Creating new timer 't'
Dim t As New Timer()
'setting interval of t
t.Interval = 1000
'Enabling timer
t.Enabled = True
'Code which runs when t ticks
AddHandler t.Tick, AddressOf TimerTick
Next
End Function
This function gets called when Button1, the start button gets pressed. It uses NumericUpDown1.Value as the parameter for the function. The function uses a loop to create new timers t, sets their intervals and the code to run when they tick.
Unfourtunately, I didn't find a way to dynamically create code, so every timer performs the same action. Using arrays and loops in a clever way might enable you to use different value for each timer. To create code for the timer use a Sub:
Sub TimerTick(ByVal sender As Object, e As EventArgs)
'Add your code here
Label1.Text += 1
End Sub
The complete code I use is:
Public Class Form1
Function add_timers(timercount As Integer)
'Using a loop to creat <timercount> timers
For g As Integer = 1 To timercount
'Creating new timer 't'
Dim t As New Timer()
'setting interval of t
t.Interval = 1000
'Enabling timer
t.Enabled = True
'Code which runs when t ticks
AddHandler t.Tick, AddressOf TimerTick
Next
End Function
Sub TimerTick(ByVal sender As Object, e As EventArgs)
'Add your code here
Label1.Text += 1
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
add_timers(NumericUpDown1.Value)
End Sub
End Class
Packing the timers into an array is possible, that way you can easily access each timer with its index. Serach for it on the internet, and if you then have no idea of how to do it, tell me in the comments.
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