没有及时更新GUI

时间:2013-05-17 12:57:38

标签: c# user-interface

我有一个更新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(),它更新了标签

跳过标签更新的可能原因是什么,但是当我立即放置消息后执行它?该计划是否会加速?

2 个答案:

答案 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();
}