How to use Backgroundworker to load form? - vb.net

When I try to load a form with a backgroundworker class, it throws an exception because I tried to access an object that was created by another thread, not by the current thread. How to load that form by backgroundworker ?
(I am looking for marquee progress bar while the form is loading)

You can't change any controls in the DoWork section of a backgroundworker since it's a separate thread. You'll want to process any data in the DoWork section, then update your form in the RunWorkerCompleted section.
Example:
Private WithEvents bgwTasks As New BackgroundWorker
Private Sub bgwTasks_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwTasks.DoWork
e.Result= GetDatabaseTable()
End Sub
Private Sub bgwTasks_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwTasks.RunWorkerCompleted
If e.Result IsNot Nothing Then
DataGridView1.DataSource = e.Result
End If
End Sub
Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If bgwTasks.IsBusy = False Then
bgwTasks.RunWorkerAsync()
End If
End Sub

This will disable checking for Cross Threads
Me.CheckForIllegalCrossThreadCalls = False
I still suggest you search for Cross Thread regarding the proper use of the BackgroundWorker Class.

There is no problem to initialize the second form in another thread, but if you want to use it in the context on your main form, you have to use original thread. The follows code creates and initializes new form in background worker and then shows it when initialization is completed in appropriate event handler:
Public Class Form1
Dim form2 As Form2
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
form2 = New Form2()
form2.TextBox1.Text = "Text"
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
form2.Show()
End Sub
End Class
You can use ProgressChanged event of background worker for the purpose of report to progress bar.

You cannot do this. Forms have Thread-Affinity. ie. they can ONLY be accessed by the thread that instantiated the Form. You background thread just became the UI thread for the form!
So what you MUST do is marshal the call onto the correct thread.
Test InvokeRequired. If true, instantiate a delegate, call BeginInvoke and IMMEDIATELY return.
Now the call will be marshaled to the correct thread.

Related

Access Main thread data from the Background thread

I´m using a backgroundworker to handle processing of data while the user is still free to for instance click another button that aborts the process.
However, the code in backgroundworker requires several pieces of data, for instance it needs to know whether a radio button is checked.
Is there a way to access data in another thread from the background thread? Or should I create global variables that hold this information?
Public Class Test
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Button1.Content = "Working..."
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If RadioButton1.IsChecked Then
MsgBox("It works")
End If
End Sub
End Class
You can pass the RadioButton checked state to the RunWorkerAsync method like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync(RadioButton1.Checked)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim checked As Boolean = CBool(e.Argument)
If checked Then
MessageBox.Show("It works")
End If
End Sub
Let me know if you need to be checking the RadioButton continuously from inside a loop, as that would be different.

How does the ProgressBar.PerformStep() function work?

