随着程序复杂度的提高,程序不可避免会出现多个线程,此时就很可能存在跨线程操作控件的问题。

跨线程操作UI控件主要有三类方式:

1、禁止系统的线程间操作检查。(此法不建议使用)

2、使用Invoke(同步)或者BeginInvoke(异步)。(使用委托实现,并用lambda表达式简化代码)

3、使用BackgroundWorker组件。(此法暂不介绍,详情可见文末的参考资料)

先看一个跨线程操作失败的例子:

新建一个Winform窗口程序项目,拖一个button1和label1控件到Form1窗体上。启动程序以后试图通过点击button1改变label1的值,完整代码如下:

 using System;
using System.Threading;
using System.Windows.Forms; namespace Windows跨线程调用控件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
thread1.Start("label已更新");
} private void UpdateLabel(object str)
{
label1.Text = str.ToString();
}
}
}

点击button1以后运行报错:

解决方案:

方法一:禁止系统的线程间操作检查。

代码就一句话:Control.CheckForIllegalCrossThreadCalls = false;通常写在Form1类的构造方法Form1()中。如下所示:

 public Form1()
{
InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false;
}

但是,这种方法是很不可靠的,有时候还是会报错。

方法二:使用Invoke(同步)或者BeginInvoke(异步)。最精简的代码如下:

 using System;
using System.Threading;
using System.Windows.Forms; namespace Windows跨线程调用控件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(UpdateLabel);//可以省略线程的委托类型ParameterizedThreadStart
thread1.Start("label已更新");
} private void UpdateLabel(object str)
{
if (label1.InvokeRequired)//不同线程为true,所以这里是true
{
BeginInvoke(new Action<string> (x => {label1.Text = x.ToString();}),str);
}
}
}
}

说明:Action是.NET预定义好的委托,可以简化委托的语法,如果不清楚它的用法,可以搜索“Action和Func的用法”。

将上面的两种方法总结在同一段程序里面如下所示:(注意看代码中的注释)

 using System;
using System.Threading;
using System.Windows.Forms; namespace Windows跨线程调用控件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent(); /************* 方法一 ************/
//Control.CheckForIllegalCrossThreadCalls = false; } private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
thread1.Start("label已更新");
} //如果控件的 Handle(句柄) 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
private void UpdateLabel(object str)
{
if (label1.InvokeRequired)//当是不同的线程在访问时为true,所以这里是true
{
/************* 方法二 ************/
//Action<string> actionDelegate = (x) => { this.label1.Text = x.ToString(); }; ////如果已经创建控件的句柄,则除了 InvokeRequired 属性以外,控件上还有四个可以从【任何线程】上安全调用的方法,
////它们是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。
////在后台线程上创建控件的句柄之前调用 CreateGraphics 可能会导致非法的跨线程调用。
////对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。
//this.label1.BeginInvoke(actionDelegate, str); /************* 方法二(变式) ************/
//也可以直接用下面一句话来完成
//Control.BeginInvoke 方法有两个重载:BeginInvoke(Delegate) ,BeginInvoke(Delegate, Object[]),下式用的是第二个重载
this.BeginInvoke(new Action<string>((x) => { label1.Text = x.ToString(); }), str);

//如果启动的多线程不需要带可变的参数,那更简单:
//label1.BeginInvoke(new Action(() => { label1.Text = "aaa"; }));
}
}
}
}

参考资料:

http://www.cnblogs.com/TankXiao/p/3348292.html

http://www.cnblogs.com/txw1958/archive/2012/08/21/csharp-crossthread-widget.html

