问题描述:
“Cross-thread operation not valid :Control ‘listBox1’ accessed from a thread other than the thread it was created on” ,即“线程间操作无效,从不是创建控件“listbox1”的线程访问它。”
原因分析:
UI控件 不是线程安全的,UI控件的设计是仅由创建它们的UI线程进行操作。
解决思路:
解决方法有好几种,这里只介绍 自认为最好的方法:子线程类中建立委托事件,子线程调用事件,具体事件的实现在UI线程中实现。
解决方法示例:
1. 第一步 新建一个消息类
在本类中只包含一个变量:String Message。
实际项目中可以扩展很多变量,比如线程是否即将结束标识、线程已经处理任务的数目等等。
//消息类,
public class MessageEventArgs : EventArgs
{
public String Message; //消息。
public MessageEventArgs(string message)
{
this.Message = message;
}
}
2. 第二步 新建一个子线程类:
class SubThread
{
/// <summary>
/// 消息委托(题外话,delegate(委托)太形象了,把消息内容委托给 主程序的方法去绑定显示。
/// </summary>
/// <param name="sender">委托的发送者,在这里就是SubThread的对象</param>
/// <param name="e">委托者发送的信息</param>
public delegate void MessageEventHandler(object sender, MessageEventArgs e);
//定义事件
public event MessageEventHandler MessageSend;
/*
* 说明:定义事件处理函数,当然这里也可以不用直接在引发事件时调用this.MessageSend(sender, e);
* 这里的参数要和事件代理的参数一样
* */
public void OnMessageSend(object sender, MessageEventArgs e)
{
if (MessageSend != null)
this.MessageSend(sender, e);
}
//定义一个线程
public System.Threading.Thread sendthread;
/// <summary>
/// 线程的方法
/// </summary>
private void UploadTest()
{
try
{
for (int i = 0; i < 5; i++)
{
System.Threading.Thread.Sleep(1000); //这里让线程睡眠,模拟长时间执行的任务
this.OnMessageSend(this, new MessageEventArgs(DateTime.Now.ToString()+" "+(i+1)+"号文件上传成功!"));
}
}
catch
{
}
}
//线程启动
public void StartUpload()
{
sendthread = new System.Threading.Thread(new System.Threading.ThreadStart(UploadTest));
sendthread.Start();
}
}
3. 第三步 新建一个Form
form中新建一个按钮,一个listBox,如下:
代码:
在主界面类中 的 private void subthread_MessageSend(object sender, MessageEventArgs e)种调用了一个
**Invoke()**的方法,此方法就是可以将 子线程传递过来的message 通过UI线程更新到 listbox1.
public partial class Form1 : Form
{
SubThread subThread;
public Form1()
{
InitializeComponent();
subThread = new SubThread();
subThread.MessageSend += new SubThread.MessageEventHandler(subthread_MessageSend);
}
//绑定
public void ListBoxAdd(Object sender,MessageEventArgs msg)
{
listBox1.Items.Add(msg.Message);
}
public delegate void MessageHandler(Object sender, MessageEventArgs e); //自定义一个委托类型.如果使用系统自带的Action,就无需定义此委托
private void subthread_MessageSend(object sender, MessageEventArgs e)
{
//方式一: 使用自定义的委托,将ListAdd赋值给自定位委托;
//实例化委托
MessageHandler handler = new MessageHandler(ListBoxAdd); //不使用自定义的委托也行,使用Action
//调用Invoke
this.Invoke(handler, sender, e); //让主线程调用ListAdd函数,参数为子线程传递的信息
方式二: 使用系统自带的委托类型Action
//Action<object, MessageEventArgs> action = ListBoxAdd; //使用系统自带的Action委托
//this.Invoke(action, sender, e);
}
private void button1_Click(object sender, EventArgs e)
{
subThread.StartUpload();
}
}