I'm quite confused about progress bar in VB.net, here is my code
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ProgressBar_my.Minimum = 0
ProgressBar_my.Maximum = 10
ProgressBar_my.Step = 1
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProgressBar_my.PerformStep()
Threading.Thread.Sleep(5000)
If 1 = 1 Then
ProgressBar_my.PerformStep()
ProgressBar_my.PerformStep()
End If
'Threading.Thread.Sleep(2000)
End Sub
For the above code, what I expected is that after I click Button1, the progress bar will increase the progress status by 1, then it will pause for 5 sec, then it will increase the progress status by 2 at once.
However, after I ran the above code, what I saw was that after I click Button1, the progress bar will increase by 3 continually after 5 sec.
Can someone tell me why it behaves like this and How should I program my code so that I can increase by 1, then pause 5 sec and then increase by 2?
Thanks in advance!
I think what you are seeing (or not seeing) is the fact that the progress bar takes a finite amount of time to advance each step.
When you call Threading.Thread.Sleep on the UI thread this stops the progress bar from being redrawn until after the Sleep
What you should do is update the progress bar on a background worker instead, then I think you will see the effect you desire.
Add a BackgroundWorker to your form
Change your button click code to start the worker:
Private Sub frmSO_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ProgressBar_my.Minimum = 0
ProgressBar_my.Maximum = 10
ProgressBar_my.Step = 1
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync
End Sub
Then perform the update in the DoWork event:
'define a delegate to handle updates to the UI thread
Private Delegate Sub PerformStepCallback()
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim cb As PerformStepCallback = AddressOf ProgressBar_my.PerformStep
Me.BeginInvoke(cb)
Threading.Thread.Sleep(5000)
Me.BeginInvoke(cb)
Me.BeginInvoke(cb)
End Sub
The problem is that you call Threading.Thread.Sleep(5000), which will, well, pause the current thread. But that also means that the window won't be redrawn, so you see the effect of the first call to PerformStep only after the thread is no longer paused.
You can use another thread for wait and the second update; the easiest way in this case is using the Task Parallel Library:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProgressBar1.PerformStep()
Task.Delay(5000).ContinueWith(Sub()
ProgressBar1.PerformStep()
ProgressBar1.PerformStep()
End Sub, TaskScheduler.FromCurrentSynchronizationContext)
End Sub
Thanks Matt Wilko for giving me the clue. Here is my code.
From the Components tab of the Toolbox, add a BackgroundWorker component named backgroundWorker2. Create DoWork and ProgressChanged event handlers for the BackgroundWorker.
The following code will initiate the Component.
Public Sub New()
InitializeComponent()
BackgroundWorker2.WorkerReportsProgress = True
BackgroundWorker2.WorkerSupportsCancellation = True
End Sub
Call RunWorkerAsync when the button is clicked and the background worker is not running.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If BackgroundWorker2.IsBusy <> True And ProgressBar_my.Value < 6 Then
BackgroundWorker2.RunWorkerAsync()
End If
End Sub
Private Sub BackgroundWorker2_DoWork_1(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles BackgroundWorker2.DoWork
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
worker.ReportProgress(1)
System.Threading.Thread.Sleep(2000)
worker.ReportProgress(1)
worker.ReportProgress(1)
End Sub
The following event is raised when every time the ReportProgress method is called.
Private Sub backgroundWorker2_ProgressChanged(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles BackgroundWorker2.ProgressChanged
ProgressBar_my.PerformStep()
End Sub
More information about the BackgroundWorker:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx

Visual Basic 2010 Live Logger

Hello I am trying to make a live logger in my program so I can keep track of the progress.
Like this:
http://i.stack.imgur.com/cmcTp.png
How do I make something like this.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
You can use background worker, with a listbox to accomplish what you are asking. On the reports progress method of background worker, you can specify the "userstate" object to "log" to controls on the UI thread, just make sure to set 'worker reports progress' to true:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'where you call your worker to do work
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'make sure to set worker reports progress to true
BackgroundWorker1.ReportProgress(0, "About to Messagebox") 'where 0 is a progress percent if you want it and the string is overloaded
MsgBox("This is to show how to report before an event!")
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'
ListBox1.Items.Add(e.UserState)
End Sub
End Class

Disposing background worker does not work

I have a background worker that calls a form, holding a gif animation. The purpose is to display the animation while process is underway but it should close when the process is done. But it does not close even after completion of the process. Please help.
Thanks
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
frmAnimation.ShowDialog()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
Dim sqldatasourceenumerator1 As SqlDataSourceEnumerator = SqlDataSourceEnumerator.Instance
datatable1 = sqldatasourceenumerator1.GetDataSources()
DataGridView1.DataSource = datatable1
'I have tried CancelAsync, but did not work
BackgroundWorker1.CancelAsync()
frmAnimation.Dispose()
End Sub
BackgroundWorkers are intended to actually do the "work" of the background operation, so the main UI thread can continue rendering things onto the screen. I suspect you want the GetDataSources() function call to be done within the BackgroundWorker thread.
Try switching what's in your button click function and what's in the DoWork function of your BackgroundWorker. Specifically, I mean something like this:
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim sqldatasourceenumerator1 As SqlDataSourceEnumerator = SqlDataSourceEnumerator.Instance
datatable1 = sqldatasourceenumerator1.GetDataSources()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
frmAnimation.ShowDialog()
End Sub
And in addition, add some code to the RunWorkerCompleted event to handle what should be done upon completion of your background operation.
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
DataGridView1.DataSource = datatable1
frmAnimation.Close()
End Sub
You may also want to consider using frmAnimation.Show() instead of frmAnimation.ShowDialog() depending on if you want the procedure to be modal or modeless. You can read more about that here.

Threading functions with VB.net

I'm trying to thread my functions so the main GUI of my application doesn't fail to respond while the tasks are running.
At the moment my form1.vb is something like this. I've cut it down as to reduce the text, but everything works fine:
Public Class MAIR
Private Sub InstallTheAgent_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InstallTheAgent.Click
InstallAgentWorker.RunWorkerAsync()
End Sub
Private Sub InstallAgentWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles InstallAgentWorker.DoWork
'Do some stuff
End Sub
Private Sub InstallAgentWorker_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles InstallAgentWorker.RunWorkerCompleted
' Called when the BackgroundWorker is completed.
MsgBox("Installation of the Agent Complete")
ProgressBar1.Value = 0
End Sub
End Class
From my understanding, when the button "Open" is pressed, it calls the function Install and it should start this off in a separate thread, however this doesn't work.
This seems to work, but the GUI still locks up, suggesting its not in a separate thread
I recommend using the BackgroundWorker Class to implement this kind of basic threading. You don't have a lot going on here so the basic implementation should suffice. Here is a snippet of how I would go about doing it. My method is a bit too complex (I have base classes and events wired up to catch a lot of worker events) to list here succinctly, so I'll abbreviate.
Public Class MAIR
Dim worker as BackgroundWorker
Private Sub Open_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Open.Click
worker.RunWorkerAsync()
End Sub
Protected Sub Worker_DoWork(ByVal sender as Object, ByVal e as DoWorkEventArgs)
Call Shell("psexec.exe", AppWinStyle.Hide, True)
End Sub
Protected Sub Worker_ProgressChanged(Byval sender as Object, ByVal e as ProgressChangedEventArgs)
' You can track progress reports here if you use them
End Sub
Protected Sub Worker_RunWorkerCompleted(Byval sender as Object, ByVal e as RunWorkerCompletedEventArgs)
' Report back to the main application that the thread is completed
End Sub
Private Sub Install_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
worker = New BackgroundWorker()
' Add the event wire-ups
' ### This event wire-up establishes the link
' ### that is used by RunWorkerAsync to initiate
' ### the code you want to run asynchronously
AddHandler worker.DoWork, AddressOf Worker_DoWork
AddHandler worker.ProgressChanged, AddressOf Worker_ProgressChanged
AddHandler worker.RunWorkerCompleted, AddressOf Worker_RunWorkerCompleted
End Sub
End Class
This is all translated from C#, so treat it as psuedo-code. The important piece is the documentation of the BackgroundWorker and its behaviors. There is a lot of terrific functionality in this class that takes the headaches of threads away for simple usages. This class is located in System.ComponentModel so you'll need the reference and an Imports statement.
Edit: Something I forgot to mention is that once the worker is fired asynchronously, the only manner of tracking it or communicating with the main application is through the ProgressChanged and RunWorkerCompleted events. No global variables or cross-thread items will be available, so you'll have to use the built-in properties to pass in any values (such as computerName it looks like) that you may need during the run.