在多线程操作WinForm窗体上的控件时,出现“线程间操作无效:从不是创建控件XXXX的线程访问它”,那是因为默认情况下,在Windows应用程序中,.NET Framework不允许在一个线程中直接操作另一个线程中的控件(因为访问Windows窗体控件本质上不是线程安全的)。微软为了线程安全,窗体上的控件只能通过创建控件的线程来操作控件的数据,也就是只能是UI线程来操作窗体上的控件!可看看Control的Invoke和BeginInvoke

  要解决这个问题可以用以下方法:

  1、关闭线程安全检查(不过本人不推荐,这种方式可能会发生一些不可预计的后果)

Control对象.CheckForIllegalCrossThreadCalls = false;

  2、使用控件的Invoke方法(或BeginInvoke方法、BackgroundWorker)

(1)、Invoke方法

if (this.InvokeRequired)
{
this.Invoke(new Action(() => button1.Enabled = false));
//button1.Invoke(new MethodInvoker(delegate() { button1.Enabled = false; }));
//textBox1.SafeCall(() =>{ textBox1.Text = (i++).ToString();});

button1.Invoke(new MethodInvoker(() => button1.Enabled = false ));
button1.Invoke(new Action(() => button1.Enabled = false)); // 跨线程访问UI控件
}
else
{
button1.Enabled = false
}

(2)、BeginInvoke方法

        delegate void AppendValueDelegate(string strValue);

        public void AppendValue(string strValue)
{
textBox1.BeginInvoke(new AppendValueDelegate(AddValueToTextBox), new object[] { strValue });
} private void AddValueToTextBox(string strValue)
{
textBox1.Text += strValue;
}

可精简成:

        public void AppendValue(string strValue)
{
// 无返回值无参数用MethodInvoker委托,无返回值可有参数用Action委托,有返回值可有参数用Func委托
textBox1.BeginInvoke(new Action<string>(msg => textBox1.Text += msg),
new object[] { strValue });
}

  3、使用委托

public delegate void AddLog(string info);
/// <summary>
/// 添加日志
/// </summary>
AddLog OnAddLog; /// <summary>
/// 加载事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FrmDataBackup_Load(object sender, EventArgs e)
{
OnAddLog = new AddLog(PrintMsg);
} /// <summary>
/// 打印信息到即时显示控件
/// </summary>
/// <param name="info"></param>
public void PrintMsg(string info)
{
// InvokeRequired 属性判断是否跨线程操作
if (this.InvokeRequired)
{
this.Invoke(OnAddLog, info);
return;
}
listBoxInfo.Items.Insert(, "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "] " + info);
if (listBoxInfo.Items.Count > )
{
listBoxInfo.Items.RemoveAt();
}
}

4、使用SynchronizationContext基类,该类记录着线程的同步上下文对象,我们可以通过在GUI线程中调用SynchronizationContext.Current属性来获得GUI线程的同步上下文,然后当线程池线程需要更新窗体时,可以调用保存的SynchronizationContext派生对象的Post方法(Post方法会将回调函数送到GUI线程的队列中,每个线程都有各自的操作队列的,线程的执行都是从这个队列中拿方法去执行),向Post方法传递要由GUI线程调用的方法(该方法的定义要匹配SendOrPostCallback委托的签名),还需要想Post方法传递一个要传给回调方法的参数。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting.Messaging;
using System.Threading; namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} /// <summary>
/// 定义用来实现异步编程的委托
/// </summary>
/// <returns></returns>
private delegate string GetTextInfoDelegate(); /// <summary>
/// 线程同步上下文对象
/// </summary>
SynchronizationContext syncContext;
/// <summary>
/// 调用委托
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
GetTextInfoDelegate textInfo = new GetTextInfoDelegate(GetInfo);
textInfo.BeginInvoke(new AsyncCallback(GetTextInfoResult), null);
// 捕捉调用线程的同步上下文派生对象
syncContext = SynchronizationContext.Current;
} /// <summary>
/// 异步操作获取信息
/// </summary>
/// <returns></returns>
private string GetInfo()
{
return "使用SynchronizationContext基类";
} /// <summary>
/// 异步操作完成时执行的方法
/// </summary>
/// <param name="result"></param>
private void GetTextInfoResult(IAsyncResult result)
{
GetTextInfoDelegate caller = (GetTextInfoDelegate)((AsyncResult)result).AsyncDelegate;
// 调用EndInvoke去等待异步调用完成并且获得返回值
// 如果异步调用尚未完成,则 EndInvoke 会一直阻止调用线程,直到异步调用完成
string text = caller.EndInvoke(result); // 通过获得GUI线程的同步上下文的派生对象,
// 然后调用Post方法来使更新GUI操作方法由GUI 线程去执行
// ShowText(text); // 报错:线程间操作无效: 从不是创建控件“textBox1”的线程访问它。
syncContext.Post(new SendOrPostCallback(ShowText), text);
} /// <summary>
/// 显示结果到TextBox文本框
/// 因为该方法是由GUI线程执行的,所以当然就可以访问窗体控件了
/// </summary>
/// <param name="text"></param>
private void ShowText(object text)
{
textBox1.Text = text.ToString();
}
}
}

