ProgressBar在阻止的UI线程中更新

时间:2011-05-23 10:14:32

标签: c# .net winforms multithreading progress-bar

为什么ProgressBar会在理论上阻止UI线程?

在简单的应用程序中,我有一个ProgressBar和一个Label。我在UI线程中运行一个耗时的方法,试图更新ProgressBar和标签。由于UI线程被阻止,这应该可以正常工作。但ProgressBar正在更新!

在我对表单执行任何操作并冻结之前,ProgressBar会更新(标签不会)。

为什么?

示例代码(在表单上放置Button,ProgressBar和Label):

private void button1_Click(object sender, EventArgs e)
{
    while (true)
    {
        progressBar1.Value += 1;
        label1.Text += "1";
        Thread.Sleep(100);
    }
}

ProgressBar正在更新,Label不是。我的问题不是如何进行标签更新,以及为什么ProgressBar 更新。我知道Threads,DoEvents,async / await,这不是答案。

2 个答案:

答案 0 :(得分:4)

我认为如果不拆解一些Windows就很难完全回答这个问题,这对我来说太过分了。

但基本上,当您在WinForms ProgressBar控件上设置.Value时,它只会使用消息1026(PBM_SETPOS)调用SendMessage,它会告诉Windows进度条设置其位置。

我会得出结论,Windows进度条会同步重绘以回应PBM_SETPOS 以及以响应WM_PAINT。或者它可能正在另一个线程上运行一个计时器,以便进行花哨的眩光动画,并且能够重绘控件而无需等待绘制消息。

无论哪种方式,它只是你所看到的Windows内部 - 并且在WM_PAINT处理程序之外绘制东西并不是 这是一种不寻常的技术,即使它不是教科书的做法。

实际上,查看PBM_SETPOS(http://msdn.microsoft.com/en-us/library/bb760844(v=vs.85).aspx)的文档时,它被记录为导致重绘 - 我想这是故意做的,以帮助懒惰/没有经验的阻止进度条更新工作,没有通常的麻烦做得好。

答案 1 :(得分:1)

有不同的方法来回答您的问题。让我解释。如果您正在运行一个冗长的任务,用户必须等待继续,那么无论您是否在UI线程上运行,都无关紧要。无论哪种方式,用户都必须等待任务完成才能继续,因此无论如何他们都无法使用该应用程序。

但是,在您可能希望用户在任务运行时与应用程序的不同部分进行交互的操作中,您可以创建工作线程来执行所述任务。您需要了解主UI线程用于处理UI。绘制进度条是UI的一部分。 WinForms / .NET的设计架构是通过BackgroundWorker类创建后台线程,或者自己编写原始线程。它的设计。

然而,这一切都会随着C#5.0使用async和await关键字以及Task对象进行彻底改变。您可以在channel9上搜索TechDays 11,或访问我的Facebook(发布一些帖子)。

如果您愿意在UI线程上保持操作,可以在任务中调用Application.DoEvents()以保持Windows消息流畅,或者您可以以正确的方式执行并实际实现,以帮助解决您的情况正确穿线。它很容易使用Thread类,委托和调用进行连接,甚至更容易使用BackgroundWorker,它几乎被构建,其唯一目的是在另一个线程上执行任务并报告0/100%的进度值。 / p>