Update control asynchronously - vb.net

I've been reading over the internet how to solve the problem of updating controls from different threads than the one that contains the control and also read "Pro VB 2010 and the .Net 4.0 Platform" and I start to wonder. Is the following code performing in synchronous fashion?
Private Sub SaveData(ByVal filepath As String)
If InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf SaveData))
Else
... the actual code
End If
End Sub

It depends widely on your calling context.
If you are calling it from the UI thread or your main thread, then yes it will function synchronously.
If you call it from a thread that is not your UI or main thread, then it is going to function asynchronously to your UI or main thread, but synchronously with the calling thread, waiting until the UI or main thread is done processing the delegate.
So it can go both ways. It can operate synchronously and asynchronously. I think what you are missing is that the code itself does not preform in a synchronous or asynchronous fashion, the execution of the code is either synchronous or asynchronous.
The reason why you make an update function:
Private Sub UpdateLabel(byval tLabel as Label, byval Value as String)
If tLabel.Invokerequired Then
Me.Invoke(New MethodInvoker(AddressOf UpdateLabel)
Else
tLabel.Text = Value
End If
End Sub
Is so that you can call it whenever you want to change the text, without having to worry about a cross thread exception.
This method would be safe to call from the UI thread during a click event and it would also be safe to call from a along running background thread, because the change would still only ever be made on the UI or Main thread. You use code like this to ensure synchronicity with your changes to controls owned by other threads.
This style of updating is meant to synchronize your changes and avoid cross threading.

The Invoke method is a synchronous call that will block the calling thread until the delegate finishes executing on the UI thread.
If you want to calling thread to continue immediately (before the code on the UI thread runs), call BeginInvoke(), which is asynchronous.

I know this question is a little old, but here's a modification of Pow-Ian's answer that worked for me.
Private Sub UpdateLabel(ByVal tControl As Control, ByVal Value As String)
If tControl.InvokeRequired Then
tControl.Invoke(Sub() tControl.Text = Value)
Else
tControl.Text = Value
End If
End Sub
This should work to change the text for any control using the UI thread, even if it is called from an async method.

Related

How to update a rich text box from a background worker using a delegate

Ok I'm pretty new to using threads but so far I've managed to get the following:
Private Delegate Sub dlgUpdateText(text as string)
Private Sub UpdateStatus(text as string)
If rtxStatus.InvokeRequired then
Dim dlg as new dlgUpdateText(AddressOf UpdateStatus)
Me.Invoke(dlg, text)
Else
rtxStatus.text = text
End If
End Sub
and from my Async BackgroundWorker I call
UpdateStatus("Some text")
which seems to work ok however in my original code (which generates errors because I'm updating the control from the wrong thread) I used the following code to append to the (rich)textbox:
rtxStatus.Select(rtxStatus.TextLength, 0)
rtxStatus.SelectionColor = Color.Red
rtxStatus.AppendText("Some error occurred gathering data")
My question is how should I modify my new code to allow me to do this rather than just replace the text? I have read several guides on using delegates but I'm still lost on a few points so I don't really know what's going on with the code I have.
Bonus questions (which probably serve best to show what needs explaining to me!):
What does the Delegate Sub actually do? It doesn't seem to serve any purpose other than hold the property (text) that was already passed to the main Sub (UpdateStatus)?
What is happening when Me.Invoke is called? Me is the current form so when I pass the Delegate Sub and the text where is it specified that the text should be passed to the rtxSTatus.Text property?
UpdateStatus runs in the main thread (?) and is called from one of the background worker threads so why is the Invoke even necessary or is the UpdateStatus Sub run under the background thread that called it? When I altered the text that is applied in the Else statement to see which was run it seems that Invoke is never used to change the text in the box.
Any help would be really appreciated, I'm completely baffled by this - thanks!
Rather than creating a delegate I would suggest using the existing methods offered from a backgroundworker. The backgroundworker provides two methods to access the main thread:
The ProgressChanged event to update the main thread during backgroundworker processing and the RunWorkerCompleted event to update the main thread once the backgroundworker process is complete.
You can find this information and how to implement it from the following link:
http://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx

Using a thread with designer objects vb.net

I have a big problem. I am currently designing an Antivirus, and it is coming along very well. But having all the scanning engines running on the same thread, I.E. the main one, is causing the app to lag in loading, and to become unresponsive during processes. I have tried implementing multithreading to increase the speed and overall performance of my application. But, every time that I try, i get the error of cross threading, I.E. I cannot use the form designers progress bars, buttons and labels etc. I just want to know why this error is thrown up, and how to fix it.
Thanks in Advance!
Use InvokeRequired to check which thread you are calling from, if you're not in the UI thread then InvokeRequired is True, and so you can invoke a delegate from the UI Thread to safely alter the Control:
Public Sub SetText(ByVal text As String)
If (Me.InvokeRequired) Then
'Invoke a delegate from the UI Thread
Me.Invoke(DirectCast(Sub() Label1.Text = "Test", MethodInvoker))
Else
Button1.Text = text
End If
End Sub
It is unsafe to call a control from a thread other than the one that created the control without using the Invoke method. Take a look at this example: http://msdn.microsoft.com/library/ms171728%28v=vs.110%29.aspx
Set your form property: CheckForIllegalCrossThreadCalls to false. The you are getting no errors any more.
Furthermore you must show, that you get a reference between your Thread and your form Controls. Otherwise you change the Controls of the Thread handled window (which you don't see).
Try to write Methods or Functions with By Ref parameter, to share your controls.

VB.NET Invalid cross-thread access

I have a background worker control on a form.
In this form I have another form that shows a progress:
Private _fWait As frmWait
I am updating this form, change its label to tell the user what is currently going on.
When the background worker is finished, I want to close this form _fWait.
I am using
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'do the background worker stuff. I have not stated it here because it is not important
_fWait.Close()
_bDone = True
End Sub
But I am getting the error "Invalid cross-thread access: The access to the control frmWait occured from a diffent thread than the thread it was created by." on the line "_fWait.Close()".
Can somebody tell me what I did wrong?
Thank you!
When you call _fWait.Close() is has to be called on the UI thread whereas the BackgroundWorker1_DoWork handler will be running on a different thread which is why you are getting the error. There are two options for closing the form:
1) Continue to close the form on the DoWork handler but marshal the request onto the UI thread using something like the code below:
this.Invoke(() => _fWait.Close());
It's been a while since I've done VB, so you'll have to convert the C# code into VB...sorry.
2) Handle the RunWorkerCompleted event and close it there. If you BackgroundWorker was started on
the UI thread, then the RunWorkerCompleted will also be called on the UI thread.
If I remember right the ProgressedChanged event will also be called on the UI assuming that the BackgroundWorker was created on the UI thread. If the BackgroundWorker is created another another thread, then the RunWorkerCompleted and ProgressChanged will also be called on a separate thread and you will have to marshal calls to the UI as described above in step 1.
In simple programs, you can ignore cross-thread errors by adding this to your Form.Load method:
CheckForIllegalCrossThreadCalls = False
Note that it can cause problems when running multiple asynchronous threads that access shared data, so don't use it indiscriminately.
If multiple threads cause a shared routine to run, use SyncLock to prevent multiple simultaneous instances.

Does DoEvents effect only the current thread?

I have a simple 'Working' form that runs on its own thread to keep the user informed that the application hasn't died during long running operations. In order to get the working form to update I had to insert a DoEvents() call.
I'm curious, will this only pump messages for the current thread I'm in, or will it do it for the whole application? I would prefer that the main window stay unresponsive till the operation finishes, so I'm curious as to the behavior. Below is the code for the working form.
Just to be clear, I'm fine with the code I have, but I would like to know how DoEvents() behaves with threads.
Public Class frmWorking
''' <summary>
''' Creates and starts a new thread to handle the Working Dialog
''' </summary>
''' <returns>The thread of the Working dialog.</returns>
''' <remarks></remarks>
Public Shared Function StartWait() As WorkingFromToken
Dim th As New Threading.Thread(AddressOf ShowWait)
Dim token As New WorkingFromToken
th.Start(token)
Return token
End Function
Private Shared Sub ShowWait(token As WorkingFromToken)
Dim frm As New frmWorking
Try
frm.Show()
Do
If frm.txtWait.Text.Length > 45 Then
frm.txtWait.Text = "Working"
Else
frm.txtWait.Text &= "."
End If
Windows.Forms.Application.DoEvents()
Threading.Thread.Sleep(250)
Loop While token.Running
frm.Hide()
Catch ex As Threading.ThreadAbortException
Threading.Thread.ResetAbort()
frm.Hide()
Return
End Try
End Sub
End Class
DoEvents will only pump the current UI thread.
However, I do not recommend your approach.
Instead, you should do your work on a background thread, and show a modal progress form on the UI thread and update it using BeginInvoke or a BackgroundWorker.
DoEvents will only effect the thread from which it is called. It will dequeue all windows messages posted to that thread and dispatch them accordingly. After all messages have been dispatched it will return back to the caller.
I have a couple of other observations about your code though.
You have basically created your own crippled version of a message loop by calling DoEvents repeatedly in a loop. It would be better to just call Application.Run to initiate a full blown message loop.
Creating a message loop on a thread other than the main UI thread is rarely a good idea. There are some weird things that occur that are hard to deal with. For example, a modal dialog box from one thread could overlap a modal dialog box from another.
Attempting to catch a ThreadAbortException is pointless in most situation. If you ever get this exception then it is possible (perhaps even likely) that the state of the entire AppDomain has been corrupted. It is better to tear down the application domain than to try to gracefully deal with it. This is because the exception could be injected at any point during the execution of the thread and those injection points could be in the middle or a write, a lengthy operation, or otherwise some unsafe point.
As a corollary to the point above do not use Thread.Abort to terminate another thread. There are many too many things that can go wrong. It is better to cause the thread to end gracefully using safer mechanisms.

running vb.net in a new thread

how do I force a particular set of vb.net codes to run in a new thread or process.?
Edit 1: I am trying TTS in vb.net but whenever click the play button , The whole program freezes and I cannot do anything else until the speech is over
In a comment below you mention the library you are using and that changes this whole answer.
Answer to your problem:
In your case since you are using the SAPI.SpVoice library you don't need to do any work related to spinning up background threads and such since that object support asynchronous playback. Check out the arguments of the Speak method. Here is the documentation: http://msdn.microsoft.com/en-us/library/ms723609(v=vs.85).aspx
Answer to your question as it is posed:
The simplest method is to use a background worker process to run some code. This will allow your program to run some long process and not block the UI thread. The background worker even provides events to notify your UI thread of it's progress.
Here is an link to MSDN http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
The background worker is a simple way to spin off some work onto another thread, there are other tools for more complex scenarios. In essence you create an instance of a background worker object and add your long-running code to a function that is created to handle it's DoWork event. There are ProgressChanged and RunWorkerCompleted events that should be handled as well. There are methods that can be called to cancel the process. The MSDN link above provides a couple of good complete code examples.
Wrap the "set of codes" into a method and dump it onto the ThreadPool
ThreadPool.QueueUserWorkItem(AddressOf MyMethod)
the ThreadPool suggestion worked for me for a WP7 Silverlight app:
Private Sub AddAnagrams()
ClearAnagramsList()
UpdateAnagramsCount() 'update the count first, then add the items
ShowCalculating(True)
ThreadPool.QueueUserWorkItem(AddressOf UpdateAnagramsOnUIthread)
End Sub
Private Sub UpdateAnagramsOnUIthread()
Dispatcher.BeginInvoke(AddressOf UpdateAnagrams)
End Sub
Private Sub UpdateAnagrams()
ListAnagrams.ItemsSource = _Combinator.CombinedItems 'this is a virtualized datasource
ShowCalculating(False)
End Sub
Private Sub ShowCalculating(ByVal flag As Boolean)
LblCalculating.Visibility = If(flag, Windows.Visibility.Visible, Windows.Visibility.Collapsed)
End Sub