I have a form that loads just fine, and I'm trying to fire off a task using a Background Worker as it loads.
I'm getting no errors with the code below, but the bw.DoWork event doesn't seem to be firing.
Am I missing something here? Thanks.
Here is my form Class -
Public Class mainForm
Dim objWorker As MyWorker
Private Sub mainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Call Me.loadForm()
End Sub
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
Call Me.closeForm()
End Sub
Private Sub loadForm()
Me.objWorker = New MyWorker ' Invoke the background worker
End Sub
Private Sub closeForm()
Me.objWorker.bw_Cancel() ' Cancel the background worker
Me.Close() ' Close the form
End Sub
End Class
Here is my BackgroundWorker Class -
Imports System.ComponentModel
Partial Public Class MyWorker
Private bw As BackgroundWorker = New BackgroundWorker
Public Sub New()
bw.WorkerReportsProgress = False
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
For i = 1 To 10
If bw.CancellationPending = True Then
e.Cancel = True
Exit For
Else
System.Threading.Thread.Sleep(500)
MsgBox("iteration " & i)
End If
Next
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
MsgBox("Complete!")
End Sub
Public Sub bw_Cancel()
If bw.WorkerSupportsCancellation = True Then
bw.CancelAsync()
End If
End Sub
End Class
add to MyWorker constructor ('new' method) this line:
bw.RunWorkerAsync()
Related
I am trying to implement BackgroundWorker in my vb.net code. I understand you cannot update the UI from the background worker. Since when setting breakpoints in my code in the Backgroundworker.DoWork sub I would get
Me.Accessibility.Object threw an exception of type 'System.InvalidOperationException'
Message "Cross-thread operation not valid: Control 'FrmLoad' accessed from a thread other than the thread it was created on."
To try to understand why this was happening I copied the code EXACTLY from
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netframework-4.7.2
and when setting a breakpoints again in the DoWork sub I get the same exception. I have tried several other microsoft code examples with the same issue. Is there something wrong with the code?
Imports System
Imports System.ComponentModel
Imports System.Windows.Forms
Namespace BackgroundWorkerSimple
Public Partial Class Form1
Inherits Form
Public Sub New()
InitializeComponent()
backgroundWorker1.WorkerReportsProgress = True
backgroundWorker1.WorkerSupportsCancellation = True
End Sub
Private Sub startAsyncButton_Click(ByVal sender As Object, ByVal e As EventArgs)
If backgroundWorker1.IsBusy <> True Then
backgroundWorker1.RunWorkerAsync()
End If
End Sub
Private Sub cancelAsyncButton_Click(ByVal sender As Object, ByVal e As EventArgs)
If backgroundWorker1.WorkerSupportsCancellation = True Then
backgroundWorker1.CancelAsync()
End If
End Sub
Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim worker As BackgroundWorker = TryCast(sender, BackgroundWorker)
For i As Integer = 1 To 10
If worker.CancellationPending = True Then
e.Cancel = True
Exit For
Else
System.Threading.Thread.Sleep(500)
worker.ReportProgress(i * 10)
End If
Next
End Sub
Private Sub backgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
resultLabel.Text = (e.ProgressPercentage.ToString() & "%")
End Sub
Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If e.Cancelled = True Then
resultLabel.Text = "Canceled!"
ElseIf e.[Error] IsNot Nothing Then
resultLabel.Text = "Error: " & e.[Error].Message
Else
resultLabel.Text = "Done!"
End If
End Sub
End Class
End Namespace
It's not actually hindering the code from running, but I want to make sure that the thread is actually remaining safe.
That sample code is written in C#, and you apparently left out a function and it’s call:
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
}
While this can be rewritten using addhandler, the vbish way to do this is to add a handles clause to you method declaration.
I believe this is what you are looking for:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
InitializeComponent()
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.WorkerSupportsCancellation = True
End Sub
Private Sub startAsyncButton_Click(sender As Object, e As EventArgs) Handles btn_Start.Click
If Not BackgroundWorker1.IsBusy Then
BackgroundWorker1.RunWorkerAsync()
End If
End Sub
Private Sub cancelAsyncButton_Click(sender As Object, e As EventArgs) Handles btn_Cancel.Click
If BackgroundWorker1.WorkerSupportsCancellation = True Then
BackgroundWorker1.CancelAsync()
End If
End Sub
Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Dim worker As BackgroundWorker = TryCast(sender, BackgroundWorker)
For i As Integer = 1 To 10
If Not BackgroundWorker1.CancellationPending Then
System.Threading.Thread.Sleep(500)
BackgroundWorker1.ReportProgress(i * 10)
Else
e.Cancel = True
Exit For
End If
Next
End Sub
Private Sub backgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
resultLabel.Text = (e.ProgressPercentage.ToString() & "%")
End Sub
Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If e.Cancelled = True Then
resultLabel.Text = "Canceled!"
ElseIf e.Error IsNot Nothing Then
resultLabel.Text = "Error: " & e.[Error].Message
Else
resultLabel.Text = "Done!"
End If
End Sub
The main thing it looked like you were missing was the Handles Event for the Background1.ProgressChanged
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 using the following code to get the size of files inside a directory
and put it in Label1:
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _
"\windows",Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly,_
"*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
Label1.Text = Label1.Text + filesizelabel.Length
Next
The problem is that i have more than 50 for each loops (a system cleaning app).
When I run the loops my app freezes until the loops finish, even if I run one loop.
Is there a solution to make it show the name of the current file? I tried this as well, but it also froze my application:
label2.text = foundfile
The application does not respond to any click, until it finishes the loops. It shows the size in Label1 and the last scanned file in Label2. This also freezes the application:
system.threading.thread.sleep(100)
Is there any alternative to foreach or a solution to fix this issue?
Here's a quick example using Async/Await with a Button Click() Handler:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Await Task.Run(Sub()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
End Sub)
Button1.Enabled = True
End Sub
For VB.Net 2010, try this instead:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim T As New System.Threading.Thread(AddressOf Worker)
T.Start()
End Sub
Private Sub Worker()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
Me.Invoke(Sub()
Button1.Enabled = True
End Sub)
End Sub
This is a prime candidate for a background worker.
Have a read about how they work, but at a high level the task is run in another thread with some events that you access in your main UI thread.
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If bw.WorkerSupportsCancellation = True Then
' this will allow the user to cancel the work part way through
bw.CancelAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' your slow code goes here
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
Derek has put a bad code, it's not working and Idle mind code does not work on .NET 2.0
Dereks approach is working if code is complete, as below:
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub app_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
..
End sub
Private Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'your work to not freeze form
end sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
I am doing some transition coding, using the .net transitions library found # http://code.google.com/p/dot-net-transitions/. I am trying to add an event to fire on transitions completed. In my sub, I have the following statements:
Private Sub btnLogin_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLogin.Click
If md5Password = rtnPassHash Then
AddHandler Me.TransitionCompletedEvent, AddressOf theHandlerFunction
Dim tr_empID = New Transition(New TransitionType_Linear(500))
tr_empid.add(txtEmployeeID, "BackColor", Color.LightGreen)
Dim tr_passw = New Transition(New TransitionType_Linear(500))
tr_passw.add(txtPassword, "BackColor", Color.LightGreen)
tr_empID.run()
tr_passw.run()
AddHandler Me.TransitionCompletedEvent, AddressOf theHandlerFunction
Dim tr_empID = New Transition(New TransitionType_Linear(500))
tr_empid.add(txtEmployeeID, "BackColor", Color.LightGreen)
Dim tr_passw = New Transition(New TransitionType_Linear(500))
tr_passw.add(txtPassword, "BackColor", Color.LightGreen)
tr_empID.run()
tr_passw.run()
end if
end sub
Outside of that sub I have:
Public Event TransitionCompletedEvent As EventHandler(Of Transition.Args)
Private Sub theHandlerFunction(ByVal sender As Object, ByVal args As Transition.Args) Handles Me.TransitionCompletedEvent
MsgBox("Event Fired")
End Sub
However, the event is not firing after the transition has finished. Why would this be?
Basic design:
Public Class Transition
Public Event TransitionCompleted(args As Transition.Args)
Public Sub SomeSub()
RaiseEvent TransitionCompleted(New Transition.Args With {set some properties})
End Sub
...
End Class
Public Class Form1
Private transition1 As New Transition
Private Sub Login_Click(...) ...
...
Addhandler transition1.TransitionCompleted, AddressOf TransitionCompleted
End Sub
Private Sub TransitionCompleted(args As Transition.Args) ' no handles clause
MessageBox.Show("event fired")
End Sub
End Class
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