i will be grateful if somebody will explain to me why this code that i wrote is not executing on another thread (the code just executing Button4_Click sub without any exception). if i`m calling timerclass() sub from the main thread everything is running perfect.
i cant understand this behavior.. maybe i need to use delegate(?)
Private t As System.Threading.Thread
Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
t = New System.Threading.Thread(AddressOf timerclass)
t.Start()
End Sub
Private TargetDT As DateTime
Private CountDownFrom As TimeSpan = TimeSpan.FromMinutes(3)
Private Sub timerclass()
tim = New Timer
AddHandler tim.Tick, AddressOf tim_Tick
tim.Interval = 500
tim.Interval = 500
TargetDT = DateTime.Now.Add(CountDownFrom)
tim.Start()
End Sub
Private Sub tim_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim ts As TimeSpan = TargetDT.Subtract(DateTime.Now)
If ts.TotalMilliseconds > 0 Then
Label2.Text = ts.ToString("mm\:ss")
Else
Label2.Text = "00:00"
tim.Stop()
MessageBox.Show("Done")
End If
End Sub
Related
I am trying to start and stop a timer and it keeps telling me that they are both not a member of System.Windows.Forms.Timer
Below is my code. I used this in a different windows project and it works fine, don't know if I have to initialize it differently in this project.
Private Sub InitializeComponent()
Me.timer = New System.Windows.Forms.Timer
End Sub
Private Sub timer_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles timer.Tick
counter -= 1
If counter = 0 Then
timer.Stop()
lblPrice.Text = "$0.00"
lblBarcode.Text = "Place Item Near Scanner"
lblName.Text = ""
lblID.Text = ""
lblUnit.Text = "Price Checker"
counter = 8
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
timer = New System.Windows.Forms.Timer
AddHandler timer.Tick, AddressOf Me.timer_Tick
timer.Interval = 1000
timer.Start()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Process.Start("https://google.com/")
'I want to wait here for 20 seconds. Then I want him to open the yandex.
Process.Start("https://yandex.com/")
End Sub
Can you help me how can I do. Thanks for all.
In this example you can see how start a process after a specific delay.
In your case is launch a browser but you can use in other scenarios as a technique delay related.
Take a look on code and improve it based in your necessities.
Event TimerDone()
Private Sub ProcessStartAfterDelay(Optional ByVal url As String = "", Optional interval As Integer = 2000)
Dim tmr As System.Windows.Forms.Timer = New System.Windows.Forms.Timer
AddHandler tmr.Tick, Sub()
tmr.Stop()
tmr.Dispose()
If url.Length > 0 Then
Try
Process.Start(url)
RaiseEvent TimerDone()
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End If
End Sub
tmr.Interval = interval
tmr.Start()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim index As Integer = 0
Dim delay As Integer = 3000
Dim listOfUrls As List(Of String) = New List(Of String)({"https://google.com/",
"https://yandex.com/",
"https://it.yahoo.com/",
"https://www.wikipedia.org/"})
' First launch need to be here with delay > 0 important!
ProcessStartAfterDelay(listOfUrls(index), 1)
AddHandler Me.TimerDone, Sub()
index += 1
If index < listOfUrls.Count Then
ProcessStartAfterDelay(listOfUrls(index), delay)
Else
RemoveHandler Me.TimerDone, Nothing
End If
End Sub
End Sub
I have a small VB.net app that has a LOT of buttons. I need to change the back color of the buttons when they are clicked and then set it back to its original color after a duration of 10 seconds. I am struggling with either using a timer or the time process both of which have their own issues.
Any ideas to make this work and work efficiently?
Code:
Private Sub MyButtons_Click(sender As Object, e As EventArgs) _
Handles Button1.Click,
Button2.Click
Dim myButton = DirectCast(sender, Button)
MakeCall()
myButton.BackColor = Color.Green
'TurnOnActiveCallCOLOR.Enabled = True
For i As Integer = 0 To 10000 - 1
Threading.Thread.Sleep(10000)
Next
myButton.BackColor = Color.FromArgb(64, 64, 64)
End Sub
Here is an example of using a Windows Forms Timer to accomplish what you need:
Private MyButton As Button
Private Sub MyButtons_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
MyButton = DirectCast(sender, Button)
MyButton.BackColor = Color.Green
Timer1.Enabled = True
MakeCall()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
MyButton.BackColor = Color.FromArgb(64, 64, 64)
Timer1.Enabled = False
End Sub
To handle an arbitrary number of buttons, you could have something like this:
Public Class Form1
Dim buttonTimers As New Dictionary(Of Control, ButtonTimer)
Class ButtonTimer
Property Timeout As Integer = 2000
Property Target As Control
Property ActiveColor As Color = Color.Green
Property DefaultColor As Color = Color.FromArgb(64, 64, 64)
Private tim As Timer
Sub TimTick(sender As Object, e As EventArgs)
tim.Stop()
Target.BackColor = DefaultColor
End Sub
Sub New()
' empty constructor
End Sub
Sub New(target As Control)
Me.Target = target
Me.Target.BackColor = Me.ActiveColor
tim = New Timer With {.Interval = Timeout}
AddHandler tim.Tick, AddressOf TimTick
tim.Start()
End Sub
Sub Restart()
Target.BackColor = Me.ActiveColor
If tim IsNot Nothing Then
tim.Stop()
tim.Start()
End If
End Sub
Public Sub DisposeOfTimer()
If tim IsNot Nothing Then
tim.Stop()
RemoveHandler tim.Tick, AddressOf TimTick
tim.Dispose()
End If
End Sub
End Class
Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
Dim myButton = DirectCast(sender, Button)
'MakeCall()
If buttonTimers.ContainsKey(myButton) Then
buttonTimers(myButton).Restart()
Else
buttonTimers.Add(myButton, New ButtonTimer(myButton))
End If
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
For Each x In buttonTimers
x.Value.DisposeOfTimer()
Next
End Sub
End Class
If a button is clicked again before the timeout, the time is restarted.
You can add other constructors if you want to have a different timeout/colours for different buttons.
The MyBase.FormClosing code should be included in your form closing handler (if there is one) so that the timers are cleaned up properly.
I expect it would be tidier overall to make your own custom button class which inherits from Button, so you might want to investigate doing that. (How to: Inherit from Existing Windows Forms Controls.)
asynh and await if you don't want use timer. Simple method using task.delay
Private Async Sub ButtonClick(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click, Button3.Click, Button4.Click
DirectCast(sender, Button).BackColor = Color.Red 'after click setcolor to red
Await setColorAfterDelay(sender) 'Add this comand on button click and don't forget add asynh before sub in this method
End Sub
Public Async Function setColorAfterDelay(sender As Object) As Task
Await Task.Delay(1000) ''Milisecound how long you wana dealey
DirectCast(sender, Button).BackColor = Color.White 'and set colorto white
End Function
With the use of lambda expressions (and a lookup table if you want to interact with it further) you can do this pretty easily:
'Lookup table for if you want to be able to interact with the timers even more.
Dim ButtonTimers As New Dictionary(Of Button, Timer)
Private Sub MyButtons_Click(sender As Object, e As EventArgs) _
Handles Button1.Click, Button2.Click
MakeCall()
Dim myButton = DirectCast(sender, Button)
myButton.BackColor = Color.Green
'If a timer already exists for the button, restart it.
Dim existingTimer As Timer = Nothing
If ButtonTimers.TryGetValue(myButton, existingTimer) Then
existingTimer.Stop()
existingTimer.Start()
Return 'Do not execute the rest of the code.
End If
'Create the timer and set its Interval to 10000 ms (10 seconds).
Dim buttonTimer As New Timer() With {.Interval = 10000}
'Add a handler to its Tick event.
AddHandler buttonTimer.Tick, _
Sub(tsender As Object, te As EventArgs)
myButton.BackColor = Color.FromArgb(64, 64, 64)
'Dispose timer and remove from lookup table.
ButtonTimers.Remove(myButton)
buttonTimer.Stop()
buttonTimer.Dispose()
End Sub
ButtonTimers.Add(myButton, buttonTimer)
buttonTimer.Start()
End Sub
If you want to interact with a button's timer (if one exists) you can do:
Dim buttonTimer As Timer = Nothing
If ButtonTimers.TryGetValue(yourButtonHere) Then
'Do something with buttonTimer...
End If
This could probably be done better by dynamically creating timer controls so each button has it's own timer but here is what I came up with.
Oh, an set your timer to 1000 interval and enabled to 'False'.
Public Class Form1
Dim T1 As Integer = 0
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
T1 = T1 + 1
If T1 = 10 Then
For Each button In Controls
button.backcolor = Color.FromArgb(225, 225, 225)
Next
Timer1.Stop()
T1 = 0
End If
Me.Text = T1
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.BackColor = Color.Red
Timer1.Start()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Button2.BackColor = Color.Orange
Timer1.Start()
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Button3.BackColor = Color.Yellow
Timer1.Start()
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Button4.BackColor = Color.Green
Timer1.Start()
End Sub
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
Button5.BackColor = Color.Blue
Timer1.Start()
End Sub
End Class
I am running a BackgroundWorker, and want to report its progress. In the example below I create a test list which the BackgroundWorker then iterates through. The problem lies in the line 'sender.ReportProgress(i)'. If I have Option Strict on, it does not like my use of 'i' due to Late Binding issues. Is there any alternative way to code this and avoid that issue?
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
' Configuring for Background Workers
Control.CheckForIllegalCrossThreadCalls = False
Dim MyList As New List(Of String)
For a As Integer = 0 To 100
MyList.Add(CStr(a))
Next
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim bgw As New System.ComponentModel.BackgroundWorker
bgw.WorkerReportsProgress = True
bgw.WorkerSupportsCancellation = True
AddHandler bgw.DoWork, AddressOf bgw_DoWork
' I create a BackgroundWorker here rather than add one in the toolbox so that I can specify the Handler and use different Handler routines for different part of a large program.
Button1.Enabled = False
Dim progress As New Progress(bgw)
progress.ShowDialog()
Button1.Enabled = True
End Sub
Private Sub bgw_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs)
For i = 0 To MyList.Count -1
Label1.Text = MyList(i)
sender.ReportProgress(i)
System.Threading.Thread.Sleep(200)
Label1.Refresh()
Next
End Sub
End Class
Public Class Progress
Private WithEvents _BGW As System.ComponentModel.BackgroundWorker
Public Sub New(ByVal BGW As System.ComponentModel.BackgroundWorker)
_BGW = BGW
InitializeComponent()
End Sub
Private Sub frmProgress_Shown(sender As Object, e As System.EventArgs) Handles Me.Shown
If Not IsNothing(_BGW) Then
_BGW.RunWorkerAsync()
End If
End Sub
Private Sub _BGW_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles _BGW.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
Label1.Text = e.ProgressPercentage
End Sub
Private Sub _BGW_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles _BGW.RunWorkerCompleted
Me.Close()
End Sub
End Class
CType(sender, BackgroundWorker).ReportProgress(i)
Also, if you want to do multiple actions with it, then create a local reference variable like this:
Private Sub bgw_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs)
Dim bgw As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
' ... now you can use "bgw" multiple times below instead of casting each time ...
For i = 0 To MyList.Count -1
Label1.Text = MyList(i)
bgw.ReportProgress(i)
bgw.SomethingElse()
bgw.MoreStuff()
System.Threading.Thread.Sleep(200)
Label1.Refresh()
Next
End Sub
Obviously this isn't necessary in your case, just an FYI...
I am stuck updating a progressbar from a different thread.
I did get it running in the simplest way, but then cleaning the code gets me stuck.
My testing code looks like all the examples on the web related to backgroundworker and BeginInvoke.
FormP is the Progressbar-Form.
This works:
Public Class Form1
Private Delegate Sub delegate_ProgressUpdate(ByVal paramValue As Integer,
ByVal paramMax As Integer)
Private Sub Button1_Click(sender As System.Object,
e As System.EventArgs) Handles Button1.Click
' Test 01:
' Show Progressbar via BGW
' All functions are within Form1
Dim bgw As New BackgroundWorker()
AddHandler bgw.DoWork, AddressOf BGW_Sample01_DoWork
FormP.Show(Me)
bgw.RunWorkerAsync()
End Sub
Private Sub invokeMe_ProgressUpdate(ByVal paramValue As Integer, ByVal paramMax As Integer)
FormP.ProgressBar1.Maximum = paramMax
FormP.ProgressBar1.Value = paramValue
FormP.ProgressBar1.Update()
End Sub
Private Sub BGW_Sample01_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
For i As Integer = 1 To 10
Threading.Thread.Sleep(500) ' Test delay
Me.BeginInvoke(New delegate_ProgressUpdate(AddressOf invokeMe_ProgressUpdate),
i, 10)
Next
MessageBox.Show("Fertig")
End Sub
If I try to make things work more orderly encapsulated in FormP, it doesn't work.
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Dim bgw As New BackgroundWorker
AddHandler bgw.DoWork, AddressOf BGW_Sample02_DoWork
FormP.Show(Me)
bgw.RunWorkerAsync()
End Sub
Private Sub BGW_Sample02_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
For i As Integer = 1 To 10
Threading.Thread.Sleep(500)
FormP.SetProgress(i, 10)
Next
MessageBox.Show("Fertig")
End Sub
' ########## FormP #################
Public Class FormP
Private Delegate Sub delegate_ProgressUpdate(ByVal value As Integer, ByVal maximum As Integer)
Public Sub SetProgress(ByVal paramValue As Integer, ByVal paramMaximum As Integer)
If Me.InvokeRequired Then
Me.Invoke(New delegate_ProgressUpdate(AddressOf Me.SetProgress), paramValue, paramMaximum)
Else
Me.ProgressBar1.Maximum = paramMaximum
Me.ProgressBar1.Value = paramValue
Me.ProgressBar1.Update()
End If
End Sub
End Class
FormP does not freeze, but UI is not updated.
Actually Me.InvokeRequired is false and I think that's where I begin to miss some important parts.
I tried Form1.InvokeRequired here, but it's false as well.
My understanding is: the calling thread here is the bgw thread, no matter in what class the code is that this thread calls...
That seems not to be it?
Thanks for any thoughts.
What worked eventually:
Private frmP As FormP
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Dim bgw As New BackgroundWorker
If Me.frmP IsNot Nothing AndAlso Me.frmP.Visible Then Return
Me.frmP = New FormP
Me.frmP.Show(Me)
AddHandler bgw.DoWork, AddressOf BGW_Sample02_DoWork
bgw.RunWorkerAsync(New Object() {Me.frmP})
End Sub
Private Sub BGW_Sample02_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
Dim objFrmP As FormP = DirectCast(e.Argument(0), FormP)
For i As Integer = 1 To 10
objFrmP.setProgress(i, 10)
Threading.Thread.Sleep(500)
Next
MessageBox.Show("Finished")
End Sub
The Progress-Dialog-Code in FormP:
Public Sub setProgress(paramValue As Integer, paramMaximum As Integer)
If Me.InvokeRequired Then
' defining a delegate type is not really necessary
Me.Invoke(Sub() Me.setProgress(paramValue, paramMaximum))
Else
Me.ProgressBar1.Maximum = paramMaximum
Me.ProgressBar1.Value = paramValue
Me.ProgressBar1.Update()
End If
End Sub