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

跨线程操作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. RK3288 制作开机动画

    Android 的开机动画是由 Linux 本地守护程序 bootanimation 专门控制实现的,其代码在 frameworks/base/cmds/bootanimation/ 目录下. 修改开 ...

  2. Face detection in color images, 彩色图像中的人脸检测

    人脸检测在视频监督,人机交互,人脸识别和人脸图像数据库管理等应用领域处于很重要的地位. 论文<Face detection in color images>中给出一种在YCbCr空间检测人 ...

  3. C# 中的委托和事件(2)

    委托.事件与Observer设计模式范例说明 上面的例子已不足以再进行下面的讲解了,我们来看一个新的范例,因为之前已经介绍了很多的内容,所以本节的进度会稍微快一些: 假设我们有个高档的热水器,我们给它 ...

  4. java代码----数据类型的转换-----int --->String

    总结:int ----->String package com.a.b; //测试..char--->int // int--->String public class Yue2 { ...

  5. Android笔记——对系统通话记录的删除操作

    手机通话记录是保存在数据库中的,位置:  /data/data/com.android.providers.contacts/databases/calllog.db ,表名:calls 这张表中有个 ...

  6. Java 中 wait, notify 和 notifyAll的正确使用 – 以生产者消费者模型为例

    如何使用Wait 尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单.如果你在面试中让应聘者来手写代码,用wait和notify解决生产者消费者问题 ...

  7. PHP redis client封装

    config1.inc.php if (!isset($_REQUEST['pageName']) || $_REQUEST['pageName'] != 'txl-app-test') { $CON ...

  8. log4j:WARN No appenders could be found for logger 解决办法

    转自:https://blog.csdn.net/chw0629/article/details/80567936 使用log4j时不起作用,每次执行完出现以下提示: log4j:WARN No ap ...

  9. TThread 线程的例子

    TThread 线程的例子 D:\Documents\Embarcadero\Studio\14.0\Samples\CPP\RTL\Threads TThread类   该线程类可以完成大多数的线程 ...

  10. 黑暗之光 Day2

    1. 鼠标点击UI检测 UICamera.isOverUI 2. 鼠标指针管理 public class CussorManager : MonoBehaviour { public static C ...