How to update the UI control value in WinForm/Silverlight multi-thread programming, winformui
In a single-threaded winfom program, it is easy to set the value of a control. this. TextBox1.value = "Hello World! "; This is done, but if you do this in a new thread, for example:
Private void btnSet_Click (object sender, EventArgs e)
{
Thread t = new Thread (new ParameterizedThreadStart (SetTextBoxValue ));
// Of course, you can also use an anonymous delegate to write Thread t = new Thread (SetTextBoxValue );
T. Start ("Hello World ");
}
Void SetTextBoxValue (object obj)
{
This. textBox1.Text = obj. ToString ();
}
During running, a heartless error is reported:
The Inter-thread operation is invalid: It is accessed by a thread that does not create the control "textBox1.
The reason is that the UI control in winform is NOT thread-safe. If you can change its value in any thread at will, you can create a thread and I will create a thread, everyone is scrambling to change the value of "TextBox1". If there is no order, the world will be messy...
Solution:
1. Control. checkforillegalcrossthreadcils = false;) -- valid only for Winform
Using System;
Using System. Threading;
Using System. Windows. Forms;
Namespace ThreadTest
{
Public partial class Form1: Form
{
Public Form1 ()
{
InitializeComponent ();
Control. checkforillegalcrossthreadcils = false; // This row is critical.
}
Private void btnSet_Click (object sender, EventArgs e)
{
Thread t = new Thread (new ParameterizedThreadStart (SetTextBoxValue ));
T. Start ("Hello World ");
}
Void SetTextBoxValue (object obj)
{
This. textBox1.Text = obj. ToString ();
}
}
}
Set Control. checkforillegalcrossthreadcils is false, which is equivalent to not detecting conflicts between threads and allowing various threads to be messed up at will. Of course, the final TextBox1 value is unpredictable, only days know, but this is also the most effort-saving method.
2. Use delegate call-the most common method (only valid for WinForm)
Using System;
Using System. Threading;
Using System. Windows. Forms;
Namespace ThreadTest
{
Public partial class Form1: Form
{
Delegate void D (object obj );
Public Form1 ()
{
InitializeComponent ();
}
Private void btnSet_Click (object sender, EventArgs e)
{
Thread t = new Thread (new ParameterizedThreadStart (SetTextBoxValue ));
T. Start ("Hello World ");
}
Void SetTextBoxValue (object obj)
{
If (textBox1.InvokeRequired)
{
D d = new D (DelegateSetValue );
TextBox1.Invoke (d, obj );
}
Else
{
This. textBox1.Text = obj. ToString ();
}
}
Void DelegateSetValue (object obj)
{
This. textBox1.Text = obj. ToString ();
}
}
}
3. Use SynchronizationContext context-the most mysterious method (Winform/Silverlight can be used)
The reason why it is mysterious is that the msdn official explanation of it is also unclear.
Using System;
Using System. Threading;
Using System. Windows. Forms;
Namespace ThreadTest
{
Public partial class Form1: Form
{
Public Form1 ()
{
InitializeComponent ();
}
Private void btnSet_Click (object sender, EventArgs e)
{
Thread t = new Thread (new ParameterizedThreadStart (Run ));
MyPram _ p = new MyPram () {context = SynchronizationContext. Current, parm = "Hello World "};
T. Start (_ p );
}
Void Run (object obj)
{
MyPram p = obj as MyPram;
P. context. Post (SetTextValue, p. parm );
}
Void SetTextValue (object obj)
{
This. textBox1.Text = obj. ToString ();
}
}
Public class MyPram
{
Public SynchronizationContext context {set; get ;}
Public object parm {set; get ;}
}
}
4. Use BackgroundWorker-the lazy method (applicable to Winform and Silverlight)
BackgroundWorker will open another background thread outside the main thread. We can put some processing in the background thread for processing. After completion, the background thread will pass the result to the main thread and end itself at the same time.
Using System;
Using System. ComponentModel;
Using System. Windows. Forms;
Namespace ThreadTest
{
Public partial class Form1: Form
{
Public Form1 ()
{
InitializeComponent ();
}
Private void btnSet_Click (object sender, EventArgs e)
{
// MessageBox. Show (Thread. CurrentThread. ManagedThreadId. ToString ());
Using (BackgroundWorker bw = new BackgroundWorker ())
{
Bw. RunWorkerCompleted + = new RunWorkerCompletedEventHandler (bw_RunWorkerCompleted );
Bw. DoWork + = new DoWorkEventHandler (bw_DoWork );
Bw. RunWorkerAsync ("Hello World ");
}
}
Void bw_DoWork (object sender, DoWorkEventArgs e)
{
// MessageBox. Show (Thread. CurrentThread. ManagedThreadId. ToString ());
E. result = e. argument; // here, the parameter is simply returned as the result. Of course, after complicated processing, return the desired result (the operation is completed on another thread)
}
Void bw_RunWorkerCompleted (object sender, RunWorkerCompletedEventArgs e)
{
// The background thread is completed and the main thread is returned, so you can directly use the UI control.
This. textBox1.Text = e. Result. ToString ();
// MessageBox. Show (Thread. CurrentThread. ManagedThreadId. ToString ());
}
}
}
5. Dispatcher. BeginInvoke -- the exclusive secret of Silverlight
Code using System. Threading;
Using System. Windows. Controls;
Using System. Windows. Input;
Namespace ThreadTest
{
Public partial class MainPage: UserControl
{
Public MainPage ()
{
InitializeComponent ();
}
Private void LayoutRoot_MouseLeftButtonDown (object sender, MouseButtonEventArgs e)
{
Thread t = new Thread (SetTextValue );
T. Start ("Hello World ");
}
Void SetTextValue (object text)
{
This. Dispatcher. BeginInvoke () => {this.txt. Text = text. ToString ();});
}
}
}