C#中progressbar进度条何时开始显示?

6lilu9 2018-12-04 04:50:25
题外:这是上一篇贴子https://bbs.csdn.net/topics/392486090?page=1#post-403592169
的相似问法,上篇帖子措词不当,导致许多大神误会,另开新贴。

如下代码:

private void button2_Click(object sender, EventArgs e)
{
progressBar1.Maximum = 3;//设置最大长度值
progressBar1.Minimum = 0;

for (int i = progressBar1.Minimum; i <= progressBar1.Maximum; i++)//循环
{
System.Threading.Thread.Sleep(500);//暂停1秒
textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + i.ToString() + "项";
i.ToString();
progressBar1.Value = i; //让进度条增加一次
this.Refresh();
}
}

效果如下图:


我的问题是:应该当文本框的内容是:共3项工作,已经完成1项 时进度条就应该开始显示才对呀。
可效果却是 当文本框的内容是:共3项工作,已经完成2项时 进度条才开始显示1/3(不是视觉误差,可用F8调试)
总是慢半拍,不知道是哪里的问题。
...全文
1537 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
6lilu9 2018-12-07
  • 打赏
  • 举报
回复
引用 14 楼 sp1234_maJia 的回复:
DoEvents 语句的问题我再重复说明一下。
请教老师: 现有一个简单窗体,一个datagridview,一个button 点击button后,会从数据库中提取数据,经过长时间的运算,最后在datagridview中显示。 第一次点击没问题,我现在的提问从第二次点击开始 第二次点击button后,会首先执行下面两句代码datagridview.datasource=null; application.DoEvents(); 然后再开始提取数据、运算,datagridview中显示。 这就是当时我老师教的。 如果不用application.DoEvents();,用什么合适的方法清除当前窗体呢?
6lilu9 2018-12-07
  • 打赏
  • 举报
回复
引用 14 楼 sp1234_maJia 的回复:
DoEvents 语句的问题我再重复说明一下。
请教老师: 现有一个简单窗体,一个datagridview,一个button 点击button后,会从数据库中提取数据,经过长时间的运算,最后在datagridview中显示。 第一次没问题,我现在的提问从第二次开始 第一次点击button后,会首先执行下面两句代码datagridview.datasource=null; application.DoEvents(); 然后再开始提取数据、运算,datagridview中显示。 这就是当时我老师教的。 如果不用application.DoEvents();,用什么合适的方法清除当前窗体呢?
良朋 2018-12-07
  • 打赏
  • 举报
回复
这个问题我也曾遇到过,我用的多线程异步加载的,也是达不到同步,除非你sleep的时间足够长,等待界面刷新。 大差不错就行了,微软它自己的各种加载进度条也不见得完美。
  • 打赏
  • 举报
