问题描述
我做的winform程序,窗口里有5个数字按扭。在点击一个控制按扭之后,想让5个数字按扭依次消失,再重新显示出来。每两个按扭之间有一个延时。消失没有问题,可是重新显示的时候,按扭不是依次显示的,而且等待了很久,一下全部显示出来...一下是部分代码,求大神指点!多谢~privatevoidbutton25_Click(objectsender,EventArgse){for(inti=0;i<N;i++){this.Controls[5*i+4].Visible=false;Thread.Sleep(200);}for(inti=0;i<N;i++){this.Controls[5*i+4].Visible=true;Thread.Sleep(200);}}
解决方案
解决方案二:
你占用主线程的操作必须结束,不能让你那几行代码就把人家cpu给死循环了。学学使用定时器。
解决方案三:
能不能麻烦讲具体点...为什么第一个循环可以,而第二个循环不行?
解决方案四:
加上一个application.doevents()在循环里面
解决方案五:
Visible=false本来就是当时就隐藏的,因为简单地进行界面刷新就行了,不用主线程进行计算。但是Visible=true则不行。要想让控件很好地显示,并且不影响用户体验,那么要异步编程。要让主线程能够空闲地去处理windows消息。例如要轮流显示Label1、Label2,可以写privatevoidbutton1_Click(objectsender,EventArgse){this.label1.Visible=false;this.label2.Visible=false;newSystem.Threading.Timer(x=>{this.BeginInvoke((Action)ShowLabel1);},null,3000,0);}privatevoidShowLabel1(){this.label1.Visible=true;newSystem.Threading.Timer(x=>{this.BeginInvoke((Action)ShowLabel2);},null,3000,0);}privatevoidShowLabel2(){this.label1.Visible=false;this.label2.Visible=true;}
它们是异步操作的。你的主线程可以去处理其它千万行代码,而不是在你的那几行代码上死循环。
解决方案六:
要让界面比较流畅地响应用户视觉要求和鼠标键盘操作,那么就要让你的主线程尽快地空闲。绝不能写出死循环的所谓的“监控”程序。这种监控其实就是导致“死”的结果。要让主线程空闲,这是一个基本概念。系统与用户交互,这是一个过程的起点和终点,你必须及时释放你的程序、不占用主线程,才能保证交互。你应该学会用这种基于事件的逻辑方式来设计windows下的交互程序。
解决方案七:
在.net下除非不得已,尽量不要使用Application.DoEvent(并且它也只适合winform,不适合wpf等)。它是用来移植vb6程序的。在15年前的vb中使用这种方式,跟后来出现的.net的处理方式完全不同。它是单线程的,并且它实际上很容易让windows消息泵提前执行而产生响应用户操作时的恶性循环(你看不到的windows消息如滚雪球般地越来越多,而你什么也看不见,只能感觉到电脑越来越卡)。在.net下,应该写多线程的程序。合理延迟显示(而不是提前显示),充分利用你的cpu,特别是多核cpu。用异步编程方式,而不要使用Application.DoEvent。在.net的机制下,你可以看到所有注册给主线程的UI操作都是排着队列自动被调用的。是在你的代码结束之后,主线程空闲时才自动执行的。而Application.DoEvent本身是单线程的,而且它是提前强行执行原本在后边的windows消息泵的机制。这就相当于.net方式是一个“有秩序地轨道交通”,自定义程序总是尽快地让出主线程来,都强调让主线程去调度;而DoEvent模式则是一个“随时将等在很远以外的其它列车强插到当前位置”的操作,windows消息泵被执行时本身就会又去带进来更多的消息,这就形成了恶性循环。并且它也是一直霸占主线程的。
解决方案八:
引用4楼sp1234的回复:
Visible=false本来就是当时就隐藏的,因为简单地进行界面刷新就行了,不用主线程进行计算。但是Visible=true则不行。要想让控件很好地显示,并且不影响用户体验,那么要异步编程。要让主线程能够空闲地去处理windows消息。例如要轮流显示Label1、Label2,可以写privatevoidbutton1_Click(objectsender,EventArgse){this.label1.Visible=false;this.label2.Visible=false;newSystem.Threading.Timer(x=>{this.BeginInvoke((Action)ShowLabel1);},null,3000,0);}privatevoidShowLabel1(){this.label1.Visible=true;newSystem.Threading.Timer(x=>{this.BeginInvoke((Action)ShowLabel2);},null,3000,0);}privatevoidShowLabel2(){this.label1.Visible=false;this.label2.Visible=true;}它们是异步操作的。你的主线程可以去处理其它千万行代码,而不是在你的那几行代码上死循环。
非常感谢!明白了。
解决方案九:
引用4楼sp1234的回复:
Visible=false本来就是当时就隐藏的,因为简单地进行界面刷新就行了,不用主线程进行计算。但是Visible=true则不行。要想让控件很好地显示,并且不影响用户体验,那么要异步编程。要让主线程能够空闲地去处理windows消息。例如要轮流显示Label1、Label2,可以写privatevoidbutton1_Click(objectsender,EventArgse){this.label1.Visible=false;this.label2.Visible=false;newSystem.Threading.Timer(x=>{this.BeginInvoke((Action)ShowLabel1);},null,3000,0);}privatevoidShowLabel1(){this.label1.Visible=true;newSystem.Threading.Timer(x=>{this.BeginInvoke((Action)ShowLabel2);},null,3000,0);}privatevoidShowLabel2(){this.label1.Visible=false;this.label2.Visible=true;}它们是异步操作的。你的主线程可以去处理其它千万行代码,而不是在你的那几行代码上死循环。
可不可以这么理解,Visible=false时,只需界面刷新,没有UI改变的命令需要等待主线程处理;Visible=true时,UI改变的命令被存储到队列中,等待Click事件结束后,这些命令才被主线程处理,所以按钮是一起显示的,没有延时。
解决方案十:
你的UI就是你的主线程,你在里面Sleep将造成UI线程阻塞,你的每个Sleep是循环进行的,循环体内只有一个改变显示属性的操作,几乎没有给你停歇的时间,等于是连续阻塞,UI一直得不到响应,只有等离开循环UI才会恢复,给你假象就类似于一起显示了。而你说的你的第一个循环没问题,那是不可能的,肯定阻塞的,你可能仅仅是凭的感觉,你可以将Sleep放大一点,再拖动窗体看看。
解决方案十一:
引用9楼ajianchina的回复:
你的UI就是你的主线程,你在里面Sleep将造成UI线程阻塞,你的每个Sleep是循环进行的,循环体内只有一个改变显示属性的操作,几乎没有给你停歇的时间,等于是连续阻塞,UI一直得不到响应,只有等离开循环UI才会恢复,给你假象就类似于一起显示了。而你说的你的第一个循环没问题,那是不可能的,肯定阻塞的,你可能仅仅是凭的感觉,你可以将Sleep放大一点,再拖动窗体看看。
第一个循环拖动窗体确实会有阻塞,但是按钮确实是依次、有延时的消失的。所以是先处理了显示属性的操作,再进行的睡眠?可是如果从第二个循环来看的话,是等到整个循环结束,退出Click事件之后,显示属性的操作才得到了执行。
解决方案十二:
引用6楼sp1234的回复:
在.net下除非不得已,尽量不要使用Application.DoEvent(并且它也只适合winform,不适合wpf等)。它是用来移植vb6程序的。在15年前的vb中使用这种方式,跟后来出现的.net的处理方式完全不同。它是单线程的,并且它实际上很容易让windows消息泵提前执行而产生响应用户操作时的恶性循环(你看不到的windows消息如滚雪球般地越来越多,而你什么也看不见,只能感觉到电脑越来越卡)。在.net下,应该写多线程的程序。合理延迟显示(而不是提前显示),充分利用你的cpu,特别是多核cpu。用异步编程方式,而不要使用Application.DoEvent。在.net的机制下,你可以看到所有注册给主线程的UI操作都是排着队列自动被调用的。是在你的代码结束之后,主线程空闲时才自动执行的。而Application.DoEvent本身是单线程的,而且它是提前强行执行原本在后边的windows消息泵的机制。这就相当于.net方式是一个“有秩序地轨道交通”,自定义程序总是尽快地让出主线程来,都强调让主线程去调度;而DoEvent模式则是一个“随时将等在很远以外的其它列车强插到当前位置”的操作,windows消息泵被执行时本身就会又去带进来更多的消息,这就形成了恶性循环。并且它也是一直霸占主线程的。
答的真详细!
解决方案十三:
不用Thread.Sleep,用Delay试试。下面是以秒计算的,你可以根据自己的理解,改成毫秒。///<summary>///延时函数///</summary>///<paramname="delayTime">需要延时多少秒</param>///<returns></returns>publicstaticboolDelay(intdelayTime){DateTimenow=DateTime.Now;ints;do{TimeSpanspand=DateTime.Now-now;s=spand.Seconds;Application.DoEvents();}while(s<delayTime);returntrue;}
解决方案十四:
使用定时器,或delay,sleep会阻塞线程