在.NET中如需在非UI线程中改变UI控件属性时,CLR会抛出异常,提示无法在非UI线程中更新界面上的控件(Cross-thread operation not valid)。一般情况下有两种解决办法。第一种就是设置Control的静态属性CheckForIllegalCrossThreadCalls为 False,如下:
03 | InitializeComponent(); |
04 | Control.CheckForIllegalCrossThreadCalls = false ; |
07 | private void button1_Click( object sender, EventArgs e) |
09 | Thread thread = new Thread(() => |
11 | for ( int i = 0; i < 100000; i++) |
13 | label1.Text = i.ToString(); |
另一种办法,就是使用委托,根据控件的InvokeRequired属性判断当前控件的更新操作是否是在另一个线程中。如果是,则使用委托进行方法 调用并更新控件。但是这种方法有个缺点,就是需要针对每个控件的属性设置方式创建一些单独的委托和方法,这些委托和方法仅仅是在解决跨线程操作的时候使 用。比如,你在另一个线程中需要修改某个label的text时,你就需要创建一个SetLabelText方法,假设你还需要更新TextBox的 text,那么你还需要另外创建一个SetTextBoxText方法。
通过下面的委托和方法的定义,我们实现了“一次定义,多次使用”。请看:
01 | private delegate void ParameterizedControlUpdate( params object [] args); |
03 | private delegate void ControlUpdateDelegate(Component c, |
04 | ParameterizedControlUpdate callback, |
05 | params object [] args); |
07 | private void DelegatedControlUpdate(Component c, |
08 | ParameterizedControlUpdate callback, |
11 | Control target = (c is Control) ? (c as Control) : this ; |
12 | if (target.InvokeRequired) |
14 | ControlUpdateDelegate d = DelegatedControlUpdate; |
15 | target.Invoke(d, new object [] { c, callback, args }); |
于是,上面的例子可以改为:
01 | private void button1_Click( object sender, EventArgs e) |
03 | Thread thread = new Thread(() => |
05 | for ( int i = 0; i < 100000; i++) |
07 | DelegatedControlUpdate(label1, args => |
09 | label1.Text = ( string )args[0]; |