Many requests of Silverlight are basically asynchronous. Both WCF and WEBCLIENT are used for asynchronous requests and an event is provided for callback execution. When using WCF and WEBCLIENT to communicate with the server, we usually don't have any trouble, because both the WCF class method and WEBCLIENT execute and delegate events on the main thread. Of course, the callback event is also run on the main thread. However, when we use Socket or HttpWebRequest (HttpWebRequest itself is useless, but it seems that others encounter the same problem when using it) in the callback event, if you try to modify the UI or the like, the error "cross-thread access is invalid" will occur.
Why? For Socket, asynchronous callback events are not defined on Socket objects, but on their SocketAsyncEventArgs objects. asynchronous callback events must be implemented, of course, Silverlight will create a subthread to run this asynchronous event. Therefore, although Socket and SocketAsyncEventArgs are defined on the main thread, after the Socket executes the asynchronous method, the SocketAsyncEventArgs is delegated to an asynchronous subthread, so the Event Callback defined on SocketAsyncEventArgs is also a subthread. In this case, of course, the callback function of the subthread does not allow operations on objects in the main thread. Therefore, modifying the UI in the callback event of the Socket parameter will cause an "invalid cross-thread access" exception.
How can this problem be solved?
Silverlight provides a synchronization context class SynchronizationContext class (under the System. Treading namespace). This class provides the basic functions to spread the synchronization context in various synchronization models.
That is to say, you can use this class to schedule methods on different threads to a specified thread.
The following uses Socket as an example:
Xaml: Very simple. It is a button and a text box. The button has a Click event and is bound to the OnSend method.
Code
<UserControl x:Class="SilverlightTest.Socket1"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
<Grid.RowDefinitions >
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox x:Name="txtToSend" Grid.Row="0"/>
<Button Grid.Row="1" Click="OnSend" Content="Send" Margin="20" />
</Grid>
</UserControl>
Background code
Code
1 using system;
2
3 using system. Collections. Generic;
4
5 using system. LINQ;
6
7 using system. net;
8
9 using system. windows;
10
11 using system. Windows. controls;
12
13 using system. Windows. documents;
14
15 using system. Windows. input;
16
17 using system. Windows. Media;
18
19 using system. Windows. Media. animation;
20
21 using system. Windows. shapes;
22
23 using system. net. Sockets;
24
25 using system. Threading;
26
27 using system. text;
28
29
30 namespace silverlighttest
31
32 {
33
34 public partial class socket1: usercontrol
35
36 {
37
38 public socket1 ()
39
40 {
41
42 initializecomponent ();
43
44}
45
46 system. net. sockets. Socket socket;
47
48 synchronizationcontext SYN;
49
50
51 // click the send message button
52
53 void onsend (Object sender, eventargs ARGs)
54
55 {
56
57 SYN = synchronizationcontext. Current;
58
59 socket = new System. Net. Sockets. Socket (AddressFamily. InterNetwork, SocketType. Stream,
60
61 ProtocolType. Tcp );
62
63 SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs ()
64
65 {
66
67 RemoteEndPoint = new DnsEndPoint (
68
69 Application. Current. Host. Source. DnsSafeHost, 4502)
70
71 };
72
73 socketArgs. Completed + = OnOperationCompleted;
74
75 socketArgs. UserToken = socket;
76
77 socket. ConnectAsync (socketArgs );
78}
79
80
81 // callback function
82
83 void OnOperationCompleted (object sender, SocketAsyncEventArgs e)
84
85 {
86 syn. Post (GetText, "OK ");
87}
88
89
90 void GetText (object str)
91
92 {
93 txtToSend. Text = str. ToString ();
94}
95
96}
97
98}
99
This Code creates a Socket and its parameters, binds the parameters to a callback event, and then executes the connection. After the connection is completed, calls back OnOperationCompleted.
If you want to write txtToSend directly to the callback function. text = "XXX", will certainly cause cross-thread exceptions. Therefore, we use the syn Post method of the instance of SynchronizationContext defined on the main thread, schedule the operation of the callback thread to the GetText method on the main thread and pass the "OK" parameter. In this way, the callback function schedules the operation to the GetText method, then, you can modify the attributes on the UI in the GetText method.
When SynchronizationContext is defined earlier, we instantiate it as SynchronizationContext. current, that is, when the Current thread defines and instantiates SynchronizationContext, It is performed on the main thread. Therefore, the context or thread of Current refers to is the main thread, so in later callback functions, scheduling GetText is scheduled to the main thread. The main thread can modify the UI, so GetText can modify the UI.