我有一个更新GUI元素的类
public class UpdateLabelClass
{
static MainGUI theForm = (MainGUI)Application.OpenForms[0];
Label lblCurProgress = theForm.curProgress;
public ProgressBarUpdate()
{
}
public void UpdateLabel(String newLabel)
{
lblCurProgress.Text = newLabel;
}
}
在其他类中,我创建了一个类的实例并调用UpdateLabel(someString);
现在的问题是,它跳过更新标签的操作,所以我想“也许它甚至没有达到代码”,所以我在它之后放了一个MessageBox.Show(),它更新了标签
跳过标签更新的可能原因是什么,但是当我立即放置消息后执行它?该计划是否会加速?
答案 0 :(得分:2)
您很可能在主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。
有关这些方法的一些示例,请参阅here。
最后,这是一个类的一个非常简单的例子,它从一个单独的线程中引发自定义事件:
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;
}
}
答案 1 :(得分:0)
public void UpdateLabel(String newLabel)
{
lblCurProgress.Text = newLabel;
lblCurProgress.Refresh();
}