参考资料:http://www.cnblogs.com/easyfrog/archive/2012/02/08/2343075.html 和 http://www.cnblogs.com/zhili/archive/2013/05/10/APM.html

实现Winform 跨线程安全访问UI控件的更多相关文章

  1. C# Winform 跨线程更新UI控件常用方法汇总(多线程访问UI控件)

    概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件的线程访问它”的异常.处理跨线程更新Winform UI控件常用的方法有4种:1. ...

  2. 理解SynchronizationContext,如何在Winform里面跨线程访问UI控件

    SynchronizationContext 类是一个基类,可提供不带同步的自由线程上下文. 此类实现的同步模型的目的是使公共语言运行库内部的异步/同步操作能够针对不同的异步模型采取正确的行为.此模型 ...

  3. 跨线程访问UI控件时的Lambda表达式

    工作中经常会用到跨线程访问UI控件的情况,由于.net本身机制,是不允许在非UI线程访问UI控件的,实际上跨线程访问UI控件还是 将访问UI的操作交给UI线程来处理的, 利用Control.Invok ...

  4. c#中如何跨线程调用windows窗体控件

    c#中如何跨线程调用windows窗体控件?   我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍.首 ...

  5. C#学习之在辅助线程中修改UI控件----invoke方法

    Invoke and BeginInvoke 转载地址:http://www.cnblogs.com/worldreason/archive/2008/06/09/1216127.html 在Invo ...

  6. 如何跨线程调用Windows窗体控件

    通过一个子线程来操作主线程中的控件,但是,这样作会出现一个问题(如图1所示),就是TextBox控件是在主线程中创建的,在子线程中并没有对其进行创建,也就是从不是创建控件的线程访问它.那么,如何解决跨 ...

  7. WPF / Win Form:多线程去修改或访问UI线程数据的方法( winform 跨线程访问UI控件 )

    WPF:谈谈各种多线程去修改或访问UI线程数据的方法http://www.cnblogs.com/mgen/archive/2012/03/10/2389509.html 子线程非法访问UI线程的数据 ...

  8. 实现 winform 异步跨线程访问UI控件

    在开发winform时经常会用到多线程防止界面出现假死现象,比如当你单击某个按钮时,需要执行很多代码,但是在执行过程中想实时的将当前执行的情况报告给用户,类型进度条或文本什么的. 这个时候很显然,如果 ...

  9. 实现跨线程访问UI控件的3种方法

    namespace 多线程 { public partial class Form2 : Form { public Form2() { InitializeComponent(); } privat ...

随机推荐

  1. BeanFactoryPostProcessor vs BeanPostProcessor

    BeanFactoryPostProcessors affect BeanDefinition objects because they are run right after your config ...

  2. MySQL 5.5 服务器变量详解(二)

    innodb_adaptive_flushing={ON|OFF} 设定是否允许MySQL服务器根据工作负载动态调整刷写InnoDB buffer pool中的脏页的速率.动态调整刷写速率的目的在于避 ...

  3. 转场动画UINavigationControllerDelegate

    从iOS7开始,苹果更新了自定义ViewController转场的API,这些新增的类和接口让很多人困惑,望而却步.本文就从这些API入口,让读者理清这些API错综复杂的关系. 几个protocol ...

  4. 004-linux命令-搜索命令find、locate、whereis、which、type

    一.概述 使用linux系统难免会忘记文件所在的位置,可以使用以下命令对系统中的文件进行搜索. 1.1.find 语法:find <指定目录> <指定条件> <指定动作& ...

  5. NO Route to Host 连接mysql数据库

    显然是请求被服务器的防火墙给拦截了 1,vi /etc/sysconfig/iptables 2,在倒数第三行以前添加 -A INPUT -p tcp -m state --state NEW -m  ...

  6. xxx.app已损坏,打不开.你应该将它移到废纸篓-已解决

    到这种情况可能不是你的软件包的问题,可能是macos安全设置问题. 解决办法就是: 1.系统偏好设置... -> 安全性与隐私-->修改为任何来源 2.serria里面没有“任何来源”这一 ...

  7. nodejs+mysql入门实例(删)

    //连接数据库 var mysql = require('mysql'); var connection = mysql.createConnection({ host: 'bdm253137448. ...

  8. Photobucket不能用了怎么办?推荐10个在线图片储存服务!

    近日,图片共享服务网站Photobucket更新了政策,要求用户缴纳399美元的年费,才能使用第三方网站图片储存服务. 由于Photobucket出台这一政策,亚马逊及其他电商平台上相关的产品图片已被 ...

  9. Linux修改SSH登录端口

    Linux的默认登录端口为:22,为系统安全运维都会将端口改成其它端口. 假如我们修改的端口为:3000 1.首先要配置防火墙,允许此端口通行. /sbin/iptables -A INPUT -p ...

  10. Jmeter-----邮件观察仪

    设置邮件观察仪的目的,将测试结果通过邮件的方式发送给相关的人员,可及时获取到当前测试结果情况. 说明: 本人公司使用的为腾讯企业邮箱,因此下面的介绍方式将以腾讯企业邮箱为准来进行描述 必要的设置: 邮 ...