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();};
//或者
//ActionactionDelegate=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元素的资料请关注毛票票其它相关文章!