thanks Jmc - i'll take a look a your sample
Printable View
thanks Jmc - i'll take a look a your sample
I have the following Sub that performs a couple of loops through a datatable . I currently have a progressbar1 that works fine, however while the sub is running if the user clicks on anything the progress bar and label freeze, the sub is still running and every functions fine. SO would I be correct in assuming this is a situation for backgroundworker? and have the label and progressbar woking on there own thread??
If so I am not sure how to implement it with my sub?? I am trying to follow and learn from the example you posted. I built the sample and trying to see how my code would be implemented.
I placed my Sub "GetValue()" in the BGW do work event but thats obviously not correct because that would just a one time event
I think I need to capture the first loop of that Sub
For i As Integer = 0 To dtlist.Rows.Count - 1
Dim lat As Double = CDbl(dtlist.Rows(i)("Latitude"))
Dim lon As Double = CDbl(dtlist.Rows(i)("Longitude"))
vb Code:
Dim Coords() As Coord = GetCoords() Dim sw As Stopwatch sw = Stopwatch.StartNew Me.propsprocessed.Visible = True Me.lblCount.Visible = True Me.ProgressBar1.Visible = True For i As Integer = 0 To dtlist.Rows.Count - 1 Dim lat As Double = CDbl(dtlist.Rows(i)("Latitude")) Dim lon As Double = CDbl(dtlist.Rows(i)("Longitude")) 'Labelcount Me.lblCount.Text = CStr(i + 1) Me.lblCount.Refresh() 'progress bar ProgressBar1.Maximum = dtlist.Rows.Count ProgressBar1.Minimum = 0 For d = ProgressBar1.Minimum To ProgressBar1.Maximum ProgressBar1.Value = i Next For t As Integer = 0 To dtsold.Rows.Count - 1 dtsold.Rows(t)("Distance") = (DistanceCalc1(lat, lon, Coords(t).Lat1, Coords(t).Lon1, CChar("M"))) Next Dim Dt As Decimal = NumericUpDown1.Value dtlist.Rows(i)("Value") = dtsold.Compute("Max(ClosePrice)", "Distance <=" & Dt & "") Next ProgressBar1.Visible = False sw.Stop() Dim StopWatchString As String StopWatchString = "Processing Time = " & sw.ElapsedMilliseconds & " milliseconds" Dim ts As String = CStr(sw.Elapsed.Minutes) ElapsedTime.Text = "In" & " " & ts & " " & "Seconds" ElapsedTime.Show()
If someone can give me some guideanceiwold appreciate it
Thanks
@billboy: It's very simple. You perform the long-running task in the DoWork event handler. If the long-running task is an entire loop then you put the entire loop in the DoWork event handler. If you need to update the UI during the task then you call ReportProgress and handle the ProgressChanged event. If you need to update the UI after the task then you handle the RunWorkerCompleted event. That's really all there is to it.
I think I am a bit closer
getting error:
An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation.
I put the task in the DoWorkEvent
vb Code:
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork Dim worker As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker) Dim Coords() As Coord = GetCoords() Dim sw As Stopwatch sw = Stopwatch.StartNew For i As Integer = 0 To dtlist.Rows.Count - 1 Dim lat As Double = CDbl(dtlist.Rows(i)("Latitude")) Dim lon As Double = CDbl(dtlist.Rows(i)("Longitude")) worker.ReportProgress(i) For t As Integer = 0 To dtsold.Rows.Count - 1 dtsold.Rows(t)("Distance") = (DistanceCalc1(lat, lon, Coords(t).Lat1, Coords(t).Lon1, CChar("M"))) Next Dim Dt As Decimal = NumericUpDown1.Value dtlist.Rows(i)("Value") = dtsold.Compute("Max(ClosePrice)", "Distance <=" & Dt & "") Next
The error comes up at the last line, what am I missing/doing wrong?
@billboy:
First things first, what's this doing in your DoWork event handler?A NumericUpDown is a control. No accessing controls in the DoWork event handler.Code:Dim Dt As Decimal = NumericUpDown1.Value
As for the error, that's the exception you see on the UI thread when an exception is thrown on a secondary thread. You need to catch the exception on the secondary thread, examine it and fix the issue that's causing it, just as you would do on the UI thread.
Oh missed that NumericUpDown I took that out and hard coded my distance for now
As for catching the exception, I am not sure how to do that. The same code runs fine when not implementing backgroundworker
try
Catch ??
My Values are returned
Thats what I thought, I am not getting a message though?
vb Code:
Try For i As Integer = 0 To dtlist.Rows.Count - 1 Dim lat As Double = CDbl(dtlist.Rows(i)("Latitude")) Dim lon As Double = CDbl(dtlist.Rows(i)("Longitude")) worker.ReportProgress(i) 'Labelcount For t As Integer = 0 To dtsold.Rows.Count - 1 dtsold.Rows(t)("Distance") = (DistanceCalc1(lat, lon, Coords(t).Lat1, Coords(t).Lon1, CChar("M"))) Next 'Dim Dt As Decimal = NumericUpDown1.Value dtlist.Rows(i)("Value") = dtsold.Compute("Max(ClosePrice)", "Distance <=.5") Next Catch exc As Exception MessageBox.Show(exc.Message & vbCrLf & vbCrLf & exc.StackTrace, exc.GetType().ToString()) End Try
Also tried it here:
vb Code:
Private Sub GetValue() Try Me.propsprocessed.Visible = True Me.lblCount.Visible = True Me.ProgressBar1.Visible = True Me.BackgroundWorker1.RunWorkerAsync() Catch exc As Exception MessageBox.Show(exc.Message & vbCrLf & vbCrLf & exc.StackTrace, exc.GetType().ToString())
And here:
vb Code:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GetValues.Click Try GetValue() Catch exc As Exception MessageBox.Show(exc.Message & vbCrLf & vbCrLf & exc.StackTrace, exc.GetType().ToString()) End Try End Sub
Ok I still dont understand why my exception message box doesnt appear, but I managed to view the debug out window and found the error, i needed to set my progressbar maximum state
ProgressBar1.Maximum = dtlist.Rows.Count
I know I still have some problems, with access control textbox etc... but I think you have given me enough insight to tackle rest for now....
Thanks
Hi,
Sorry for dragging this post back up but its the best example I found.
I'm also a bedroom hobbyist and therefore don't have any real expirence in programming so forgive me for if this is a dumb question.
I have a background worker within my form and a progress bar which works fine whilse the work being carried out is within the same form. If I move the work to a Module the work still gets carried out but my progress bar doesn't update anymore. My understanding is this is because the work and progress bar are not within the same ui thread but I dont understand how to fix this issue.
Here's a quick example of a test I setup...
My Main Form
My Module CodeCode:Public Class Form_Test
Delegate Sub SetLabelText_Delegate(ByVal Label As Label, ByVal [text] As String)
Public Sub SetLabelText_ThreadSafe(ByVal Label As Label, ByVal [text] As String)
If Label.InvokeRequired Then
Dim MyDelegate As New SetLabelText_Delegate(AddressOf SetLabelText_ThreadSafe)
Me.Invoke(MyDelegate, New Object() {Label, [text]})
Else
Label.Text = [text]
End If
End Sub
Public Sub test()
Dim i As Integer = 0
Do While i < 20
i = i + 1
Me.BackgroundWorker1.ReportProgress(CInt((i / 20) * 100))
Me.SetLabelText_ThreadSafe(Label1, "Same Form - " & FormatPercent(i / 20, 0))
System.Threading.Thread.Sleep(100)
Debug.Print("Running Test 2 From Within The Same Form: " & i)
Loop
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_DoWork_1(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
test()
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker2_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker2.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Public Sub BackgroundWorker2_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker2.DoWork
test_2()
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Me.BackgroundWorker2.RunWorkerAsync(2)
End Sub
End Class
Anyone got any suggestions?Code:Module Module_Test
Public Sub test_2()
Dim i As Integer = 0
Do While i < 20
i = i + 1
Form_Test.BackgroundWorker1.ReportProgress(CInt((i / 20) * 100))
Form_Test.SetLabelText_ThreadSafe(Form_Test.Label1, "Module - " & FormatPercent(i / 20, 0))
System.Threading.Thread.Sleep(100)
Debug.Print("Running Test 2 From Within A Module: " & i)
Loop
End Sub
End Module
Many Thanks
Ben
@jaminben, the work is carried out on a different thread to the one that owns the ProgressBar in both cases. That's the whole point. The issue is related to your use of a default instance. Follow the Blog link in my signature and check out my post on Default Form Instances to learn how they work and don't work. That should help to explain what you're doing wrong. If you still need help to fix it, post back.
Can you throw me a bone (I've read through your blog, as well as others)... And I'm not really understanding instances and how you reference between them with the background worker.
Thanks
Edit
Not too worry I worked it out... thanks for the push in the right direction though :)
Thanks a lot. Great post!
Thanks for shareing jmcilhinney, I not looked at the background worker, but your examples should help me a lot thanks agian.
How can i access one or more control from DoWork event
vb Code:
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork Dim strLines() As String = IO.File.ReadAllLines("some_path") Dim intLength As Integer = strLines.Length - 1 ' This will not less than 50,000 Dim f() As String For j As Integer = 0 To intLength f = strLines(j).Split("|") ListView1.Items.Add(f(0)) ListView2.Items.Add(f(1)) Next End Sub
jm, all controls are in the UI thread. And thus, we can't access it from the BackgroundWorker's DoWork event, which runs in a separate thread.
But the variables inside that form(class level scope) is accessible inside it. Isn't it ?
I just tried it by declaring a class level array and inside the DoWork, I have returned the length of the array.
So, any variables declared in that current form is accessible, right?
Or that's not the best approach? I should always pass the values to it as a parameter of RunWorkerAsync() ?
Thanks :wave:
You're confusing two unrelated things. The DoWork event handler of the BackgroundWorker is a method like any other other method. It is a member of the form like any other form. It has all the same rights and privileges as any other method in the form. Member variables are within the scope of the form so any methods within that form can access those methods.
You can even write code in that method to access the controls on the form and the compiler will not complain as long as the syntax is correct. If you were to call that method directly then the code would work fine. It's only if, at run time, a method is run on a secondary thread that any control access that uses the Handle will fail. That's a run time issue, not a design time issue.
Means, the methods that access the controls can't be called inside the DoWork() as well as no controls can be accessed directly within that event.
Because, the code inside the DoWork() event executes in a separate thread and the controls are in another thread.
But all other variables/methods can directly be accessed within in it.
Am I correct ?
Thanks :wave:
EDIT:
jm, I am still confused(even after reading the below post). So, I will try to refresh my brain by reading again topics about Threading and thread affinity. And will create a thread in VB.Net forum, instead of flooding this thread :) Thanks again.
No you're not correct. Controls are accessed via member variables too. All members are accessible from all other members. Accessing a control in the DoWork event handler is allowed. The compiler will not complain. At run time though, those accesses that are performed via legal code will fail. They are two different concepts: scope and thread affinity.
i Would like to know how to make progress bar work with my "for each" line reader for downloading. for example how should i get BytesReceived .Code:BackgroundWorker1_DoWork
Dim worker As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
Dim lines As String() = FileToArray(My.Computer.FileSystem.CurrentDirectory & "/filelist.txt")
Dim line As String
For Each line In lines
uriSource = New Uri("http://localhost/" & line)
uriPath = My.Computer.FileSystem.CurrentDirectory & line
???For i As Integer = 1 To 100
worker.ReportProgress(i, "Downloading : " & line)
??????Next i
downloading.DownloadFile(uriSource, uriPath)
Next line
----------------
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
Me.Label6.Text = TryCast(e.UserState, String)
End Sub
or like
thanks in advance!!Code:
ProgressBar1.Maximum = e.TotalBytesToReceive
ProgressBar1.Value = e.BytesReceived
sry for duplicate post :( website lagged
There are various options but the simplest is to just look at the name of the first parameter of the ReportProgress method: percentProgress. Instead of setting the Maximum of the ProgressBar to the total number of bytes and the Value to the actual number of bytes, simply leave the Maximum as the default 100 and calculate the percentage yourself and pass that one value, e.g. if you have 5,000,000 bytes to download and you've downloaded 2,000,000 then the percentage is 40% so that's what you pass to ReportProgress.
Anyone who has done primary school maths knows how to calculate a percentage so I'm not going to provide an example of that. As for where to call ReportProgress, just ask yourself where in your code you want to update the ProgressBar and that's where you call ReportProgress.
I want to update ProgressBar while downloading each file. For example its downloading one file then the progressbar fillsup, and then when it starts to download other file progress bar starts from begining just for different file size. As i can understand you are telling me the example for overall files being downloaded. Sorry for my bad english :(.. And thanks for your patience and help!
this is for overall. it works, but what about each file Thank You!Code:Dim worker As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
Dim lines As String() = FileToArray(My.Computer.FileSystem.CurrentDirectory & "/filelist.txt")
Dim line As String
Dim j As Integer
Dim percent As Short
For Each size In sizes
Dim i As Integer = Convert.ToInt32(size)
j = j + i
Next size
For Each line In lines
Console.WriteLine(line)
uriSource = New Uri("http://localhost/" & line)
uriPath = My.Computer.FileSystem.CurrentDirectory & line
worker.ReportProgress(percent, "Downloading : " & line)
downloading.DownloadFile(uriSource, uriPath)
Dim info As New FileInfo(My.Computer.FileSystem.CurrentDirectory & line)
Dim length As Long = info.Length
Dim g As Long
g = g + length
percent = (g * 100) / j
Next line
Hi John,
Thanks for useful explanation.
What about showing a message in another form, such as a none BorderStyleForm with a pic on it saying "please wait" or "loading" during the progress and closing the form after completion?
With so many current BGW threads i think this should be on page One.
Cross thread operation error...
Ident, you should know better than that... you know you can't access the UIThread from the BGW directly... it has to be invoked and passed back to the main UI processing thread... THEN you can get it to show up on the main form (Page1)...
-tg
Took me a while to catch that one...has me looking on page 1 for ident's post rofl
90% of me says techgnome is making a joke "thread" related, the rest is left a little confused if not :D