C# 阻止的UI线程中的ProgressBar更新

C# 阻止的UI线程中的ProgressBar更新,c#,.net,winforms,multithreading,progress-bar,C#,.net,Winforms,Multithreading,Progress Bar,为什么ProgressBar会在理论上阻塞的UI线程中更新 在简单的应用程序中,我有一个进度条和一个标签。我在UI线程中运行了一个耗时的方法,试图更新ProgressBar和标签。这是不应该工作的,因为阻塞的UI线程。但是ProgressBar正在更新 在我对表单执行任何操作并将其冻结之前,ProgressBar将更新(标签不会更新) 为什么呢 示例代码(在表单上放置按钮、进度条和标签): ProgressBar正在更新,标签未更新。我的问题不是如何更新标签,而是为什么ProgressBar会更

为什么ProgressBar会在理论上阻塞的UI线程中更新

在简单的应用程序中,我有一个进度条和一个标签。我在UI线程中运行了一个耗时的方法,试图更新ProgressBar和标签。这是不应该工作的,因为阻塞的UI线程。但是ProgressBar正在更新

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

为什么呢

示例代码(在表单上放置按钮、进度条和标签):


ProgressBar正在更新,标签未更新。我的问题不是如何更新标签,而是为什么ProgressBar会更新。我确实知道线程、DoEvents、async/await,但这不是答案。

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

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

然而,这一切都将随着C#5.0以及async和await关键字以及任务对象的使用而发生变化。你可以在channel9上搜索TechDays 11,也可以访问我的网站(发布了一些关于它的帖子)


如果您希望在任务中保持对UI线程调用应用程序.DoEvents()的操作以保持Windows消息的流动,您可以帮助纠正这种情况,或者您可以采用正确的方法并正确地实现线程。使用Thread类、委托和调用很容易连接起来,使用BackgroundWorker更容易,BackgroundWorker的设计基本上是为了在另一个线程上执行任务并报告0/100%的进度值。

我认为如果不分解一些窗口,很难完全回答这个问题,这对我来说是太多的工作了

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

我的结论是,Windows进度条会同步重画自身,以响应PBM_SETPOS和WM_PAINT。或者它在另一个线程上运行一个计时器,以执行奇特的眩光动画,这样就可以在不等待绘制消息的情况下重新绘制控件

无论哪种方式,你看到的都是Windows的内部结构——在WM_PAINT Handler之外绘制东西并不是一种不同寻常的技术,尽管这不是教科书上的做法


事实上,查看PBM_SETPOS()的文档,它被记录为导致重新绘制-我想这是为了帮助懒惰/没有经验的人在正常操作过程中,让阻止进度条更新正常工作。

为什么不应该正常工作?你可以展示一些代码吗?我有一个非常严格的政策,对建议使用
Application.DoEvents
的每个答案进行否决,但因为这是一个非常好的答案,所以我试图抵制这种诱惑……我不明白这与这个问题有什么关系。OP表示UI消息泵已停止(即无事件)。他问为什么progressbar仍在绘制(即,为什么progressbar似乎正在捕获WM_绘制消息(这是不真实的))。感谢您的回答。我非常了解如何创建额外的线程,为什么不使用Application.DoEvents(),并且我知道新的async/await概念。我的问题是关于一些不同的东西-为什么ProgressBar不断更新它应该被冻结的位置(因为故意冻结UI)。它一直在更新,直到我在表格上做任何事情-然后整个表格停止响应。很抱歉,在修改之前我误解了这个问题。我的答案真的不适用于这个问题。死亡比你,听起来像是一个答案。
private void button1_Click(object sender, EventArgs e)
{
    while (true)
    {
        progressBar1.Value += 1;
        label1.Text += "1";
        Thread.Sleep(100);
    }
}