ProgressBar在阻塞的UI线程中更新

问题描述

| 为什么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。我确实了解线程,DoEvents,异步/等待,但这不是答案。     

解决方法

我认为在不拆卸Windows的情况下很难完全回答这个问题,这对我来说现在太繁琐了。 但是基本上,当您在WinForms ProgressBar控件上设置.Value时,它所做的只是调用带有消息1026(PBM_SETPOS)的SendMessage,它告诉Windows进度栏设置其位置。 我可以得出结论,Windows进度条响应PBM_SETPOS以及WM_PAINT同步重新绘制自己。也许它正在另一个线程上运行计时器以执行炫目的眩光动画,并且无需等待绘制消息即可重绘控件。 无论哪种方式,您所看到的只是Windows内部-在WM_PAINT处理程序之外绘制内容并不是一种不寻常的技术,即使它不是教科书中的处理方式。 实际上,查看PBM_SETPOS的文档(http://msdn.microsoft.com/zh-cn/library/bb760844(v=vs.85).aspx),该文档记录为导致重绘-我想这是故意这样做是为了帮助懒惰/缺乏经验的人阻止进度条更新工作,而没有通常的麻烦。     ,有不同的方法来回答您的问题。让我解释。如果您要执行一个漫长的任务,而用户必须等待才能继续,那么实际上是否在UI线程上运行都没有关系。无论哪种方式,用户都必须等待任务完成才能继续,因此他们仍然无法使用该应用程序。 但是,在您可能希望用户在任务运行时与应用程序的不同部分进行交互的操作中,您会创建一个工作线程来执行所述任务。您需要了解主UI线程是用于处理UI的。绘制进度条是UI的一部分。 WinForms / .NET的设计体系结构是您可以通过BackgroundWorker类创建后台线程,或者自行连接原始线程。其设计。 但是,所有这些都将在C#5.0中使用async和await关键字以及Task对象进行彻底更改。您可以在channel9上搜索TechDays 11,或访问我的Facebook(有关它的几篇文章已发布)。 如果您倾向于在任务中保持对UI线程调用Application.DoEvents()的操作以保持Windows消息的畅通,则可以帮助纠正您的情况,或者您可以以正确的方式进行操作并实际实现线程。它非常容易使用Thread类进行连接,委托和调用,甚至更容易使用BackgroundWorker,该BackerWorker的设计宗旨是在另一个线程上执行任务并报告0/100%的渐进值。