转载自点击打开链接
Winform中的控件是绑定到特定的线程的(一般是主线程),这意味着从另一个线程更新主线程的控件不能直接调用该控件的成员。
控件绑定到特定的线程这个概念如下:
为了从另一个线程更新主线程的Windows Form控件,可用的方法有:
首先用一个简单的程序来示例,这个程序的功能是:在Winfrom窗体上,通过多线程用label显示时间。给出下面的两种实现方式
1.结合使用特定控件的如下成员
InvokeRequired属性:返回一个bool值,指示调用者在不同的线程上调用控件时是否必须使用Invoke()方法。如果主调线程不是创建该控件的线程,或者还没有为控件创建窗口句柄,则返回true。
Invoke()方法:在拥有控件的底层窗口句柄的线程上执行委托。
BeginInvoke()方法:异步调用Invoke()方法。
EndInvoke()方法:获取BeginInvoke()方法启动的异步操作返回值。
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控件。
原博客使用的是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 = "";
}
}
}
总觉得这样更新一个组件太麻烦了,又是代理进程的。不知道还有没有更好的办法。继续研究一下