Winform中如何跨线程访问UI元素
在C#的应用程序开发中,我们经常要把UI线程和工作线程分开,防止界面停止响应,同时我们又需要在工作线程中更新UI界面上的控件。但直接访问会出现“线程间操作无效”的情况,因为.NET禁止了跨线程调用控件,否则谁都可以操作控件,最后可能造成错误。下面介绍几种跨线程访问的方法:
1、禁止对跨线程访问做检查 (不推荐使用这种方法)
这种方法不检查跨线程访问,允许各个线程操作UI元素,容易出现错误。
publicForm2() { InitializeComponent(); //禁止对跨线程访问做检查(不推荐使用这种方法) Control.CheckForIllegalCrossThreadCalls=false; }
2、使用委托方法 将其委托给UI控件更新
//使用委托方法将其委托给UI控件更新 privatevoidbutton1_Click(objectsender,EventArgse) { Threadthread1=newThread(newParameterizedThreadStart(UpdateLabel2)); thread1.Start("更新Label"); } privatevoidUpdateLabel2(objectstr) { if(label2.InvokeRequired) { //当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它 ActionactionDelegate=(x)=>{this.label2.Text=x.ToString();}; //或者 //Action actionDelegate=delegate(stringtxt){this.label2.Text=txt;}; this.label2.Invoke(actionDelegate,str); } else { this.label2.Text=str.ToString(); } }
3、使用delegate和BeginInvoke来从其他线程中控制控件
只要把上面的this.label2.Invoke(actionDelegate,str);中的Invoke改为BeginInvoke方法就可以了。
Invoke方法和BeginInvoke方法的区别是:Invoke方法是同步的,它会等待工作线程完成,BeginInvoke方法是异步的,它会另起一个线程去完成工作线。
4、使用同步上下文:SynchronizationContext方法
该方法是取得主线程的上下文信息,然后在子线程将访问UI控件方法推送到UI上下文的消息队列里,使用POST或者Send;
privateSynchronizationContextsynchronizationContext; privatevoidbutton2_Click(objectsender,EventArgse) { synchronizationContext=SynchronizationContext.Current; newThread(()=>{UpdateText("跨线程访问");}).Start(); } voidUpdateText(stringmsg) { synchronizationContext.Post(_=>this.label2.Text=msg,null); }
5、使用BackgroundWorker组件(推荐使用这个方法)
BackgroundWorker是.NET里面用来执行多线程任务的控件,它允许编程者在一个单独的线程上执行一些操作。耗时的操作(如下载和数据库事务)。
publicpartialclassFileManagerForm:Form { FileInfofile; BackgroundWorkerbw; ServerFileserver; publicFileManagerForm(stringfilePath) { InitializeComponent(); file=newFileInfo(filePath); longsize=file.Length/1024/1024; lblOrgSize.Text=(int)size+"MB"; bw=newBackgroundWorker(); server=newServerFile(file.Name); } privatevoidFileManagerForm_Load(objectsender,EventArgse) { proUpFile.Minimum=0; proUpFile.Maximum=100; bw.WorkerReportsProgress=true; bw.WorkerSupportsCancellation=true; bw.DoWork+=Bw_DoWork; bw.ProgressChanged+=Bw_ProgressChanged; bw.RunWorkerCompleted+=Bw_RunWorkerCompleted; bw.RunWorkerAsync(); } privatevoidBw_DoWork(objectsender,DoWorkEventArgse) { using(FileStreamfileRead=file.OpenRead()) { longsetp=file.Length/100; while(file.Length>fileRead.Position) { if(bw.CancellationPending) { break; } byte[]bytes=newbyte[1024]; intcount=fileRead.Read(bytes,0,bytes.Length); longwriteLength=server.UpFile(bytes,count); if(writeLength>proUpFile.Value*setp) { intsize=(int)(writeLength/1024/1024); bw.ReportProgress(proUpFile.Value+1,size); } } server.Close(); } } privatevoidBw_ProgressChanged(objectsender,ProgressChangedEventArgse) { proUpFile.Value=e.ProgressPercentage>proUpFile.Maximum?proUpFile.Maximum:e.ProgressPercentage; lblUpLoadSize.Text=e.UserState.ToString()+"MB"; } privatevoidBw_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse) { if(this.proUpFile.Value==this.proUpFile.Maximum) { MessageBox.Show("文件发送成功!"); } else { MessageBox.Show("文件发送失败!"); } this.Close(); } privatevoidbtnCancel_Click(objectsender,EventArgse) { bw.CancelAsync(); } }
以上就是Winform中如何跨线程访问UI元素的详细内容,更多关于Winform访问UI元素的资料请关注毛票票其它相关文章!