.Net components hang when accessing GUI from events |
|
Date Entered: 12/6/2004
|
Last Update: 12/6/2004
|
APPLIES TO |
|
|
|
|
SYNOPSIS |
When I access the GUI from within the event of an asynchronous component,
In .Net, GUI components have to be accessed only from the GUI thread. If you try updating GUI components while inside an event of an asynchronous component, the application could freeze. |
SOLUTION |
IPWorks components fall in two categories in respect to threading behavior. Such separation is dictated from performance and
the nature of the implemented protocol. Ipport, Udpport, Icmpport, Mcast, Xmpp, Telnet, Mx, Dns are non blocking and asynchronous,
the rest of the components are blocking and synchronous.
The blocking/synchronous components are "plug and play" in the sense that there is absolutely no need to implement any extra
plumbing to have them work even inside GUI applications.
The non blocking/asynchronous components require extra plumbing when used inside GUI applications due to the .Net limitation that
"GUI components have to be accessed only from the GUI thread".
You connect, send data, and disconnect in a non blocking asynchronous manner by using methods such as Connect,
Send, and Disconnect.
There are respective events such as Connected, DataIn, and Disconnected that inform you in a later stage about the operation
result. Your method calls only "ask" the component to perform the task and the control is immediately returned to the calling thread
whereas the corresponding events are generated and fired from worker threads inside the component. If you try updating GUI
components while inside the event, the application could simply freeze.
In V6 of the .Net Edition, simply set the InvokeThrough property of the component to the instance of the form, and the component will take care of this
problem for you by automatically invoking onto the main thread, ie:
xmpp1.InvokeThrough = this;
Alternatively you could leave InvokeThrough to its default (null) and either not access the GUI from the event or do the invoking yourself.
---
In Compact Framework development, things are a bit different. There is no InvokeThrough property in the CF.Net Edition components, so
you'll need to handle this yourself. Here's a C# example, using the IPPort component, of how to handle the invoke in .Net CF:
nsoftware.IPWorks.IpportConnectedEventArgs e;
private void ipport1_OnConnected(object sender, nsoftware.IPWorks.IpportConnectedEventArgs e)
{
ipport1.DataToSend = "Thanks for connecting!";
this.e = e;
this.Invoke(new EventHandler(MyClientConnected));
}
void MyClientConnected(object sender, EventArgs e)
{
tbLog.Text = "Connected to remote host: " + this.e.Description;
}
---
In VB.Net, for example, using the XMPP component:
1. Create an object to hold the event args:
Private e As nsoftware.IPWorks.XmppMessageInEventArgs
2. Write your delegate function (I called it MyXmppMessageIn but you can call it whatever you want), where you will perform your GUI code:
Delegate Sub MyEventHandler(ByVal sender As Object, ByVal e As nsoftware.IPWorks.XmppMessageInEventArgs)
Private MyXmppMessageInD As MyEventHandler
...
MyXmppMessageInD = New MyEventHandler(AddressOf MyXmppMessageIn)
...
Sub MyXmppMessageIn(ByVal sender As Object, ByVal e As EventArgs)
'My gui code, use Me.e for event args
End Sub
3. Then from the IP*Works! Event itself, you can invoke MyXmppMessageIn:
Private Sub xmpp1_OnMessageIn(ByVal sender As Object, ByVal e As nsoftware.IPWorks.XmppMessageInEventArgs) Handles Xmpp1.OnMessageIn
Invoke(MyXmppMessageInD)
End Sub
|