ReportProgress in BackgroundWorker - vb.net

The progress bar is repeated two or three times before completing the operation on my backgroundworker
this code:
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Load Data
For i As Integer = 0 To ListData.Count
BackgroundWorker1.ReportProgress((i / ListData.Count) * 100)
Next
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
End If
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'set datasource of DatagridView
ProgressBar1.Style = ProgressBarStyle.Blocks
End Sub
In my load form
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.RunWorkerAsync()
ProgressBar1.Style = ProgressBarStyle.Marquee
please help me

You have several mistakes. First, if you intend to display an increasing progress bar, you should use:
ProgressBar1.Style = ProgressBarStyle.Continuous
in your load form. Next, you are checking BackgroundWorker1.CancellationPending only after going through all your ListData. That is too late, you have to check it every iteration of the loop. I also really doubt you want your loop to go from 0 to ListData.Count; you probably either want to start at 1 or go to ListData.Count - 1. I can't tell from your question. Your loop should look more like this:
For i as Integer = 0 To ListData.Count - 1
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Exit For
Else
' You should be doing some work here, not just calling ReportProgress
BackgroundWorker1.ReportProgress(100 * (i+1) / ListData.Count)
End If
Next
Another mistake is calculating (i / ListData.Count) * 100; i and ListData.Count are integers, so their division will always be zero until the end, when it will be 1. Instead, multiply the numerator by 100 to get percentages.

Related

Add points when PictureBox1 hits PictureBox2

I'm trying to make a simple game using VB.Net and its concept is like egg catching.
My expectation is that when the egg is falling (PictureBox1), the catcher (PictureBox2) catches the egg and it gets 1 point every catch. My idea is when the location of egg matches the location of the catcher it adds point. But it didnt work. Any suggestion here?
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PictureBox1.Top += 5
If PictureBox1.Location.Y = PictureBox2.Location.Y Then
score += 1
Label1.Text = score
End If
End Sub
And this is the code for game over:
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
If PictureBox1.Location.Y > 400 Then
Me.Dispose()
MsgBox("game over")
End If
End Sub
Welcome to the site! I believe the Bounds property should work for this.
Also, may want to just use a boolean (or raise an event) for the collision, so this way you're not doing the work twice.
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PictureBox1.Top += 5
Dim itHappened as Boolean
Dim other_boxes as New List(Of PictureBox) from {PictureBox2} ', PictureBox3, PictureBox4}
For each box in other_boxes
If PictureBox1.Bounds.IntersectsWith(box.Bounds) Then
score += 1
Label1.Text = score
itHappened = True
else
itHappened = False 'depending on your logic, may not be the best place
End If
Next
If PictureBox1.Location.Y > 400 Then
Me.Dispose()
MsgBox("game over")
End If
End Sub

Multiple timer thread with single Datagridview

I am using VB.NET with Visual Studio 2017.
I am testing a form which has a datagridview. That grid is being populated with initial trading symbols. After loading symbols, I need to press Start button, which starts a timer1 to look for latest price of that symbol from exchange and start timer2. Timer2 will look for certain conditions of price, if met, will place an order to exchange and again look for another loop. While this is happening, timer1 is also running at 2 seconds interval to get latest trading price.
I am using two different BackgroundWorker for each timer to do these two tasks with grid.
This works almost fine, but sometimes, both or one of timers gets disabled.
I am looking for another good and best approach to this as far as code, design and architechure is concerned. If anyone can help.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Me.Timer1.Stop()
Try
With Me.DataGridView4
For RowIndex = 0 To .RowCount - 1
With .Rows(RowIndex)
'CODE FOR CHECKING LTP OF SYMBOL
End With
Next
End With
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker2_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker2.DoWork
Me.Timer2.Stop()
Try
For Each row As DataGridViewRow In Me.DataGridView4.Rows
Dim TRDSYM As String = row.Cells(1).Value
Dim orderid As String = row.Cells(6).Value
Dim Ltp As Decimal = row.Cells(2).Value
Dim EntryPrice As Decimal = Ltp
'PLACING AN ORDER TO EXCHANGE BASED ON CONDITION
Next
Catch ex As Exception
End Try
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'SetLog("Market Watch For Ltp Background Worker going on...", LogType.Others)
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
'SetLog("Order Placement Algorithm Background Worker going on...", LogType.Others)
BackgroundWorker2.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If Me.Timer2.Enabled = False Then
Timer2.Enabled = True
Me.Timer2.Start()
Me.btnOrderplacementstatus.Text = "Order Placement Running..."
Me.btnOrderplacementstatus.BackColor = Color.Green
End If
'SetLog("BackgroundWorker 2 Order Placement Thread started...", LogType.Others)
Me.Timer1.Enabled = True
Me.Timer1.Start()
Me.btnMarketwatchstatus.Text = "Market Watch Running..."
Me.btnMarketwatchstatus.BackColor = Color.Green
End If
End Sub
Private Sub BackgroundWorker2_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker2.RunWorkerCompleted
If Me.Timer2.Enabled = False Then
Me.Timer2.Enabled = True
Me.Timer2.Start()
Me.btnOrderplacementstatus.Text = "Order Placement Running..."
Me.btnOrderplacementstatus.BackColor = Color.Green
End If
End Sub

