转载自点击打开连接html
Winform中的控件是绑定到特定的线程的(通常是主线程),这意味着从另外一个线程更新主线程的控件不能直接调用该控件的成员。多线程
控件绑定到特定的线程这个概念以下:异步
为了从另外一个线程更新主线程的Windows Form控件,可用的方法有:ui
首先用一个简单的程序来示例,这个程序的功能是:在Winfrom窗体上,经过多线程用label显示时间。给出下面的两种实现方式spa
InvokeRequired属性:返回一个bool值,指示调用者在不一样的线程上调用控件时是否必须使用Invoke()方法。若是主调线程不是建立该控件的线程,或者尚未为控件建立窗口句柄,则返回true。线程
Invoke()方法:在拥有控件的底层窗口句柄的线程上执行委托。代理
BeginInvoke()方法:异步调用Invoke()方法。code
EndInvoke()方法:获取BeginInvoke()方法启动的异步操做返回值。orm
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace MutipleThreadTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } public delegate void ChangeTimeDelegate(Control c);//包装代理类 private void PrintTime() { if (label1.InvokeRequired == true) { while (true) { label1.Invoke(new ChangeTimeDelegate((Control c)=> c.Text = DateTime.Now.ToString() ) ,
new object[] {label1}); } } else while (true) { label1.Text = DateTime.Today.ToString(); } } private void Form1_Load(object sender, EventArgs e) { Thread t = new Thread(new ThreadStart(PrintTime));
t.IsBackground = true; t.Start(); } } }还能够使用backegroundworker控件
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace MutipleThreadTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } public delegate void ChangeTimeDelegate(Control c); private void Form1_Load(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { if (label1.InvokeRequired == true) { while (true) { label1.Invoke(new ChangeTimeDelegate((Control c) => c.Text = DateTime.Now.ToString()) , new object[] { label1 }); } } else while (true) { label1.Text = DateTime.Today.ToString(); } } } }
DebugLZQ以为第一种方法要更直观一点,或是更容易理解一点。下面再用第一种方法来作一个Demo:输入一个数,多线程计算其和值更新界面上的Label,并用进度条显示计算的进度。实际上就是,更新另外一个线程的两个UI控件。htm
原博客使用的是new 一个线程实现的,这里我是用backgroundworker组件实现
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace MutipleThreadTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } public delegate void ChangeProgressDelegate(ProgressBar c,int s); public delegate void ChangeLabelDelegate(Control c, string s); private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { long num = Convert.ToInt64(e.Argument); long sum = 0; for(long i = 1; i <= num; i++) { sum += i; if(i % 100 == 0) { progressBar1.Invoke(new ChangeProgressDelegate((ProgressBar c,int s) => c.Value = s), new object[] { progressBar1, Convert.ToInt32((i * 100) / num) }); } } label1.Invoke(new ChangeLabelDelegate((Control c, string s) => { c.Text = s; }), new object[] { label1, sum.ToString() }); } private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(textBox1.Text); } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { progressBar1.Value = 0; textBox1.Text = ""; } } }总以为这样更新一个组件太麻烦了,又是代理进程的。不知道还有没有更好的办法。继续研究一下