I have a background event that is consistently withdraws data. When I close the form background event doesn't cancel for some reason. If I enable a message in between it does...... I tried to put a time pause, REFRESH, and both. But its only works properly after the message box. I don't really understand why, since button it self to cancel works fine when form is not close. ERROR "Cannot access a disposed object.Object name: 'Form1'." Somehow it doesn't see "False" on closing.
Thanks!
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
RED(False)
'Some other functions - WORK OK
End Sub
Private Sub RED(ByVal reads As Boolean)
bck.WorkerSupportsCancellation = True
If reads = True Then
bck.RunWorkerAsync()
ElseIf reads = False Then
bck.CancelAsync()
bck.Dispose()
End If
End Sub
Private Sub bck_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bck.DoWork
Do
If bck.CancellationPending = True Then --- DOESN'T look like see this without Message BOX
Exit Sub
End If
Me.Invoke(Sub()
'EVENT HERE WERE I AM GETTING AN ERROR --- HOWEVER, Works as a button when called on RED(False) and Works if I put a message BOX
End Sub)
Loop
End Sub
It would appear to be a matter of timing, as is often the case with multiple threads. What I would suggest is that, if the form is closed when the background task is in progress, you cancel the background task and then cancel the close. You can then close again when the background task has completed, e.g.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
If BackgroundWorker1.CancellationPending Then
'Cancel the background work.
Exit Do
End If
'Do the background work.
'...
Loop
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'Close the form.
Close()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If BackgroundWorker1.IsBusy Then
'Cancel the background task.
BackgroundWorker1.CancelAsync()
'Do not close the form this time.
e.Cancel = True
End If
End Sub
If you won't always want to close the form when the background task completes then you can indicate that to the RunWorkerCompleted event handler by, for instance, setting e.Result in the DoWork event handler and then testing it in the RunWorkerCompleted event handler, e.g.
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
e.Result = False
Do
If BackgroundWorker1.CancellationPending Then
e.Result = True
'Cancel the background work.
Exit Do
End If
'Do the background work.
'...
Loop
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If CBool(e.Result) Then
'Close the form.
Close()
End If
End Sub
Use PROGRESS REPORT instead of Invoke.
Private Sub bck_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bck.DoWork
Do
If bck.CancellationPending = True Then
Exit Sub
End If
bck.ReportProgress("WHATEVER YOU CAPTURING/VALUES UPDATE")
Loop
End Sub
Private Sub bck_dProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bck.ProgressChanged
YOUR DISPLAY = ("WHATEVER YOU CAPTURING/VALUES UPDATE")
End Sub
Related
Good afternoon!
Can you tell me how to stop the execution of a thread? The following code doesn't work:
Private Sub Stop_Click(sender As Object, e As EventArgs) Handles Stop.Click
If BackgroundWorker1.IsBusy Then
If BackgroundWorker1.WorkerSupportsCancellation Then
BackgroundWorker1.CancelAsync()
End If
End If
End Sub
That code does exactly what it is supposed to do, i.e. REQUEST a cancellation. It can't just stop working though, because it has no idea what work is being done. YOU are the one writing the code to do the work so YOU are the one who has to write the code to check whether a cancellation has been requested and to actually perform that cancellation. We have no idea what work you're doing so we have no idea where it is convenient to check whether a cancellation has been requested and what, if any, clean-up may be required in that case. Here's a basic example of what a cancellation might look like though:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Raise the DoWork event in a worker thread.
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
'This method is executed in a worker thread.
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)
For i As Integer = 1 To 100
If worker.CancellationPending Then
'The user has cancelled the background operation.
e.Cancel = True
Exit For
End If
'Raise the ProgressChanged event in the UI thread.
worker.ReportProgress(i, i & " iterations complete")
'Perform some time-consuming operation here.
Threading.Thread.Sleep(250)
Next i
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
Me.Label1.Text = TryCast(e.UserState, String)
End Sub
'This method is executed in the UI thread.
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If e.Cancelled Then
'The background operation was cancelled.
Me.Label1.Text = "Operation cancelled"
Else
'The background operation completed normally.
Me.Label1.Text = "Operation complete"
End If
End Sub
Private Sub Button1_Click(ByVal sender As Object, _
ByVal e As EventArgs) Handles Button1.Click
'Only cancel the background opertion if there is a background operation in progress.
If Me.BackgroundWorker1.IsBusy Then
Me.BackgroundWorker1.CancelAsync()
End If
End Sub
I am trying to make a progress bar on a separate form when doing other tasks in my application. everything opens up and shows correctly but progress bar is not updating. I tried background worker with no joy. Code is in VB.net
Please help. I must be missing a step or two in my code.
What I am trying to do is when listview is clicked, takes a bit to populate textboxes out of my excel database, so I would like the another form to open and update progress bar accordingly.
Hope someone can help.
This is my Click Event:
Private Sub ListView1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListView1.SelectedIndexChanged
Application.EnableVisualStyles()
BackgroundWorker1.RunWorkerAsync()
frmProgress.Show()
AddToDatabaseToolStripMenuItem.Enabled = False
frmProgress.Close()
End Sub
Then I have Background Worker code as follows:
Private Sub backgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
For i As Integer = 1 To 100
showData()
Thread.Sleep(1000)
BackgroundWorker1.ReportProgress(CInt(100 * i / 100), "Running..." & i.ToString)
Next
End Sub
Private Sub backgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs)
frmProgress.ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object,
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles BackgroundWorker1.RunWorkerCompleted
' Called when the BackgroundWorker is completed.
Label1.Text = "Loading... Done"
End Sub
Then my Progress form Load event is this:
Private Sub frmProgress_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Application.EnableVisualStyles()
With ProgressBar1
.Style = ProgressBarStyle.Blocks
.Visible = True
End With
Me.Text = "IDB"
Label1.Text = "Loading... Please wait"
Me.Cursor = Cursors.WaitCursor
End Sub
ShowData() is in the module and is the code that is loading and takes few seconds to load.
Thank you for your help.
I have come to learn that a module level variable's value will not be altered until a sub routine that changed it exits.
StopBackgroundWorker1 = True
Thread.Sleep(1500)
If BackgroundWorker1Complete = False Then
Exit Sub
End If
in this example, I added a long delay for testing. I'm simply trying to stop and start a background worker safely with vb 2017 new background worker class.
The example above with "StopBackgroundWorker1 = True", I was hoping to stop the worker at a safe place and then continue within that sub with other code.
But what is happening is that the "StopBackgroundWorker1 = True" is not being set "True" until the sub exits.
There must be another way to do what I am trying to do, please help
Ok here is a complete example,
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private flag As Boolean = False
Dim Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
flag = True
'Do
' do loop never see's a true flag
'Loop Until Completed
Thread.Sleep(500)
If Completed = True Then
Label1.BackColor = Color.Red
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(25)
Loop Until flag
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles
Button2.Click
Label1.Text = flag.ToString
End Sub
End Class
Now the concept is if you hit button1 and wait for background worker to complete, it should turn lable1 red. but it doesn't. The do loop looking for a true flag will spin forever locking the form up.
I have determined with this example that the flag is not set to true until you exit the sub. Hit Button1 again and lable1 turns red.
Thanks in advance for any answers.
This doesn't answer your question per se but I want to post a large code snippet so I'll post it as an answer. It demonstrates that what you think is the problem is not the problem, i.e. that a field's value changes as soon as you change it, even if that change is made from a BackgroundWorker.DoWork event handler.
Create a new Windows Forms application project, add a Button, a Label and a BackgroundWorker to your form and then paste in this code over the default code of the form:
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private flag As Boolean
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Label1.Text = flag.ToString()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Thread.Sleep(5000)
flag = True
BackgroundWorker1.ReportProgress(0)
Thread.Sleep(5000)
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Label1.BackColor = Color.Green
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Label1.BackColor = Color.Red
End Sub
End Class
Run the project and, when the form appears, start clicking the Button at a pace of a few times per second. You'll see that the value of the flag field, as displayed on the Label, changes from False to True as soon as the code to set it is executed in the DoWork event handler. The Label will turn green when that happens, so it's easy to spot. You'll know that it didn't wait until the DoWork event handler completes because the Label will turn red at that point.
EDIT: Now that you have provided all the relevant information, the issue is obvious. As I have already said, the moment you set a variable, that is the value of that variable. There's no waiting because there cannot be any waiting because there's nowhere to store a temporary value for the variable.
The reason that it looks otherwise is that your test code is faulty. If you use the debugger then you will see how. When you use a BackgroundWorker, the DoWork event handler is executed on a secondary thread but the RunWorkerCompleted event handler is executed on the UI thread. That means that your DoWork event handler can execute at the same time as your Click event handler for Button1 because they are on different threads, but the RunWorkerCompleted event handler cannot run at the same time, so it has to wait until the Click event handler completes before it can be executed. That means that the code to set the Completed field doesn't get executed until the Click event handler completes. It's not that the field value doesn't change when it's set but rather that it doesn't actually get set. If you place breakpoints on the two lines that access that Completed field then you'll see that.
The mistake you're making is trying to do something in that Click event handler after the DoWork event handler completes. That's wrong. That's exactly what the RunWorkerCompleted event handler is for. That's where you do UI work after the background work completes.
Also, you can get rid of that flag variable. Cancellation functionality is built into the BackgroundWorker class. Look at the CancelAsync method and the CancellationPending property.
Many thanks to "jmcilhinney" for his insights! I have figured out the code I was looking for!
This code allows me to stop and start a background thread safely by allowing the background thread to finish completely before restarting.
During the time that the background thread is stopped, user actions can perform operations without the worry of cross-threading or with thread.abort garbled code conditions.
Finally, stress-free threading!
I wish I could find the doc on MSDN that I read that stated the await async method was far superior to task.run but that's another argument.
This may not be the best code in the world but it works!
And in light of trying to rewrite all code in my project with async and await I'll stick with this!
Public Class Form1
Private flag As Boolean = False
Dim Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
flag = True
Await Task.Run(Sub()
Do
Loop Until Completed
End Sub)
If Completed = True Then
Label1.BackColor = Color.Red
End If
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(250)
Loop Until flag
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
End Class
For all of the academics out there this code is more appropriate.
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles
Button1.Click
BackgroundWorker1.CancelAsync()
Await Task.Run(Sub()
Do
Loop Until Completed
End Sub)
If Completed = True Then
Label1.BackColor = Color.Red
End If
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(250)
Loop Until BackgroundWorker1.CancellationPending
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
End Class
I have a main form with a progressbar at the bottom status strip. It is set to marquee style. I want it to keep animating when
I run a long function. But when the function hits, the form and everything on it freezes, so II used a backgroundworker
to run the long function. But this gave me the following error inside the MyClass.BigFunction() code.
Cross-thread operation not valid: Control 'frmMainNew' accessed from a thread other than the thread it was created on.
"frmMainNew" is the main form on which the progressbar and backgroundworker are. I pass the form as a parameter to the MyClass object
when I initialize it.
This is the first time I am using backgroundworker, so what else do I need?
I have already looked at these examples and tried them, but nothing works. (1, 2, 3, 4, 5, 6).
I have to use this for other functions too.
My code:
Private WithEvents bgw As BackgroundWorker
Private Sub frmMainNew_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
Me.SuspendLayout()
'Other Functions
w_AddBackgroundWorkerForProgressBar()
Me.ResumeLayout()
Catch ex As Exception
Scube.Planner.ErrorHandler.DisplayError(ex)
End Try
End Sub
Private Sub w_AddBackgroundWorkerForProgressBar()
bgw = New BackgroundWorker
AddHandler bgw.DoWork, AddressOf bgw_DoWork
AddHandler bgw.RunWorkerCompleted, AddressOf bgw_Completed
'AddHandler bgw.ProgressChanged, AddressOf bgw_ProgressChanged
End Sub
Private Sub MyButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton.Click
Try
bgw.WorkerSupportsCancellation = True
bgw.RunWorkerAsync()
'MyClass.BigFunction() <--- Originally called from here
Catch ex As Exception
Scube.Planner.ErrorHandler.DisplayError(ex)
Finally
Me.Cursor = Cursors.Default
End Try
End Sub
Private Sub bgw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles bgw.DoWork
'Do your lenghty operations here
MyClass.BigFunction()
System.Threading.Thread.Sleep(10000)
End Sub
Private Sub bgw_Completed(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
If Not IsNothing(e.Error) Then
MessageBoxEx.Show(e.Error.ToString)
End If
ProgressBar1.Hide()
End Sub
Private Sub w_ShowProgressBar()
ProgressBar1.Show()
Me.Refresh()
System.Windows.Forms.Application.DoEvents()
End Sub
You cant and should not access your frmMainNew from your 'BigFunction()'. It is working on a separate thread and does not have access to the UI thread. We need to see what you are doing inside your bigfunction to tell you the problem. Im going to take a guess and say you are trying to update the progressbar values from within that function? If this is so then is the incorrect way to do so.
What you need to do is, set the progress in your BigFunction like so:
bgw.ReportProgress(Progress/Percentage)
and have an event for the progress changed and inside of that event is where you update the progress bar.
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Progressbar1.value = e.ProgressPercentage
End Sub
I'm working on a custom GUI, my last step is for the button to change automatically check if the file exists or not on startup. The method below is my download/open button. Any help is appreciated!
Private Sub Button7_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button7.Click
If Dir("DownloadedFile.zip") <> "" Then
Process.Start("DownloadedFile.zip")
Else
WC.DownloadFileAsync(New Uri("http://download852.mediafire.com/3a688rz1a6ig/dk71cs34ihs3v6x/Devil+went+down+to+georgia.rar"), "DownloadedFile.zip")
MsgBox("Starting Download")
End If
End Sub
A subroutine is a seperate method that does not return a value. I would take the If statement out of your click eventhandler and put it in its own method as I stated like this.
Private Sub CheckForFile()
If Dir("DownloadedFile.zip") <> "" Then
Process.Start("DownloadedFile.zip")
Else
WC.DownloadFileAsync(New Uri("http://download852.mediafire.com/3a688rz1a6ig/dk71cs34ihs3v6x/Devil+went+down+to+georgia.rar"), "DownloadedFile.zip")
MsgBox("Starting Download")
End If
End Sub
Call it from your button like this.
Private Sub Button7_Click(sender As Object, e As EventArgs) Handles Button7.Click
CheckForFile()
End Sub
Then handle the Shown Event and call it from there. It will run as soon as the initial form is shown
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
CheckForFile()
End Sub
Responding to your comment You need to use the WebClient DownloadFileCompleted Event and the WebClient DownloadProgressChanged Event