vb.net background worker cancel not working

I'm having an issue where BackgroundWorker.CancelAsync() isn't working. I have WorkerSupportsCancellation set to TRUE. I am also polling BackgroundWorker1.CancellationPending in DoWork. Here is sample code of what I am trying to achieve. I have background worker looping through a time stamp and assigning a value to Measurement variable. I have a subroutine that queries the last reported Measurement variable and writes to listbox. After 5 loops I send BackgroundWorker.CancelAsync(). I can see the cancellation is pending, but it doesn't actually cancel the background worker. Is this a race condition?
Public Class Form1
Dim Measurement As String
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
Delay(0.5)
For x = 1 To 5
ListBox1.Items.Add(Measurement)
ListBox1.TopIndex = ListBox1.Items.Count - 1
TextBox1.Text = BackgroundWorker1.CancellationPending
Delay(1)
Next
BackgroundWorker1.CancelAsync()
TextBox1.Text = BackgroundWorker1.CancellationPending
ListBox1.Items.Add("Cancel Pend: " & BackgroundWorker1.CancellationPending)
Delay(5)
ListBox1.Items.Add("Busy: " & BackgroundWorker1.IsBusy)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Sub
Else
Do
Measurement = Now()
Loop
End If
End Sub
End Class
You just need to move the check for the cancellation inside the Do...Loop otherwise it will be tested only at the start of the DoWork event handler and never after that
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Else
Do
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Do
Else
Measurement = Now()
End If
Loop
End if
End Sub

how to run this timer in vb.net?