回复
引用 23 楼 6lilu9 的回复:
[quote=引用 16 楼 xuzuning 的回复:]
怎么不是视觉误差呢?[code=csharp] private void Do()
{
for (int i = progressBar1.Minimum + 1; i <= progressBar1.Maximum; i++)//循环
{
。。。
var bm = new Bitmap(ClientRectangle.Width, ClientRectangle.Height); //加了这3句
DrawToBitmap(bm, ClientRectangle);
bm.Save(i + ".png", ImageFormat.Png);
。。。
}

可以看到,是一一对应的

请问加这三句代码,保存屏幕是啥意思?[/quote]
那是将图片保存下来,为了查看进度是否实时(虽然我这里试了他的代码也是不实时的,可能系统关系?)。
如果是为了实现功能,你可以看看#7的代码。
如果是为了了解机制,看sp老师讲的吧
6lilu9 2018-12-07
  • 打赏
  • 举报
回复
引用 16 楼 xuzuning 的回复:
怎么不是视觉误差呢?[code=csharp] private void Do() { for (int i = progressBar1.Minimum + 1; i <= progressBar1.Maximum; i++)//循环 { 。。。 var bm = new Bitmap(ClientRectangle.Width, ClientRectangle.Height); //加了这3句 DrawToBitmap(bm, ClientRectangle); bm.Save(i + ".png", ImageFormat.Png); 。。。 } 。 可以看到,是一一对应的
请问加这三句代码,保存屏幕是啥意思?
6lilu9 2018-12-07
  • 打赏
  • 举报
回复
引用 21 楼 以专业开发人员为伍 的回复:
进度条本身控件显然是有优化机制的,不是立刻刷新的。当连续发生许多刷新动作时,这类控件应该是能够自动优化(例如仅仅显示一次)。所以这类控件其实是比价高级一点的控件,比那种许多人以为只要改变一次 .Value 就刷新一遍界面的控件强多了!! 但是现在来看你的编程最起码的理念。你的程序随便卡死了窗口,连窗口的拖动都卡死了,窗口中界面的优化过了的显示不可能正常。
进度条本身控件显然是有优化机制的,不是立刻刷新的。这句话其实就是我要的答案,我也只能理解如此了。 谢谢各位大神,我得抽时间慢慢消化上面的内容。
qvghvsadbbvvhq 2018-12-06
  • 打赏
  • 举报
回复
从1开始才行。
sp1234_maJia 2018-12-06
  • 打赏
  • 举报
回复
DoEvents 语句的问题我再重复说明一下。 DoEvents 是过去 VB 的不支持多线程的编程机制中所提供的一个递归调用 windows 消息泵来处理消息的功能,是 vb3 以前就存在的功能(是否是 vb1.0就存在的功能,我没有验证,所以这类只说24年前的 vb3 就应该是靠这个来“刷新”界面的功能)。它自然也是处理所谓的阻塞造成的用户体验的 bug 的机制。 但是显然那必须万般小心设计程序逻辑,否则就会快速混乱。因为假设在一个过程中任意点你递归地调用 windows 消息泵来触发后续消息处理,当前的消息处理过程只是运行到中间的任一点并没有完成。比如说我们随便划一下鼠标,会在某个控件立刻触发 MouseEnter、许多个MouseMove、MouseLeave事件,那么正常我们的理解自然是各个事件的处理过程被顺序激活的。但是DoEvents是什么呢?是时间处理过程运行到一半,调试跟踪会发现程序突然跳进入了其它的事件处理过程,过一会儿才会突然跳回来之前的过程的下一条语句。当你有不只一个过程写了 DoEvents 代码的时候,这种跳转就指数级加倍了。特别是当程序本身会触发一些事件时,甚至会产生事件触发的组合爆炸。 主要原因是 DoEvents 并不是正常地事件响应处理,而是打乱次序进行递归的思路。所以其实宁可用 Refresh 都不轻易用 DoEvents。但是在早期的 VB 中,除了用 Timer 来巧妙地达到异步设计(例如在游戏中将长的事务分隔为多帧连续操作,就是要使用好 Timer 机制),有些人为了省事儿,对于非常简单地、暂时不怕捅娄子不怕热出大事儿的程序,使用 DoEvens。在 vb.net 中就应该使用 .net 的多线程、异步机制来保证 UI 灵活高效率地事件响应,而不是使用 VB 的 DoEvents 方式。
  • 打赏
  • 举报
回复
比如说你去应聘,人家说“一周之内随时电话通知你”,那么你就堵住人家公司大门、你替人家干所有的工作(Refresh),这是真正的异步、事件响应之道吗?这显然是不理解生活常识的。 同样地,假设 UI 交互程序设计中本身就有自己的长事务跟 UI 机制交互的动做,那么就要设计异步的、交互用事件来驱动的操作过程。就不能满脑子只知道最蠢笨的阻塞式编程思路。就算是最差劲的阻塞式思路,那么许多人也知道必须滥用线程来写这类代码,而不是写一个什么 for 循环里边的 Refresh 代码。 时间上每一个刚学编程的人都会在 for 循环里边再插入一个 this.Refresh(....) 代码这种想法。你要想多学点计算机交互程序设计知识,那么就把所使用到的 UI 设计模式稍微高级一点点!!
  • 打赏
  • 举报
回复
你看到,第一层设计知识,根本没有什么 Refresh语句。 第二层设计知识,根本没有 Sleep 语句。
  • 打赏
  • 举报
回复
重点在你你满脑子是阻塞 UI 主线程的思路。你看连拖动窗口、关闭窗口的操作都卡死掉了,UI 界面刷新还能好吗?
    progressBar1.Maximum = 3;//设置最大长度值
    progressBar1.Minimum = 0;

    for (int i = progressBar1.Minimum; i <= progressBar1.Maximum; i++)//循环
    {
        System.Threading.Thread.Sleep(2000);//暂停1秒
        textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + i.ToString() + "项";
        i.ToString();
        progressBar1.Value = i; //让进度条增加一次
        this.Refresh();
    }
如果你实在是只能理解阻塞式编程,可以写
    progressBar1.Maximum = 3;//设置最大长度值
    progressBar1.Minimum = 0;
    System.Threading.ThreadPool.QueueUserWorkItem(h =>
    {
        for (int i = progressBar1.Minimum; i <= progressBar1.Maximum; i++)//循环
        {
            var j = i;
            System.Threading.Thread.Sleep(2000);//暂停1秒
            this.BeginInvoke((Action)delegate
            {
                textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + j.ToString() + "项";
                progressBar1.Value = j;
            });
        }
    });
此时你试试看拖动窗口、关闭窗口,能不能看到 UI 操作是干净和灵敏的? 每一个线程都需要占用大量的栈空间,系统中可能有大量的异步、事件操作,可能有至少几百上千个地方需要捕获监听各种事件,所以更好更可取地方式是从一开始就理解什么叫异步编程技术,这样写出来的程序不但能让 UI 线程是干净和灵敏的,而且整个进程内不也根本不会滥用线程、阻塞(Sleep)语句。
private async void button2_Click(object sender, EventArgs e)
{
    progressBar1.Maximum = 3;//设置最大长度值
    progressBar1.Minimum = 0;
    for (int i = progressBar1.Minimum; i <= progressBar1.Maximum; i++)//循环
    {
        await System.Threading.Tasks.Task.Delay(2000);
        textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + i.ToString() + "项";
        progressBar1.Value = i;
    }
}
这与上面的使用 QueueUserWorkItem(...) 方法的区别就不仅仅是只看这种小程序的界面了,而是需要看到大的复杂点的系统的背后的知识了。
bbjiabcd 2018-12-06
  • 打赏
  • 举报
回复
你的问题在于程序语句或是结构,而不是措辞不当,上一篇帖子都回复得很清楚了。
bbjiabcd 2018-12-06
  • 打赏
  • 举报
回复
说了不要用this.Refresh();,这就是导致问题的原因。跟progressbar没关系。 要不就按照7楼那样改成Application.DoEvents();,要不就改程序结构,异步或者用timer。
  • 打赏
  • 举报
回复
进度条本身控件显然是有优化机制的,不是立刻刷新的。当连续发生许多刷新动作时,这类控件应该是能够自动优化(例如仅仅显示一次)。所以这类控件其实是比价高级一点的控件,比那种许多人以为只要改变一次 .Value 就刷新一遍界面的控件强多了!! 但是现在来看你的编程最起码的理念。你的程序随便卡死了窗口,连窗口的拖动都卡死了,窗口中界面的优化过了的显示不可能正常。
  • 打赏
  • 举报
回复
我给你打个比方,假设有两几人协调好了钻进一个洞里,他们协调得非常专业(消息泵),一致刷新界面很流畅。 现在你把洞给霸占了,那么他们再协调、谦让、优化得好又能怎样?你干扰了他们整体。这个时候你怪他们不协调,问他们为什么总是慢一拍,这就是没有把自己对整体的干扰过程算在内!
  • 打赏
  • 举报
回复
你会看到,代码执行到 Sleep 的时候,窗口的拖动操作都死掉了,你把 UI 主线程卡死了。
  • 打赏
  • 举报
回复
引用 16 楼 xuzuning 的回复:
怎么不是视觉误差呢?
        private void Do()
{
for (int i = progressBar1.Minimum + 1; i <= progressBar1.Maximum; i++)//循环
{
System.Threading.Thread.Sleep(1500);//暂停1.5秒
progressBar1.Value = i; //让进度条增加一次
System.Threading.Thread.Sleep(500);//暂停0.5秒,进度条跳转也要时间,避免产生视觉误差
textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + i.ToString() + "项";
i.ToString();
var bm = new Bitmap(ClientRectangle.Width, ClientRectangle.Height); //加了这3句
DrawToBitmap(bm, ClientRectangle);
bm.Save(i + ".png", ImageFormat.Png);
this.Refresh();
}

}

可以看到,是一一对应的





我运行了一下你的代码,在我这里对应不了
xuzuning 2018-12-06
  • 打赏
  • 举报
回复
怎么不是视觉误差呢?
        private void Do()
{
for (int i = progressBar1.Minimum + 1; i <= progressBar1.Maximum; i++)//循环
{
System.Threading.Thread.Sleep(1500);//暂停1.5秒
progressBar1.Value = i; //让进度条增加一次
System.Threading.Thread.Sleep(500);//暂停0.5秒,进度条跳转也要时间,避免产生视觉误差
textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + i.ToString() + "项";
i.ToString();
var bm = new Bitmap(ClientRectangle.Width, ClientRectangle.Height); //加了这3句
DrawToBitmap(bm, ClientRectangle);
bm.Save(i + ".png", ImageFormat.Png);
this.Refresh();
}

}

可以看到,是一一对应的
  • 打赏
  • 举报
回复
progressBar1.Maximum = 3;//设置最大长度值
progressBar1.Minimum = 0;

for (int i = progressBar1.Minimum; i <= progressBar1.Maximum; i++)//循环
{
textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + i + "项";
progressBar1.Value = i; //让进度条增加一次
Thread.Sleep(1000);
Application.DoEvents();
}

lz试试这样
zxy2847225301 2018-12-04
  • 打赏
  • 举报
回复
引用 5 楼 zxy13826134783 的回复:
private void button2_Click(object sender, EventArgs e)
{
progressBar1.Maximum = 3;//设置最大长度值
progressBar1.Minimum = 0;
System.Threading.Thread thread = new System.Threading.Thread(Do);
thread.Start();
thread.IsBackground = true;

}

private void Do()
{
for (int i = progressBar1.Minimum + 1; i <= progressBar1.Maximum; i++)//循环
{
System.Threading.Thread.Sleep(1500);//暂停1.5秒
progressBar1.Value = i; //让进度条增加一次
System.Threading.Thread.Sleep(500);//暂停0.5秒,进度条跳转也要时间,避免产生视觉误差
textBox1.Text = "共" + progressBar1.Maximum + "项工作 ,已经完成" + i.ToString() + "项";
i.ToString();
this.Refresh();
}

}



注意:跨线程更新ui,这里采取不安全的做法,需要再加一句代码:
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;//这一行是关键,不安全的做法
}
加载更多回复(5)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