Threading in VB.net - vb.net

I have a very basic doubt in vb.net threading.
I am having a function MyFunc1() which actually launches a form and asks for input from the user and returns a string. The return must be done only when user clicks a button called 'return' in the Form.
So I framed the function like this.
Public done as Boolean = true
Public str as String
Function MyFunc1() As String
Start Thread1 //launch UI as seperate thread
While done
End While //Infinite loop to hold the parent loop till done is made as false
return str
End Function
Function Thread1
//code to launch UI
End Function
Function onClickReturn //Function triggered when 'return' is pressed
str = EditText.text
done = false
End Function
The problem right now is Thread1 launches the UI but once the UI is launched Thread1 dies and so does the UI Panel.
Any ways to fix this?

I think what you are trying to do doesn't event need threads. Since your blocking the original thread anyways, there is really no need to create a second thread. Typically you just call
MyForm.ShowDialog()
That shows a modal dialog and will block the calling code at that line, allowing the UI to be displayed and used until the user dismisses it.

If you have a .Net thread object, you can block by calling Thread.Join.

Related

How to open a form within non-GUI code in VB.NET

I write a program in Visual Basic, which doesn't need - and doesn't have - a GUI unless a certain event happens, in which the user needs to be involved (and a MsgBox wouldn't suffice).
Now I want to call a form (which I have already created in the project) from my code. So I want to call
Public Class MyForm
{...} 'In my case empty
End Class
from within
Module MyMain
{...} 'do unrelated stuff
'Call MyForm somehow
{...} 'proceed stuff, knowing users input
End Module
I see the form for a very brief moment, but I can't get my program to halt and wait for me, while I am in the form.
The code in my desperation is:
Module MyMain
{...} 'do unrelated stuff
Dim wifo As New MyForm()
wifo.Show()
wifo.Visible = True
wifo.Activate()
wifo.Focus()
{...} 'proceed stuff, knowing users input
End Module
How does one do it?
If I understand you correctly, you want the Form.ShowDialog() method...
https://msdn.microsoft.com/en-us/library/c7ykbedk(v=vs.110).aspx

Key input functions for a VB class file

I am working on a little framework and I want to have a class file that contains functions I can run to check if a certain key has been pressed so other events can be run. The only code I have found online for similar things are written into the form itself and use something like "Handles Me.KeyPress". However, this handle function can't be used in a class file.
Public Function OnKeyPress(KeyToCheck As Keys)
If KeyPressed = KeyToCheck then
return true
else
return false
End If
End Function
I have tried:
Public Function OnKeyPress(KeyToCheck As Keys)Handles Formname.Keypress
If KeyPressed = KeyToCheck then
return true
else
return false
End If
End Function
However, this does not work. Any suggestions or work arounds would be greatly appreciated.
To get keyboard input, you need to have a window. All input goes to a window, and that window can then check for key presses that are sent to it.
To get global information, you'd need to install a hook, but those should generally be avoided.

Updating GUI items upon an event raised by a thread of another class VB.nET

How to update data in GUI with messages that are being received by a thread of another class ?
I have a class with a thread that receives data from a server. This thread raises an event whenever it gets a message from the server. This event is handled inside the Starter Class (main class which contains the GUI).
The event handler (say DisplayData() has to display the message received by the other class.
My code is like this
Class GUI
receiverObj = New Receiver()
Addhandler receiverObj.MessageAlert, Addressof DisplayData
...
...
Sub DisplayData()
Dim str As receiverObj.ReceiveData
lbEvents.Add.Items(str) ' lbEvents is a ListBox inside the GUI that displays messages from Receiver
End Sub
End Class
Class Receiver
Public Event MessageAlert()
Sub New ()
MyTcpClient = New TcpClient(hostIP, port)
MyTcpClient.GetStream.BeginRead(ReceiveData, 0, PacketSize, AddressOf ReceiveStream, Nothing)
End Sub
Public Sub ReceiveStream(ByVal ar As IAsyncResult)
Dim ByteCount As Integer
Try
ByteCount = MyTcpClient.GetStream.EndRead(ar)
Dim t As New Threading.Thread(Sub() RaiseEvent MessageAlert())
MyTcpClient.GetStream.BeginRead(ReceiveData, 0, PacketSize, AddressOf ReceiveStream, Nothing)
End Sub
End Class
The Window crashes or hangs and the listbox does not display data. Throws exception saying
Cross-thread operation not valid: Control xxx accessed from a thread other than the thread it was created on.
Can anybody suggest a way to fix this error ?
How to update data in GUI with messages that are being received by a thread of another class ?
Updates to GUI elements of a Windows application must take place on the thread that created the GUI.
To fix this, there's a method called Invoke that allows you to fire a delegate that can ensure control is passed to the GUI thread and perform the update you are attempting.
You need a few things to make this work:
A Delegate type , such as
Delegate Sub MyGUIUpdateDelegate()
A variable of the type of your delegate
Public myGUIUpdateDelegate as MyGUIUpdateDelegate
A method having a signature that matches the delegate and does the work:
Public Sub MyGuiEventHandler()
' Do work on proper GUI thread, via Control.Invoke,
' such as listbox population
If (Me.InvokeRequired) Then
Me.Invoke( myGUIUpdateDelegate)
Else
// do control specific work, we're on the GUI thread here
End If
End Sub
An assignment of the event handler to your delegate:
myGUIUpdateDelegate = New MyGuiUpdateDelegate(AddressOf myForm.MyGuiEventHandler)
A call to your updater method via Control.Invoke to the proper thread from the event thread (assuming
your form instance variable is named myForm):
myForm.Invoke(myForm.myGUIUpdateDelegate);
That's at least a framework that should help you get started. The idea is that the background thread that wants to induce the update should not (and in reality, cannot) make direct GUI updates. The proper way to initiate a context switch to the GUI thread is by calling the Invoke method to call the GUI updater on the proper GUI thread.
Additionally, if you need to pass parameters to your delegate, simply alter the signature of the Delegate you define to include the parameter(s), and modify the Invoke method to provide the arguments in the handler, and the 2nd argument to Invoke.
Hope this helps.

parallel event update UI

I have a class written in C#. In it I want to run a certain function in parallel on a list. After it completes on each item I would like to update a progress bar. However, I get very odd behavior from my program. It executes the event and reaches my sub but never proceeds to actually execute any code. Instead it just freezes. (I've mixed vb.net and c#. It will be rewritten at some point)
so in my windows form I call
progressBar.Visible = True
progressBar.Value = 0
progressBar.Maximum = dataGrid.SelectedRows.Count
AddHandler quoteManager.refreshStarted, AddressOf progressBarCounter
quoteManager.refreshAllAsync(list)
and the event is simply
Private Sub progressBarCounter()
Me.Invoke(Sub()
If progressBar.Value = progressBar.Maximum Then
progressBar.Visible = False
Else
progressBar.Value += 1
End If
End Sub)
End Sub
and in the quote manager class I have this defined.
public event Action refreshStarted;
public void refreshAllAsync(List<BindableQuote> bindableQuotes)
{
bindableQuotes.AsParallel()
.WithDegreeOfParallelism(10)
.ForAll((quote) => {
quote.refreshAll();
if (refreshStarted != null) { refreshStarted(); }
});
}
So for some reason I get it to enter progressBarCounter on each item in the list but it never exists. Instead it just keeps the form frozen.
I am not exactly sure this is what is happening, but it looks like progressBarCounter is blocking because you are calling Invoke. Should you be using BeginInvoke instead? Using BeginInvoke might solve the deadlock issue. See this post: What's the difference between Invoke() and BeginInvoke()
What appears to be happening here is that you access UI objects from multiple threads.
That's not supported. You'll have to run this code on a worker thread, and let it somehow accumulate progress, and send messages back to the UI thread. The BackgroundWorker class can help you implement the marshalling back to the UI thread.

Windows Forms with async socket; no text output

Currently I am having a weird problem which I simply do not understand. I have a simple GUI, with one button & one richeditbox. I have an async socket running, I am receiving some data over the network which I want to print to the gui(richeditbox). The async socket is being started when the user hits the button. So when I receive the network data I call a function which prints the data, here how it looks like (in form1 class):
Public Sub AddText(ByVal text As String)
Try
Console.WriteLine(text)
RichTextBox1.AppendText(text)
RichTextBox1.AppendText(vbNewLine)
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
End Sub
Then I simply do Form1.AddText(..) from my network class or a module (does it matter?). The problem is that nothing appears in the richeditbox, even though the AddText function is being called, no exceptions, no errors, simply nothing. I've looked thru it with the debugger, and "text" contained the data it had to print, but simply nothing appears.. Anyone have an idea?
If the socket is running on another thread (which, of course, it is because it's asynchronous), you may have to use InvokeRequired in order to get the RichTextBox to display the text. I had a similar issue with a listener on an asynchronous serial port listener.
I'm pretty sure David is right. Here's an example.
Delegate Sub AddTextDelegate(ByVal text as String)
Public Sub AddText(ByVal text as String)
If Me.InvokeRequired Then
Me.Invoke(new AddTextDelegate(AddressOf Me.AddText), new object() { text })
Else
Try
Console.WriteLine(text)
RichTextBox1.AppendText(text)
RichTextBox1.AppendText(vbNewLine)
Catch e as Exception
Console.WriteLine(e.ToString())
End Try
End If
End Sub
The deal is that controls have to be updated on the thread they were created on. It sounds like the AddText() routine is being called in the context of your async socket's thread. The AddText() routine will behave like a recursive function. The first time it's called, the InvokeRequired property will be true. This will cause it to be called again via the Invoke() call, which takes care of marshaling the data to the correct thread. The second time it's called, InvokeRequired will be false, and the control will be updated.
Fixed. I couldn't use Form1 to call the functions, because it's a type, its like a new var there with its own memory, since its a diff thread. So when I checked InvokeRequired, it said false because that Form1 belongs to that Thread, and thus no text was being displayed because I didn't even see the form. So just made a global var such as Public myForm As Form1 and assigned myForm to Form1 in Form1_Load.