I want a timer to run on a form whose value is derived from a database. for example when i store the value for the timer in a variable, i multiply it by 60000 to convert milliseconds to minutes. Here is the code:
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim x as integer
details = ds.Tables(0).Rows(0).Item("Time")
Timer1.Interval =details * 60000
Timer1.Enabled = True
End Sub()
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
seconds.Text = x.ToString
If x= 0 Then
Timer1.Enabled = False
MessageBox.Show("You didn't finish in time.", "Sorry")
Me.Close()
Else
x-= 1
End If
End Sub
When i run this code, the timer runs in the background and when time's up, the form closes. But it doesn't show in the label seconds when timer is ticking. I hope you get what I mean :)
If you want the label to tick the seconds down to 0, you need the timer interval to be 1000, no matter how long you want this to run for.
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
details = ds.Tables(0).Rows(0).Item("Time")
Timer1.Interval =1000
endTime = DateTime.Now.AddMinutes(details)
Timer1.Enabled = True
End Sub()
Dim endTime As DateTime
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If DateTime.Now > endTime Then
Timer1.Enabled = False
MessageBox.Show("You didn't finish in time.", "Sorry")
Me.Close()
Else
Seconds.Text = (DateTime.Now - endTime).ToString("hh\:mm\:ss")
End If
End Sub
This happens because your timer ticks at Minutes interval. If you want to show seconds in the label then your timer should tick within 1 second interval (maybe a few milliseconds etc.)
So instead of setting Timer1.Interval =details * 60000, set x = details * 60 and Timer1.Interval to 1000.
There is also variable scoping problem with your code. You have declared x as local variable inside Form1_Load, which means that it won't be available inside the Timer1_Tick. Move it outside the procedure.
Dim x As Integer
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Details = ds.Tables(0).Rows(0).Item("Time")
Timer1.Interval = 1000
x = Details * 60
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
seconds.Text = x.ToString
If x = 0 Then
Timer1.Enabled = False
MessageBox.Show("You didn't finish in time.", "Sorry")
Me.Close()
Else
x-= 1
End If
End Sub
UPDATED
This is assuming that your database saved value is in Milliseconds. Otherwise modify appropriately.
Dim ticker As TimeSpan
Public Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
Details = ds.Tables(0).Rows(0).Item("Time")
Timer1.Interval = 1000
ticker = TimeSpan.FromMilliseconds(Details)
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Timer1.Tick
seconds.Text = String.Format("{0:00}:{1:00}:{2:00}", ticker.Hours, ticker.Minutes, ticker.Seconds)
If ticker.Ticks <= 0 Then
Timer1.Enabled = False
MessageBox.Show("You didn't finish in time.", "Sorry")
Me.Close()
Else
ticker = ticker.Subtract(TimeSpan.FromSeconds(1))
End If
End Sub
This may not be completely what you may want, as I didn't convert the time or anything I just made it as basic as possiable and that way you should be able to work from it :)
Public Class Form1
Dim Time As Integer = 100 'Your time in seconds goes here!
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
lblTime.Text = Time
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Time = Time - 1
lblTime.Text = Time
If Time = 0 Then
Timer1.Enabled = False
MsgBox("You didn't finish in time.", "Sorry")
Me.Close()
End If
End Sub
End Class
Any problems just message me

How to use timer for for next loop in visual basic 2008

I have a case where i need to generate millions of unique codes. For this I have created a generate function where the random number is generated. I call this function from a for loop and add the generated number on a list box. my code is as follow
for i=1 to val(txtnumber.txt)
mynum=generate()
next
I have created a lable on form where i wanted to display the no of secs elapsed while processing the loop. I used timer control as
timer1.start()
for i=1 to val(txtnumber.text)
mynum=generate()
listbox1.items.add(mynum)
next
timer1.stop
and on timer1_tick function
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Label1.Text = Val(Label1.Text) + 1
End Sub
but when i click generate button, all numbers are generated, but timer doesnot shows time elapsed.
I may have missed something, so please help me out
This is probably best handled in a BackgroundWorker. Place one on the form and set its WorkerReportsProgress=True. Also, placing a million numbers in a ListBox probably isn't a good idea, so I omitted that.
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
Button1.Enabled = False
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim started As DateTime = Now
For i As Integer = 1 To val(txtnumber.txt)
mynum=generate()
BackgroundWorker1.ReportProgress(i, Nothing)
Next
Dim ended As TimeSpan = Now.Subtract(started)
BackgroundWorker1.ReportProgress(0, ended.TotalSeconds.ToString)
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
If e.UserState IsNot Nothing Then
Label1.Text = e.UserState.ToString()
Else
Label1.Text = e.ProgressPercentage.ToString
End If
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Button1.Enabled = True
End Sub
Your label should be updating correctly when the worker reports the ProgressChanged event.
What you're encountering is a threading issue. The work you are doing to generate the numbers is being executing by the UI thread, so it never gets a chance to update the screen. Take a look here: How to prevent UI from freezing during lengthy process?
This one might also have good information for you: Updating UI from another thread
Try this:
Private _Counter As Integer = 0
Private _StartTime As Date = Now
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
_StartTime = Now
_Counter = CInt(Val(txtnumber.Text))
ListBox1.Items.Clear()
Label1.Text = "0"
Timer1.Interval = 50
Timer1.Start()
End Sub
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
ListBox1.Items.Add(generate())
Label1.Text = New Date((Now - _StartTime).Ticks).ToString("HH:mm:ss.ff")
_Counter -= 1
If (_Counter <= 0) Then
Timer1.Stop()
End If
End Sub
Or you can research actual Threading.