前言:
在开发过程中,我们的程式会报 “InvalidOperationException” 的异常。该异常产生的一个原因是由于进行了跨线程非法调用控件,即在非创建控件的线程上访问该控件。什么意思呢,通俗的讲:线程ThreadA创建了控件ControlA,线程ThreadB去访问这个控件(ControlA)就会产生上述异常。那么,要在ThreadB中访问控件ControlA,该如何实现呢?也就是如何实现跨线程访问UI控件呢,下面将探讨这个问题。
概述:
在Windows Form 中,每个控件都有一个BOOL 型 属性 InvokeRequired,该属性表示当前访问的线程是否为创建它的线程,值为FALSE表示不是。在WPF中,控件没有InvokeRequired属性,我们通过Dispatcher.Thread属性和当前线程(System.Threading.Thread.CurrentThread)进行比较判断当前访问的线程是否为创建它的线程。无论是Windows Form 还是WPF,若当前线程非控件创建线程则通过控件的Invoke方法或BeginInvoke、EndInvoke方法进行访问。下面通过WPF的例子进行说明。
Demo1--简单的跨线程设置属性值
前面板如图,由一个TextBlock控件和一个Button控件构成。实现功能,点击Click后,TextBlock将显示访问它的线程ID及时间信息。

当点击Click将开启一个线程,该线程非创建TextBlock的线程(主线程),然后该线程向TextBlock添加文字,结果如上图,每次点击Click都会产生不同的线程ID,关于线程相关知识详见C#进程、线程、任务一文。下面是主要代码:
//定义一个委托,为了将要执行方法的入口传递给创建控件线程
private delegate void textblockShowWriteInvoke(string value);
//访问textblockShow的方法
private void textblockShowWrite(string value)
{
//判断当前线程是否为控件创建线程
if (textblockShow.Dispatcher.Thread != System.Threading.Thread.CurrentThread)
{
//在该处可以理解为将要处理的方法传到创建textblockShow的线程进行执行
textblockShow.Dispatcher.Invoke(new textblockShowWriteInvoke(textblockShowWrite),
value);
}
else
{
//在创建textblockShow的线程执行时才不会发生异常
this.textblockShow.Text = value;
}
}
上面定义委托的目的就是为了将要执行的方法的入口传递给创建控件的线程。
以上,就简单的实现了跨线程的控件访问,在此处,我们仅仅是为控件的属性进行赋值。接下来我们将继续探讨如何读取控件某个属性的值以及访问控件中的某个方法。
本文介绍了在WPF中遇到的跨线程非法调用控件导致的`InvalidOperationException`异常,并提供了如何判断及解决此问题的方法。通过Dispatcher对象和线程比较,以及使用Invoke或BeginInvoke方法,演示了如何在非创建线程上安全地修改控件属性。
3327

被折叠的 条评论
为什么被折叠?