C#跨线程操作控件的最简单实现探究的更多相关文章

  1. WinForm中新开一个线程操作 窗体上的控件(跨线程操作控件)

    最近在做一个winform的小软件(抢票的...).登录窗体要从远程web页面获取一些数据,为了不阻塞登录窗体的显示,开了一个线程去加载数据远程的数据,会报一个错误"线程间操作无效: 从不是 ...

  2. C# 跨线程操作控件(简洁)

                                              C# 跨线程操作控件 .net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生.解决此问题的方法有两个: 第一 ...

  3. winform 跨线程操作控件

    当进行winform的开发时,经常遇到用时比较久的操作,在传统的单线程程序中,用户必须等待这个耗时操作完成以后才能进行下一步的操作,这个时候,多线程编程就派上用场了,将这个耗时的操作放到一个新的子线程 ...

  4. C#跨线程操作控件

    1.首先通过按键创建子线程: 创建子线程,子线程调用changeText方法. private void btnOK_Click(object sender, EventArgs e) { Threa ...

  5. 在.Net中进行跨线程的控件操作(下篇:BackgroundWorker)

    在.Net中,如果我们在非UI线程上访问窗体上的控件的时候,会产生一个跨线程调用的异常,那么如何处理这种情况呢?在上一章中,我介绍了使用Control.Invoke方法,如果你不习惯使用委托,那么.N ...

  6. C# 跨线程调用控件

    在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应.  同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线程间操作无效 第一种办法:禁 ...

  7. winform跨线程访问控件

    首先说下,.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性.所以除了控件所在的线程外的线程调用会抛异常 (Cross-thread operation not va ...

  8. C# 关于跨线程访问控件问题

    跨线程访问控件问题的原因是:控件都是在主线程中创建的,而系统默认控件的修改权归其创建线程所有.在子线程中如果需要直接修改控件的内容,需要使用委托机制将控件的修改操作交给主线程处理.因此,当没有使用委托 ...

  9. 【转载】C# 跨线程调用控件

    转自:http://www.cnblogs.com/TankXiao/p/3348292.html 感谢原作者,转载以备后用 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停 ...

随机推荐

  1. 【备忘录】CentOS服务器mysql忘记root密码恢复

    mysql的root忘记,现无法操作数据库 停止mysql服务service  mysql stop 然后使用如下的参数启动mysql, --skip-grant-tables会跳过mysql的授权 ...

  2. vc中edit控件使用总结

    通过类向导可以生成两种类成员变量,一种是cstring类型,一种是cedit类型.在程序中使用时如果只是简单的获取 edit控件内容,或设置简单的内容建议使用cstring类型成员变量.示例:CStr ...

  3. python 多线程要点

    要点整理 多线程 #coding=utf-8 import threading from time import ctime,sleep def music(func): for i in range ...

  4. DomHelper

    public class DomHelper { public static ArrayList<Person> queryXML(Context context) { ArrayList ...

  5. Eclipse中设置JDK、${user}变量

    为eclipse设置jdk方法: 两个方法: 1.设置PATH路径-eclipse自动会查找! 2.在快捷方式中加上参数:-VM java虚拟机路径 Eclipse中设置${user}变量 在Ecli ...

  6. PTA 1005 Spell It Right (20)(20 分)水题

    1005 Spell It Right (20)(20 分) Given a non-negative integer N, your task is to compute the sum of al ...

  7. Socket通讯介绍

    综上原理,代码的实施的步骤如下: Socket Families(地址簇)的三种类型,这个时候是网络层 socket.AF_UNIX unix本机进程间通信 本机之间的不同进程通讯默认是不可以通讯的, ...

  8. 我编辑的JAVA日历程序

    class calendar { public static void main(String[]args) { int yearIn ; yearIn = Integer.parseInt(args ...

  9. 用户从手机的浏览器访问www.baidu.com,看到的可能跟桌面PC电脑,是不太一样的网页效果,会更适合移动设备使用。请简要分析一下,实现这种网页区分显示的原因及技术原理。

    手机的网速问题.屏幕大小.内存.CPU等.通过不同设备的特征,实现不同的网页展现或输出效果.根据useragent.屏幕大小信息.IP.网速.css media Query等原理,实现前端或后端的特征 ...

  10. Rplot

    #!/usr/bin/Rscriptlibrary(ggplot2) cf = read.table(file = 'result_sort.txt', header = TRUE, sep='\t' ...