很可能您在主 UI 线程中不正确地运行了长时间的操作,这会阻止标签更新。你可以通过调用 DoEvents() 来“解决”这个问题:
public void UpdateLabel(String newLabel)
{
lblCurProgress.Text = newLabel;
Application.DoEvents();
}
但这只是一个糟糕设计的创可贴。 您应该将该代码正确移动到后台线程并使用委托/Invoke() 来更新标签。
编辑:(回答后续问题)
默认情况下,您的应用程序在单个线程中运行。这包括您添加到控制事件的代码,以及您看不到的在幕后运行以使您的应用程序以您期望的方式响应的代码。诸如用户交互(鼠标单击、键盘按下等)和绘画消息(当控件更改时,您的窗口被遮挡)之类的东西被放入队列中。只有在您的代码停止运行后,队列中的那些待处理消息才会得到处理。如果你有一段很长的代码在运行,比如一个很长的循环,那么这些消息就会在队列中等待处理。因此,直到循环完成后才会更新标签。DoEvents() 所做的是告诉应用程序立即处理队列中的那些待处理消息,然后返回当前正在执行的代码。这允许标签像您期望的那样实时更新。
当你遇到被 DoEvents() “修复”的情况时,它仅仅意味着你试图在主 UI 线程中运行太多代码。主 UI 线程应该专注于响应用户交互并保持显示更新。控制事件处理程序中的代码应该简短而优美,以便主 UI 线程可以重新开始其主要工作。
正确的解决方法是将冗长的代码移至不同的线程,从而允许主 UI 线程响应并保持自身更新。对于许多场景,最简单的方法是放置一个BackgroundWorker()控制您的表单并连接 DoWork()、ProgressChanged() 和 RunWorkerCompleted() 事件。*但是,您必须将 WorkerReportsProgress() 属性设置为 true,才能处理 ProgressChanged() 事件。后两个事件已经为您编组到主 UI 线程,因此您无需担心跨线程异常。从 DoWork() 处理程序中,您调用 ReportProgress() 并传递一个进度百分比值和一个可选的其他对象(它可以是任何东西)。这些值可以在 ProgressChanged() 事件中检索并用于更新 GUI。当 DoWork() 处理程序中的所有工作都完成时,会触发 RunWorkerCompleted() 事件。
在你的情况下,你有一个单独的班级正在做这项工作。您可以通过在该类中手动创建自己的线程来完成工作来反映 BackgroundWorker 所做的工作。当您想更新进度时,请让您的班级提出Custom Event
主表单订阅的内容。但是,当接收到该事件时,它将在单独线程的上下文中运行。然后,有必要跨线程边界“编组”调用,以便在更新控件之前代码在主 UI 线程中运行。这是通过使用委托(指向方法的“指针”)和 Invoke() 方法来完成的。*还有其他方法可以完成此任务,例如 SynchronizationContext。
有关这些方法的一些示例,请参见此处。
最后,这是一个从单独线程引发自定义事件的类的超级简单示例:
public partial class Form1 : Form
{
private Clock Clk;
public Form1()
{
InitializeComponent();
Clk = new Clock();
Clk.CurrentTime += new Clock.TimeHack(Clk_CurrentTime);
}
private void Clk_CurrentTime(string hack)
{
if (label1.InvokeRequired)
{
Clock.TimeHack t = new Clock.TimeHack(Clk_CurrentTime);
label1.Invoke(t, new object[] { hack });
}
else
{
label1.Text = hack;
}
}
}
public class Clock
{
public delegate void TimeHack(string hack);
public event TimeHack CurrentTime;
private Thread t;
private bool stopThread = false;
public Clock()
{
t = new Thread(new ThreadStart(ThreadLoop));
t.IsBackground = true; // allow it to be shutdown automatically when the application exits
t.Start();
}
private void ThreadLoop()
{
while (!stopThread)
{
if (CurrentTime != null)
{
CurrentTime(DateTime.Now.ToString());
}
System.Threading.Thread.Sleep(1000);
}
}
public void Stop()
{
stopThread = true;
}
}