C#中progressbar基本用法的疑问:进度条显示

6lilu9 2018-11-30 11:52:16
最近老碰到莫名其妙的问题,这次是progressbar了。
问题简单描述:progressbar进度条百分比至少多少时才可见
直接说我测试的结果:与进度条百分比无关,与变量循环的次数相关,第三次才能肉眼可见,前两次看不到。
如下测试:一个窗体上放了一个progressbar,一个button,一个textbox,按钮代码如下:

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

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

测试结果:
当 progressBar1.Maximum 与progressBar1.Minimum 的值分别为10和0时,按F8测试,循环完i=2时,进度条才可见;
当 progressBar1.Maximum 与progressBar1.Minimum 的值分别为100和0时,按F8测试,循环完i=2时,进度条才可见;
当 progressBar1.Maximum 与progressBar1.Minimum 的值分别为100和90时,按F8测试,循环完i=92时,进度条才可见;
........................................................................................................................................................................i=progressBar1.Minimum+2时,才可见

疑问:
在我逻辑中,i=progressBar1.Minimum+1时就已经可见了呀。
...全文
770 17 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
我们说编程模式背后起码是人的理解力和品质为基础的。就好像一个驾驶员不懂任何交规,见到红灯照样冲过去随便在路口冲撞,这是每一个初级的“人”都有的毛病。这能算是随便驾驶的理由吗?能说越初级的人越有理吗?
  • 打赏
  • 举报
回复
写个死循环,插入一个 Sleep,然后又总想自己拼凑一个 Refresh,这其实每一个初学者都能想到。 这样的编程认识,是在你没有学过交互、异步的编程模式基础上,是最初的阶段才有的。
  • 打赏
  • 举报
回复
那你的程序要跟系统成熟的优化过的机制进行交互,而不是你写一个 while 死循环。 如果不理解异步概念,那么你至少有半的编程设计知识根本没有学过、或者没有动过脑筋。
6lilu9 2018-12-04
  • 打赏
  • 举报
回复
引用 14 楼 xuzuning 的回复:
1、楼主的代码是可以运行的,且他只是纠结于进度条何时出现第一个 bar 而已 2、楼主的代码中使用了 this.Refresh();,这将导致窗体不会响应 Paint 事件(运行结束前,拖一个窗体遮挡一下,就会发现程序窗体没反应里) 3、如果压根就不知道 Thread 是什么,那么还能编程吗?(答案是肯定的)
对,我的表述确实成问题 ...
  • 打赏
  • 举报
回复
我要说明的是,即使在过去从不考虑多线程编程概念的 VB 中,使用 Timer 驱动的异步操作,也是用来处理这类编程模式的。 所以,这里不首先纠结什么主线程、子线程之类的概念,而是要理解异步编程概念。此时,异步编程是更加本质的东西,多线程是皮毛。
  • 打赏
  • 举报
回复
使用定时器是一种比较简单的、传统的实现异步的方法。比如说在古老的VB中,没有多线程编程机制,使用 Timer 在窗体 UI 线程轮询操作,但是我们用 Timer 仍然如上面楼层那样地写出等待一小会儿、让UI界面刷新、然后异步调用后续操作 for 循环代码。所以说 Timer 是以前的传统上在 VB 中常用的一种方式(在2、30年前的游戏中,也经常用这种方式) 中间的过程,例如使用 ThreadPool 的异步回调之类的我就不多说了。以现在的技术来说,我只强调
await Task.Delay(....)
这类方式。这显然比声明一个 Timer 来的简单直接。所以我现在推荐这样写代码。这可能就让我们以后再也不用 Timer 了!!!
  • 打赏
  • 举报
回复
DoEvents 实际上是在一个时间处理过程中,递归地调用窗体消息泵中的后续消息处理操作。这就打乱了原本正常的消息处理过程,比如说1事件完毕之后会触发2,然后会触发事件3,而事件3其实是跟事件1 是同一类事件。例如有时候 MouseEnter 跟 MoouseLeave 等等操作事件的自然的时间处理过程安排,就是这种需求。如果以自然的异步处理顺序,没有任何问题,会自然地按照顺序处理。但是以同步函数递归处理,在事件1的处理过程的中间某个诡异地地方插入了 DoEvents 之后,原本的设计次序就乱了、逻辑就乱了,不但是业务逻辑乱了,而且很容易触发事件递归爆发。这其实并不是正常的 Refresh 思路。所以宁可用 Refresh 也不用 DoEvents。 但是及时能很好地执行 Refresh 方法(而不让 UI 线程数据冲突)也并不是科学、优化的做法。UI线程空闲时才会自动刷新。你实在是不会编程时,才应该写 Refresh。 而你会编程模式时,你应该先从业务领域的流程设计做起,先把业务逻辑分析为异步的,那么你就会自然而然地写出简洁的异步、委托回调代码了。
bbjiabcd 2018-12-03
  • 打赏
  • 举报
