Hello I am studying multithreading with join method
I am trying to create multiple threads and run thread in order.
I know that join method is able to join threads and run threads in order.
However, form is updating text box, but whole form is freezing so i can't click any button in the form
Here is code that I have..
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
Dim T_List As New ArrayList
Dim VALE As Integer = 99
For i = 0 To 2
VALE = i
t = New Thread(New ThreadStart(Sub() run_t(VALE, 10 + (5 * VALE))))
t.IsBackground = True
t.Start()
t.Join()
' T_List.Add(t)
Next
End Sub
Private Sub run_t(t_num As Integer, number As Integer)
For i = 0 To number
Thread.Sleep(100)
CounterBox.AppendText("thread : " & t_num & " : " & i.ToString() + vbNewLine)
If i Mod 2 = 0 Then
ComboBox1.Items.Add(i)
End If
Next
End Sub
I was checking "STATHREAD"
STATHREAD and main thread
and
https://msdn.microsoft.com/en-us/library/ms182351.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
however, STATHREAD seems not working for this case.
what can be the solution to use Join method for threads without freezing form..
In VB.Net Form, I would recommand you to use BackgroundWorker which will let you to:
launch a calculation thread
interact with your Form without blocking the UI thread.
BackgroundWorker
Thread.Join is a blocking method which should never be used in GUI event handlers.
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
Dim tt As New Thread(AddressOf working) : tt.Start()
End Sub
Sub working()
Dim T_List As New ArrayList
Dim VALE As Integer = 99
For i = 0 To 2
VALE = i
run_t(VALE, 10 + (5 * VALE))
' T_List.Add(t)
Next
End Sub
Sub run_t(t_num As Integer, number As Integer)
For i = 0 To number
Thread.Sleep(100)
CounterBox.AppendText("thread : " & t_num & " : " & i.ToString() + vbNewLine)
If i Mod 2 = 0 Then
ComboBox1.Items.Add(i)
End If
Next
End Sub
Related
Long time reader, first time poster. Usually I'm able to find the answer and make it work. Not this time..... I'm using VB.NET in VS2013. I am trying to update a progress bar with work done in a secondary thread. Easy right? No. I had to make it more complicated. The progress bar (ToolStripProgressBar1) is on the main form (frmMain), the MDI of the project. A secondary form (frmShipping) has a button which initiates a second thread to do some COMM Port communications in a class (cApex). I can get the progress bar to update on the frmMain from the main UI thread (frmShipping button).
This is the code from button on frmShiping and the multithread procedure:
Private Sub btnreadScanner_Click(sender As Object, e As EventArgs) Handles btnreadScanner.Click
Dim thrReadScanner As New System.Threading.Thread(AddressOf ReadScanner)
thrReadScanner.IsBackground = True
thrReadScanner.Start()
End Sub
Private Sub ReadScanner()
Dim strRowCount As String
ShipmentMsg(2)
strRowCount = objShipping.RecordsExisit.ToString()
Try
objApex.ImmediateMode()
If objApex.FileDownload = False Then
Throw New Exception(Err.Description)
End If
Catch ex As Exception
ShipmentMsg(1)
MessageBox.Show("No Data downloaded from Scanner. Try Again. Error#: " & Err.Number & " : " & Err.Description)
Exit Sub
End Try
RecordCount()
DataGridUpdate()
btnProcessShipment.Enabled = True
ShipmentMsg(5)
ScanErrors()
End Sub
This all works great. As expected. The call to objApex.FileDownload in class cApex is where progress bar is to be updated from (actually in another function called from FileDownload). So here is the code there.
Try
GetHeaderRecord()
If Count <> 0 Then intTicks = Math.Round((100 / Count), 1)
For intcount As Integer = 1 To Count
Dim intLength As Integer = Length
Do While intLength > 0
literal = Chr(_serialPort.ReadChar.ToString)
If literal = ">" Then Exit Do
strRecord = strRecord & literal
intLength = intLength - 1
Loop
REF = strRecord.Substring(0, 16).TrimEnd
SKID = strRecord.Substring(16, 16).TrimEnd
REEL_BC = strRecord.Substring(32, 15).TrimEnd
ScanDate = strRecord.Substring(47, 8).TrimEnd
ScanDate = DateTime.ParseExact(ScanDate, "yyyyMMdd", Nothing).ToString("MM/dd/yyyy")
ScanTime = strRecord.Substring(55, 6).TrimEnd
ScanTime = DateTime.ParseExact(ScanTime, "HHmmss", Nothing).ToString("HH:mm:ss")
strRecordTotal = strRecordTotal & strRecord & CRLF
Dim strSQL As String
strSQL = "INSERT INTO tblScanData (PONo,Barcode,SkidNo,ScanDate,ScanTime) " & _
"VALUES (" & _
Chr(39) & REF & Chr(39) & _
"," & Chr(39) & REEL_BC & Chr(39) & _
"," & Chr(39) & SKID & Chr(39) & _
"," & Chr(39) & ScanDate & Chr(39) & _
"," & Chr(39) & ScanTime & Chr(39) & ")"
objData.Executecommand(strSQL)
strRecord = ""
Next
And finally this is how I was calling the progress bar update.
Dim f As frmMain = frmMain
System.Threading.Thread.Sleep(100)
DirectCast(f, frmMain).ToolStripProgressBar1.PerformStep()
I really need to put the PerformStep in the For loop. Each time around the loop will step the progress bar the percentage of steps needed to make bar fairly accurate (done by the math code before loop). Also I setup the properties of the progress bar control on frmMain. So, am I crazy, or is there a way to accomplish this? I tried using a delegate; Me.Invoke(New MethodInvoker(AddressOf pbStep)) to make code cross thread safe. I don't get an error about cross thread calls, but the progress bar doesn't update either. Sorry it's a long one but I'm lost and my ADHD won't let me scrap this idea.
EDIT AS REQUESTED:
Public Sub pbStep()
Dim f As frmMain = frmMain
If Me.InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf pbStep))
Else
DirectCast(f, frmMain).ToolStripProgressBar1.PerformStep()
System.Threading.Thread.Sleep(100)
End If
End Sub
Both responses helped lead me to the correct answer I was needing. The code provided by James was a great starting point to build on, and Hans has several post explaining the BackgroundWorker. I wanted to share the "Answer" I came up with. I'm not saying its the best way to do this, and I'm sure I'm violating some rules of common logic. Also, a lot of the code came from a MSDN example and James's code.
Lets start with the form from which I am calling the bgw, frmShipping. I added this code:
Dim WithEvents bgw1 As New System.ComponentModel.BackgroundWorker
Private Sub bgw1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles bgw1.RunWorkerCompleted
If e.Error IsNot Nothing Then
MessageBox.Show("Error: " & e.Error.Message)
ElseIf e.Cancelled Then
MessageBox.Show("Process Canceled.")
Else
MessageBox.Show("Finished Process.")
End If
End Sub
Private Sub bgw1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles bgw1.ProgressChanged
DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.Maximum = 1960
DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.Step = 2
Dim state As cApex.CurrentState =
CType(e.UserState, cApex.CurrentState)
DirectCast(Me.MdiParent, frmMain).txtCount.Text = state.LinesCounted.ToString
DirectCast(Me.MdiParent, frmMain).txtPercent.Text = e.ProgressPercentage.ToString
DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.PerformStep()
End Sub
Private Sub bgw1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles bgw1.DoWork
Dim worker As System.ComponentModel.BackgroundWorker
worker = CType(sender, System.ComponentModel.BackgroundWorker)
Dim objApex As cApex = CType(e.Argument, cApex)
objApex.CountLines(worker, e)
End Sub
Sub StartThread()
Me.txtCount.Text = "0"
Dim objApex As New cApex
bgw1.WorkerReportsProgress = True
bgw1.RunWorkerAsync(objApex)
End Sub
Next I added the following code the my cApex class.
Public Class CurrentState
Public LinesCounted
End Class
Private LinesCounted As Integer = 0
Public Sub CountLines(ByVal worker As System.ComponentModel.BackgroundWorker, _
ByVal e As System.ComponentModel.DoWorkEventArgs)
Dim state As New CurrentState
Dim line = ""
Dim elaspedTime = 20
Dim lastReportDateTime = Now
Dim lineCount = File.ReadAllLines(My.Settings.strGenFilePath).Length
Dim percent = Math.Round(100 / lineCount, 2)
Dim totaldone As Double = 2
Using myStream As New StreamReader(My.Settings.strGenFilePath)
Do While Not myStream.EndOfStream
If worker.CancellationPending Then
e.Cancel = True
Exit Do
Else
line = myStream.ReadLine
LinesCounted += 1
totaldone += percent
If Now > lastReportDateTime.AddMilliseconds(elaspedTime) Then
state.LinesCounted = LinesCounted
worker.ReportProgress(totaldone, state)
lastReportDateTime = Now
End If
System.Threading.Thread.Sleep(2)
End If
Loop
state.LinesCounted = LinesCounted
worker.ReportProgress(totaldone, state)
End Using
End Sub
I also added a couple of text boxes to my main form to show the current line count from the file being read from and the overall progress as a percentage of a 100. Then on the Click event of my button I just call StartThread(). It is not 100% accurate, but its close enough to give the user a very good idea where the process stands. I have a little more work to do to add it to the "ReadScanner" function, where I originally was wanting to use the progress bar. But this function it the longer of the two that I perform on the scanner, writing almost 2,000 lines of code through a COMM Port. I'm happy with the results.
Thank you guys for helping out!
P.S. I have also now added variables to set the pbar.Maximum and the pbar.step since those can change if the scanner file is changed.
Background workers are useful for this purpose. Just use it in combination with a delegate. All the threaded work is done within the DoWork event of the worker. As progress is made, progress is reported within the DoWork event. This in turn fires the ProgressedChanged event of the worker class which is on the same thread as the progressbar. Once the DoWork has completed and is out of scope, the RunWorkerCompleted event is fired. This can be used to do inform the user that the task is complete, etc. Here is a working solution that I threw together. Just paste it behind an empty form and run.
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private _progressBar As ProgressBar
Private _worker As BackgroundWorker
Sub New()
' This call is required by the designer.
InitializeComponent()
Initialize()
BindComponent()
End Sub
Private Sub Initialize()
_progressBar = New ProgressBar()
_progressBar.Dock = DockStyle.Fill
_worker = New BackgroundWorker()
_worker.WorkerReportsProgress = True
_worker.WorkerSupportsCancellation = True
Me.Controls.Add(_progressBar)
End Sub
Private Sub BindComponent()
AddHandler _worker.ProgressChanged, AddressOf _worker_ProgressChanged
AddHandler _worker.RunWorkerCompleted, AddressOf _worker_RunWorkerCompleted
AddHandler _worker.DoWork, AddressOf _worker_DoWork
AddHandler Me.Load, AddressOf Form1_Load
End Sub
Private Sub Form1_Load()
_worker.RunWorkerAsync()
End Sub
Private Sub _worker_ProgressChanged(ByVal o As Object, ByVal e As ProgressChangedEventArgs)
_progressBar.Increment(e.ProgressPercentage)
End Sub
Private Sub _worker_RunWorkerCompleted(ByVal o As Object, ByVal e As RunWorkerCompletedEventArgs)
End Sub
Private Sub _worker_DoWork(ByVal o As Object, ByVal e As DoWorkEventArgs)
Dim worker = DirectCast(o, BackgroundWorker)
Dim value = 10000
SetProgressMaximum(value)
For x As Integer = 0 To value
Thread.Sleep(100)
worker.ReportProgress(x)
Next
End Sub
Private Sub SetProgressMaximum(ByVal max As Integer)
If _progressBar.InvokeRequired Then
_progressBar.Invoke(Sub() SetProgressMaximum(max))
Else
_progressBar.Maximum = max
End If
End Sub
End Class
I understand the concept of threading. I understand the concept of delegates but I am having trouble combining the two concepts. I followed a tutorial and I was able to make two counters start at the same time using multiple threads on my form. I was getting the cross threading error and I used the Me.CheckForIllegalCrossThreadCalls = False work around. I know my current method isnt ideal and I was wondering how I would use delegates to produce the same results. I have been at it all day and still cant seem to grasp the idea. How would I add delegates to the code below to allow two counters to work simultaneously on my form?
Public Class Form1
Dim i As Integer = 0
Dim i2 As Integer = 0
'declare two threads
'thread 1
Dim thread As System.Threading.Thread
'thread 2
Dim thread2 As System.Threading.Thread
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
'replace countup() with, this will assign the countup method to thread 1
thread = New System.Threading.Thread(AddressOf countup)
thread.Start()
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
' countup2()
thread2 = New System.Threading.Thread(AddressOf countup2)
thread2.Start()
End Sub
Private Sub countup()
Do Until i = 100000
i = i + 1
Label1.Text = i
'We wont be able to see the label unless we refresh the form
Me.Refresh()
Loop
End Sub
Private Sub countup2()
Do Until i2 = 100000
i2 = i2 + 1
Label2.Text = i2
'We wont be able to see the label unless we refresh the form
Me.Refresh()
Loop
End Sub
End Class
I would love to see the code using delegates but what I would really like is to have the understanding of whats going on.
Thanks guys
Not sure if this is exactly what you're looking for, but here's my best shot at it:
Module Module1
Dim i As Integer = 0
Dim i2 As Integer = 0
Public Delegate Sub counting()
Sub Main()
Dim del2 As counting = AddressOf countup2
Dim callback2 As IAsyncResult = del2.BeginInvoke(Nothing, Nothing)
Dim del1 As counting = AddressOf countup
Dim callback1 As IAsyncResult = del1.BeginInvoke(Nothing, Nothing)
del2.EndInvoke(callback2)
del1.EndInvoke(callback1)
Console.ReadLine()
End Sub
Private Sub countup()
Do Until i = 100000
i = i + 1
Loop
Console.WriteLine("i = " & i)
End Sub
Private Sub countup2()
Do Until i2 = 100000
i2 = i2 + 1
Loop
Console.WriteLine("i2 = " & i2)
End Sub
End Module
Sorry I have the first and second parts reversed and it's a console app instead of a form, but I figured the important part was to demonstrate delegates...
As a note, I'm not sure how familiar you are with delegates, but I included the EndInvoke to make sure the program wouldn't terminate prior to the delegates finishing their operations. They are used to return any values or exceptions from the method call as well as making the program wait. (In this case, since it's a sub there is no return value, so I didn't bother worrying about it)
One should use Control.Invoke to execute a specified delegate on the thread that owns the control's underlying window handle. Also, replace Me.Refresh() with Thread.Sleep(1) to ensure that other threads get some execution time.
Private Sub countup()
For i As Integer = 0 To 100000
Me.Invoke(Sub() Me.Label1.Text = i.ToString())
Thread.Sleep(1)
Next
End Sub
Here's an example.
' n=0 n=1
Private threads As Thread() = New Thread(2 - 1) {Nothing, Nothing}
Private Sub ButtonsClick(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
Dim n As Integer = -1
If (sender Is Me.Button1) Then
n = 0
ElseIf (sender Is Me.Button2) Then
n = 1
End If
If (n <> -1) Then
If (Me.threads(n) Is Nothing) Then
'Start new thread.
Me.threads(n) = New System.Threading.Thread(Sub() Me.CountUp(n))
Me.threads(n).Start()
Else
'Abort thread.
Me.threads(n).Abort()
Me.threads(n) = Nothing
End If
End If
End Sub
Public Sub DisplayCount(n As Integer, text As String)
'Inside UI thread.
If (n = 0) Then
Me.Label1.Text = text
ElseIf (n = 1) Then
Me.Label2.Text = text
End If
End Sub
Private Sub CountUp(n As Integer)
'Inside worker thread.
Try
If ((n < 0) OrElse (n > 1)) Then
Throw New IndexOutOfRangeException()
End If
For i As Integer = 0 To 100000
Me.Invoke(Sub() Me.DisplayCount(n, i.ToString()))
Thread.Sleep(1)
Next
Catch ex As ThreadAbortException
Me.Invoke(Sub() Me.DisplayCount(n, "Cancelled"))
Thread.Sleep(1)
Catch ex As Exception
'TODO: Handle other exceptions.
End Try
End Sub
using Me.CheckForIllegalCrossThreadCalls = False is not the right approach.
Basically, Cross-thread operation not valid exception is raised when a control is being updated from a thread other than the thread it was created on.
Each control exposes a InvokeRequired property that allows it to be updated in a thread-safe manner.
Therefore the right way to update the label is to use code like -
Private Delegate Sub UpdateLabelDelegate(i As Integer)
Private Sub UpdateLabel(i As Integer)
If Label1.InvokeRequired Then
Dim del As New UpdateLabelDelegate(AddressOf UpdateLbl)
Label1.Invoke(del, New Object() {i})
'Me.Refresh()
Else
' this is UI thread
End If
End Sub
Private Sub UpdateLbl(i As Integer)
Label1.Text = i.ToString()
End Sub
Delegate.BeginInvoke will execute the method on a thread pool thread. Once the method returns, the thread is returned to the pool.
So basically instead of starting a new thread, you will asynchronously execute the method using Delegate.BeginInvoke
I'm trying to create a thread so when I click a button it creates a new PictureBox from a class, this is how far I've got but nothing comes up on the screen at all.
Form1 code:
Public Class Form1
Private pgClass As New SecondUIClass
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
pgClass = New SecondUIClass
pgClass.x += 100
pgClass.thread()
End Sub
End Class
Class Code:
Imports System.Threading
Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0
Private trd As Thread
Public Sub thread()
trd = New Thread(AddressOf NewUIThread)
trd.SetApartmentState(ApartmentState.STA)
trd.IsBackground = False
trd.Start()
End Sub
Private Sub NewUIThread()
emeny(counter) = New PictureBox
emeny(counter).BackColor = Color.Red
emeny(counter).Visible = True
emeny(counter).Location = New System.Drawing.Point(x, 100)
emeny(counter).Size = New System.Drawing.Size(10, 50)
Form1.Controls.Add(emeny(counter))
For z = 0 To 13
emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
Application.DoEvents()
Threading.Thread.Sleep(100)
Next
counter += 1
End Sub
End Class
I have posted something similar before on here but it was different, the pictureBoxes were showing on the screen but I was trying to get them to move at the same time but they wouldn't move, they only moved one at a time. The question that I asked before was this Multi threading classes not working correctly
I made a few assumptions for this answer so it may not work for you out of the box but I think it will put you on the right track without using any Thread.Sleep calls because I personally don't like building intentional slows to my apps but that's a personal preference really.
So For my example I just used a bunch of textboxes because I didn't have any pictures handy to fiddle with. But basically to get it so that the user can still interact with the program while the moving is happening I used a background worker thread that is started by the user and once its started it moves the textboxes down the form until the user tells it to stop or it hits an arbitrary boundary that I made up. So in theory the start would be the space bar in your app and my stop would be adding another control to the collection. For your stuff you will want to lock the collection before you add anything and while you are updating the positions but that is up to your discretion.
So the meat and potatoes:
in the designer of the form I had three buttons, btnGo, btnStop and btnReset. The code below handles the click event on those buttons so you will need to create those before this will work.
Public Class Move_Test
'Flag to tell the program whether to continue or to stop the textboxes where they are at that moment.
Private blnStop As Boolean = False
'Worker to do all the calculations in the background
Private WithEvents bgWorker As System.ComponentModel.BackgroundWorker
'Controls to be moved.
Private lstTextBoxes As List(Of TextBox)
'Dictionary to hold the y positions of the textboxes.
Private dtnPositions As Dictionary(Of Integer, Integer)
Public Sub New()
' Default code. Must be present for VB.NET forms when overwriting the default constructor.
InitializeComponent()
' Here I instantiate all the pieces. The background worker to do the adjustments to the position collection, the list of textboxes to be placed and moved around the form
' and the dictionary of positions to be used by the background worker thread and UI thread to move the textboxes(because in VB.NET you can not adjust controls created on the UI thread from a background thread.
bgWorker = New System.ComponentModel.BackgroundWorker()
Me.lstTextBoxes = New List(Of TextBox)
Me.dtnPositions = New Dictionary(Of Integer, Integer)
For i As Integer = 0 To 10
Dim t As New TextBox()
t.Name = "txt" & i
t.Text = "Textbox " & i
'I used the tag to hold the ID of the textbox that coorelated to the correct position in the dictionary,
' technically you could use the same position for all of them for this example but if you want to make the things move at different speeds
' you will need to keep track of each individually and this would allow you to do it.
t.Tag = i
dtnPositions.Add(i, 10)
'Dynamically position the controls on the form, I used 9 textboxes so i spaced them evenly across the form(divide by 10 to account for the width of the 9th text box).
t.Location = New System.Drawing.Point(((Me.Size.Width / 10) * i) + 10, dtnPositions(i))
Me.lstTextBoxes.Add(t)
Next
'This just adds the controls to the form dynamically
For Each r In Me.lstTextBoxes
Me.Controls.Add(r)
Next
End Sub
Private Sub Move_Test_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
'Don't need to do anything here. Placeholder
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Try
If Not bgWorker.IsBusy Then
'User starts the movement.
bgWorker.RunWorkerAsync()
End If
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnReset_Click(sender As Object, e As EventArgs) Handles btnReset.Click
Try
'Reset the positions and everything else on the form for the next time through
' I don't set the blnStop value to true in here because it looked cooler to keep reseting the textboxes
' and have them jump to the top of the form and keep scrolling on their own...
For Each r In Me.lstTextBoxes
r.Location = New System.Drawing.Point(r.Location.X, 10)
Next
For i As Integer = 0 To dtnPositions.Count - 1
dtnPositions(i) = 10
Next
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub bgWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
Try
'This is where we do all the work.
' For this test app all its doing is scrolling through each value in the dictionary and incrementing the value
' You could make the dictionary hold a custom class and have them throttle themselves using variables on the class(or maybe travel at an angle?)
For i As Integer = 0 To dtnPositions.Count - 1
dtnPositions(i) += 1
Next
Catch ex As Exception
blnStop = True
End Try
End Sub
Private Sub bgWorker_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
Try
'Once the background worker is done updating the positions this function scrolls through the textboxes and assigns them their new positions.
' We have to do it in this event because we don't have access to the textboxes on the backgroun thread.
For Each r In Me.lstTextBoxes
r.Location = New System.Drawing.Point(r.Location.X, dtnPositions(CInt(r.Tag)))
Next
'use linq to find any textboxes whose position is beyond the threshhold that signifies they are down far enough.
' I chose the number 100 arbitrarily but it could really be anything.
Dim temp = From r In Me.lstTextBoxes Where r.Location.Y > (Me.Size.Height - 100)
'If we found any textboxes beyond our threshold then we set the top boolean
If temp IsNot Nothing AndAlso temp.Count > 0 Then
Me.blnStop = True
End If
'If we don't want to stop yet we fire off the background worker again and let the code go otherwise we set the stop boolean to false without firing the background worker
' so we will be all set to reset and go again if the user clicks those buttons.
If Not Me.blnStop Then
bgWorker.RunWorkerAsync()
Else
Me.blnStop = False
End If
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
Try
'The user clicked the stop button so we set the boolean and let the bgWorker_RunWorkerCompleted handle the rest.
Me.blnStop = True
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
End Class
Theres a lot of code there but a lot of it is comments and I tried to be as clear as possible so they are probably a little long winded. But you should be able to plop that code on a new form and it would work without any changes. I had the form size quite large (1166 x 633). So I think that's when it works best but any size should work(smaller forms will just be more cluttered).
Let me know if this doesn't work for your application.
This is a problem that is well suited to async/await. Await allows you to pause your code to handle other events for a specific period of time..
Private Async Function Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) As Task Handles Button1.Click
pgClass = New SecondUIClass
pgClass.x += 100
await pgClass.NewUIThread()
End Sub
End Class
Class Code:
Imports System.Threading
Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0
Private Async Function NewUIThread() As Task
emeny(counter) = New PictureBox
emeny(counter).BackColor = Color.Red
emeny(counter).Visible = True
emeny(counter).Location = New System.Drawing.Point(x, 100)
emeny(counter).Size = New System.Drawing.Size(10, 50)
Form1.Controls.Add(emeny(counter))
For z = 0 To 13
emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
await Task.Delay(100) 'The await state machine pauses your code here in a similar way to application.doevents() until the sleep has completed.
Next
counter += 1
End Sub
End Class
I am working on windows form application :
in my form am filling my datagrid view in some interval.so some time my application getting stuck..so i used back ground worker and timer
in back ground worker i am calling my function to fill the my data grid view.i set Timer Interval as 10000. in background worker i given code like this:
Private Sub BackgroundWorker1_DoWork
Call Fetch_Info()
End Sub
in Timer click event i given code like thise:
If Not BackgroundWorker1.IsBusy Then
BackgroundWorker1.RunWorkerAsync()
End If
my Fetch_Info() function like this:
Dim cnt As Integer
Dim tbarcodedgv As String
Dim totaltbarcode As String
cnt = DGVall.RowCount
Dim tbar As String
Dim locTable As New DataTable
locTable.Columns.Add("carid", GetType(String))
If cnt > 0 Then
For i = 0 To cnt - 2
tbarcodedgv = DGVall.Rows(i).Cells(0).Value
locTable.Rows.Add(tbarcodedgv)
Next
End If
Dim flag As Boolean = False
Dim dcnt As Integer = DGVall.RowCount
Dim trid As Integer
Dim tbarcode As String
Dim keyloc As String
Dim cmd23 As New SqlCommand("IBS_fetchrequested", con.connect)
cmd23.CommandType = CommandType.StoredProcedure
cmd23.Parameters.Add("#tid", SqlDbType.Int).Value = tid
If cnt > 1 Then
Dim tvp1 As SqlParameter = cmd23.Parameters.AddWithValue("#Tbaroced", locTable)
tvp1.SqlDbType = SqlDbType.Structured
tvp1.TypeName = "dbo.TBarcode"
End If
dr = cmd23.ExecuteReader
While dr.Read
flag = False
tbarcode = dr("TBarcode")
If flag = False Then
If dr("keyloc") Is DBNull.Value Then
keyloc = ""
Else
keyloc = dr("keyloc")
End If
Dim row0 As String() = {tbarcode, keyloc, "", "Release"}
DGVall.Rows.Add(row0)
AxWindowsMediaPlayer1.URL = "C:\Beep.mp3"
End If
End While
dr.Close()
con.disconnect()
While your background worker runs in another thread than your GUI you are manipulating the Datagridview that's running in the GUI's thread. This should usually not work at all but it is probably the reason, why your GUI hangs while the BGW is running.
Try splitting the work: The time consuming fetching of data from the database is carried out in the Backgroundworker's DoWork event handler and you set the results as the e.Result value of the EventArgs variable in the DoWork function.
Then you handle the Backgroundworker's RunWorkerCompleted event and there you quickly update your datagridview with the results you set in the DoWork method. That way your GUI has nothing to do with the actual time consuming task and will only be affected by the quick update of your datagridview.
The code example for this is:
Public Class Form1
Private WithEvents LazyBGW As New System.ComponentModel.BackgroundWorker
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'This code runs in the UI-Thread
LazyBGW.RunWorkerAsync()
End Sub
Private Sub LazyBGW_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles LazyBGW.DoWork
'This code runs in the BGW-Thread
Dim a As Integer = 0
For i = 1 To 5
a += 1
'I'm a lazy worker, so after this hard work I need to...
Threading.Thread.Sleep(1000) 'This locks up the BGW-Thread, not the UI-thread
Next
'Work is done, put results in the eventargs-variable for further processing
e.Result = a
End Sub
Private Sub LazyBGW_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles LazyBGW.RunWorkerCompleted
'This code runs in the UI-Thread
Dim results As Integer = CInt(e.Result) 'e.Result contains whatever you put into it in the DoWork() method
MessageBox.Show("Finally the worker is done and our result is: " & results.ToString)
End Sub
End Class
I am having an issue where I get multiple entries in my ListView for the same item if I run my action more than once.
I am creating a simple network scanner/hostname grabber that will add the items to the listview as they come back alive to my ping test.
When I run it the first time it runs fine and creates one entry as it should.
When I run it subsequent times it creates the item as many times as I have ran the code ex. 3rd time hitting start it creates each entry 3 times when it should just create the entry once.
Here is my go button code:
Private Sub Go_Click(sender As Object, e As EventArgs) Handles Go.Click
Dim verifyIP
ListView1.Items.Clear()
chkDone = 0
verifyIP = ipChk(ipAdd.Text)
If verifyIP = 1 Then
ipAddy = Split(ipAdd.Text, ".")
pingTest1.WorkerReportsProgress = True
pingTest1.WorkerSupportsCancellation = False
AddHandler pingTest1.ProgressChanged, AddressOf pingTest1_ProgressChanged
pingTest1.RunWorkerAsync()
pingTest2.WorkerReportsProgress = True
pingTest2.WorkerSupportsCancellation = False
AddHandler pingTest2.ProgressChanged, AddressOf pingTest2_ProgressChanged
pingTest2.RunWorkerAsync()
pingTest3.WorkerReportsProgress = True
pingTest3.WorkerSupportsCancellation = False
AddHandler pingTest3.ProgressChanged, AddressOf pingTest3_ProgressChanged
pingTest3.RunWorkerAsync()
pingTest4.WorkerReportsProgress = True
pingTest4.WorkerSupportsCancellation = False
AddHandler pingTest4.ProgressChanged, AddressOf pingTest4_ProgressChanged
pingTest4.RunWorkerAsync()
While chkDone < 4
wait(25)
End While
Else
MsgBox("IP Invalid")
End If
MsgBox("Done")
End Sub
Here is the code from one of the background workers I am using:
Private WithEvents pingTest1 As BackgroundWorker = New BackgroundWorker
Private Sub pingTest1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles pingTest1.DoWork
Try
Dim hostCheck
pingResult1 = 0
pingTestDone1 = 0
tryIP1 = ipAddy(0) & "." & ipAddy(1) & "." & ipAddy(2) & ".1"
If My.Computer.Network.Ping(tryIP1) = True Then
'Dim pingsender As New Net.NetworkInformation.Ping
'If pingsender.Send(tryIP).Status = Net.NetworkInformation.IPStatus.Success Then
Try
'Dim host As System.Net.IPHostEntry
hostCheck = ""
'host = System.Net.Dns.GetHostByAddress(tryIP3)
'MsgBox(host.HostName)
'host3 = host.HostName
'hostCheck = System.Net.Dns.GetHostEntry(tryIP3).HostName
hostCheck = System.Net.Dns.GetHostByAddress(tryIP1)
'get the hostname property
hostCheck = hostCheck.HostName
pingTest1.ReportProgress("1", hostCheck)
Catch f As Exception
'MsgBox("Error: " & f.Message)
pingTest1.ReportProgress("1", "No Hostname Found")
End Try
Else
pingResult1 = 2
End If
Catch d As Exception
MsgBox("There was an error trying to ping the IP Address: " & d.Message)
End Try
End Sub
Private Sub pingTest1_ProgressChanged(e.ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
MsgBox("Hey")
Dim str(2) As String
Dim itm As ListViewItem
str(0) = tryIP1 & " Is Alive!!!"
str(1) = e.UserState
itm = New ListViewItem(str)
ListView1.Items.Add(itm)
str(0) = ""
str(1) = ""
itm = Nothing
End Sub
Private Sub pingTest1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles pingTest1.RunWorkerCompleted
chkDone = chkDone + 1
End Sub
I added the Hey box and sure enough the ProgressChanged event gets triggered the amount of times I have hit the Go button. Is it something I have coded incorrectly?
It's most likely because you're adding, but not removing your handlers for the progress changed, so you're handling the event multiple times.
Try adding your Progress Changed Event Handlers when you're instantiating your Background workers, rather than every time you click your button. This way they will only handled once.