C#表单控件不会使用multithreading更新

我一直在玩multithreading并阅读这里的一些问题,但我没有找到直接解决我的问题的答案。

我有一个在单个线程上运行的应用程序,除了单独窗口中的进度条。 根据我的研究,我需要为该表单创建一个新线程,它将重新绘制表单的控件,因为它的属性会发生变化。 我已将问题简化为以下简单示例:

这是’主要’计划:

class Program { static MyForm form; static void Main(string[] args) { form = new MyForm(); form.Show(); doWork(); form.Close(); } //arbitrary example of processing that takes some period of time static void doWork() { while (form.Value < 100000) { form.ChangeVal(); Thread.Sleep(1); } return; } } 

……这是表格。 我不包括来自VS的自动生成的东西。

 public partial class MyForm : Form { private int val; public int Value { get { return val; } set { val = value; } } public Thread GUIupdater; public MyForm() { InitializeComponent(); this.Refresh(); } private void MyForm_Load(object sender, EventArgs e) { GUIupdater = new Thread(new ThreadStart(GUIupdaterThread)); GUIupdater.Start(); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(killThreadOnClose); } public void ChangeVal() { val++; } private void changeLabel(string s) { label.Text = s; label.Refresh(); } private delegate void labelChanger(string s); private void GUIupdaterThread() { while (true) { Invoke(new labelChanger(changeLabel), new object[]{val.ToString()} ); Thread.Sleep(100); //100 ms } } private void killThreadOnClose(object sender, FormClosingEventArgs e) { GUIupdater.Abort(); } } 

因此,我的目的是让计算不断运行,窗口的图形合理地快速更新。 但是,当我运行程序时,调用函数只调用一次,标签永远不会实际更新!

任何和所有反馈都表示赞赏。 如果要在IDE中查看代码,可以从此处下载我的项目

编辑:

  • 当我添加Console.WriteLine语句时,我发现GU​​IupdaterThread(用于更新GUI的东西)循环始终在Invoke语句中“中断”,从不到达’Thread.Sleep’。 我将其更改为’BeginInvoke’,这使循环正常运行,但这并没有改变GUI不更新的事实。

澄清:

关于我的“实际”项目:

  • ‘Program’中的主要线程模拟我的软件,这是一个实现接口的插件。 我决定改变该线程中的val / value,而不是在窗口创建的线程中,这是故意的。
  • 我不得不使用.NET 4.0。 任何更新的function都无法帮助我

我建议覆盖表单的OnPaint方法。 然后在ChangeVal ,在更新了需要更新的任何变量/数据之后,调用this.Invalidate ,它应该触发表单重新绘制自己。

或者,如果您只是更新单个标签,请在ChangeVal方法中调用label.Refresh。 表单应该正确更新。 这是一个对我有用的例子:

此表单上有一个标签。

 public partial class ProgressForm : Form { private int currentValue = 0; public ProgressForm() { InitializeComponent(); } public void ChangeValue(int newValue) { currentValue = newValue; lblValue.Text = string.Format("Current value: {0}", currentValue); lblValue.Refresh(); //Call Refresh to make the label update itself } } static class Program { private static ProgressForm progressForm = null; [STAThread] static void Main() { //Application.EnableVisualStyles(); //Application.SetCompatibleTextRenderingDefault(false); //Application.Run(new Form1()); progressForm = new ProgressForm(); progressForm.Show(); doWork(); progressForm.Close(); } //arbitrary example of processing that takes some period of time static void doWork() { int i = 0; while (i < 100000) { progressForm.ChangeValue(i); Thread.Sleep(1); i++; } return; } } 

因为在你的应用程序中你有GUI线程(主线程) – 所有UI控件只能从这个线程访问。

有几种方法可以从其他线程更新控件。 我建议您使用基于Progress 类的现代和原生方法之一(它是.Net平台的原生)。

当您尝试访问除主线程(创建它)之外的UI控件时,您可以使用以下代码。

 while ( true ) { Invoke ( ( Action ) (() => { label.Text = val.ToString(); label.Refresh() Application.DoEvents(); })); Thread.Sleep( 100 ); } 

我建议你使用“backgroundworker”。

首先添加CheckForIllegalCrossThreadCalls = false; 到初始化部分否则会发生InvalidOperationException

 private void btnDoIt_Click(object sender, EventArgs e) { backgroundWorker.RunWorkerAsync(); } private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { Foo(); } int total = 0; private void Foo() { for (int i = 0; i <= 100000; i++) { total += i; this.Text = i.ToString(); } } private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // Run next process }