回复
漏了个,i的定义在外边

        int i = 0;
        private void btnStart_Click(object sender, EventArgs e)
        {
            i = 0;
            progressBar1.Value = 0;
            timer1.Interval = 500;
            timer1.Enabled = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (i < 10)
                progressBar1.Value = ++i;
            else
                timer1.Enabled = false;
        }
bbjiabcd 2018-12-03
  • 打赏
  • 举报
回复
如果仅仅是测试ProgressBar,建议用Timer

        private void btnStart_Click(object sender, EventArgs e)
        {
            i = 0;
            progressBar1.Value = 0;
            timer1.Interval = 1000;
            timer1.Enabled = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (i < 10)
                progressBar1.Value = ++i;
            else
                timer1.Enabled = false;
        }
xuzuning 2018-12-03
  • 打赏
  • 举报
回复
1、楼主的代码是可以运行的,且他只是纠结于进度条何时出现第一个 bar 而已
2、楼主的代码中使用了 this.Refresh();,这将导致窗体不会响应 Paint 事件(运行结束前,拖一个窗体遮挡一下,就会发现程序窗体没反应里)
3、如果压根就不知道 Thread 是什么,那么还能编程吗?(答案是肯定的)
threenewbee 2018-12-02
  • 打赏
  • 举报
回复
加上application.doevents()
xuzuning 2018-12-02
  • 打赏
  • 举报
回复
Invalidate() 触发 Paint 事件
由于需要在其他事件体的循环中加入 application.doevents() 以交出控制权,使 Paint 后其他事件得以响应
Refresh() 不触发 Paint 事件,所以不需要 application.doevents()
但 Refresh() 有可能引起事件循环死锁,不要轻易使用 this.Refresh();

所谓 VB6 那种不支持多线程,只是说他没有提供书写多线程代码的手段,而不是不能以多线程方式运行
所有的事件处理代码都是在子线程中运行的
xuzuning 2018-12-02
  • 打赏
  • 举报
回复
不要太相信你的眼睛,0.5 秒的间隔,对于你来说还是太短了点
我测试了一下,并未出现你说的问题
  • 打赏
  • 举报
回复
.net 以及 windows SDK 内部有一些复杂的机制,现在要学的编程设计知识又那么多,那么有些古老的东西我们就不总是优先去纠结了。实际上底层缓冲和优化,以及为 VB6 那种不支持多线程的程序而设计的 DoEvents 函数,各有个的问题,不但可能产生难以理解、诡异的跟踪调试轨迹,甚至会让你正常程序产生循环事件递归爆炸的灾难。 那么从一开始就学一个“正的”设计模式,一开始就见过各种必备的的设计维度,以后你遇到的怪异问题就会更少。也不需要过早使用太多过去的有问题的东西。
  • 打赏
  • 举报
回复
举一个简单的例子,假设点击按钮之后,要在 Label 控件显示循环变化的数字,可以这样写
private async void button1_Click(object sender, EventArgs e)
{
    for(var i=0; i<1000; i++)
    {
        this.label1.Text = i.ToString();
        await Task.Delay(1500);     //把 1500 改为 500 也不影响这里的说明结论
    }
}
那么这里就要理解 async、await 内部机制,否则就容易被自己骗。当执行到 await 语句的时候(这条语句之前),实际上 button1_Click 方法就执行完毕了。例如我们可以用另外一个按钮和文本标签来验证
private void button2_Click(object sender, EventArgs e)
{
    this.button1_Click(sender, e);
    this.label2.Text = "button2_Click执行完毕";
}
这跟你插入 Refresh 是有不同的思路的。做一件事情,然后就把控制权交给进程,然后再让 i 进入下一个循环节点......这是异步编程思路。而你插入的同步顺序的 Refresh 操作,则容易自己把自己绊倒、搅乱、前后颠倒、难以调试。
  • 打赏
  • 举报
回复
总是感觉“莫名其妙”是因为你总是那初学编程时那种函数式、一根筋、顺序式的思维方式来考虑异步程序设计问题,所以产生了诡异的结果。 程序中假设有成千上万的并发的过程,而某些过程又完全可能是中间有异步操作,那么你应该学会用事件、用异步的方式来设计和调试。不要千方百计往过去的同步顺序操作上去“套”。 假设你程序中从来也不写 Sleep(500) 这类坑代码,你还会编程设计吗?学会了就提高了一大步。
蒋晟 2018-12-01
  • 打赏
  • 举报
回复
WM_PAINT是个低优先级消息,就算你请求了操作系统刷新窗口,操作系统也是会等你消息队列空了才会发WM_PAINT出来。所以到底什么时候刷新得看你的消息队列有多忙。

111,092

社区成员

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

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

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