...程序长时间执行任务时窗体会失去响应造成假死避免重复点击按钮的...

VB.NET学习笔记:程序长时间执⾏任务时窗体会失去响应造成假死避免重复点
按钮的解决思路
⼀、暴露问题——窗体假死
新建⼀个Windown窗体应⽤程序,在窗体代码⾥写⼊如下代码:
Public Class MainForm
Dim intTime As Integer = 0
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
'运⾏计时器
intTime = 0
Me.Timer1.Enabled = True
'******************************
'模拟长时间⼯作代码
Me.Label1.Text = "开始⼯作!"
For i As Integer = 0 To 100
'模拟长时间⼯作
Threading.Thread.Sleep(100)
'显⽰⼯作进度
Me.Label1.Text = i.ToString & "%"
Next
'******************************
'关闭计时器
Me.Timer1.Enabled = False
End Sub
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Timer1.Interval = 1000 '设置引发Timer1.Tick事件的时间间隔
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
intTime += Me.Timer1.Interval
'实时显⽰运⾏时间
Me.Label2.Text = String.Format("{0}秒", intTime / 1000)
End Sub
End Class
因为需长时间运⾏代码,所以想整个显⽰⼯作进度的界⾯,2种实现⽅式:百分⽐⽅式适⽤于循环等
知道总⼯作量的代码、计时显⽰适⽤于⽆法事先计算总⼯作量的代码,可是测试时却发现事与愿为,代码并没有依我们所想象的那样在窗体界⾯实时显⽰⼯作进度或⼯作时间,⽽且窗体失去响应,⽆法拖动,点击按钮也没有反应,甚⾄计时器压根就没有启动计时⼯作。因为不知道是否成功运⾏了按钮事件代码,所以忍不住的多次重复点击按钮,⼜发现程序会重复运⾏按钮事件代码,即使将按钮的Enalbe 属性设置为 false还是会触发点击事件,这可不是我想的结果。张家口医学院
于是从⽹上搜索得知:winfrom会有消息排队,当程序繁忙⼯作(本代码中,程序忙于处理循环代码,⽆法分⼼去处理其他事情了)的时候消息被堵住了,所以整个窗体界⾯就处于假死状态,这时候当你点击按钮时,系统还是会记录你的点击事件,等程序响应回来后,继续后续的点击事件。
⼆、使⽤Application.DoEvents()解决窗体假死
代码繁忙⼯作的时候,可以使⽤Application.DoEvents()语句让程序喘⼝⽓去解决其他事情,如下在循环⾥加⼊该语句。
'******************************
'模拟长时间⼯作代码
Me.Label1.Text = "开始⼯作!"
For i As Integer = 0 To 100
'模拟长时间⼯作
Threading.Thread.Sleep(100)
Application.DoEvents()'关键语句,解决⼤问题
'显⽰⼯作进度
Me.Label1.Text = i.ToString & "%"
Next
'MessageBox.Show("⼯作完成!")
'******************************
这时窗体可以正常实时的显⽰进度和⼯作时间,⽽且窗体也可以及时响应了,但当你不断点击按钮时程序就会不断的多次重复执⾏事件代码,如何避免重复点击按钮呢?可以设置按钮的Enalbe 属性,如下代码:
'******************************
'设置按钮不响应点击
牛黄狗宝Me.Button1.Enabled = False
'模拟长时间⼯作代码
Me.Label1.Text = "开始⼯作!"
For i As Integer = 0 To 100
'模拟长时间⼯作
Threading.Thread.Sleep(100)
Application.DoEvents()
'显⽰⼯作进度
Me.Label1.Text = i.ToString & "%"
Next
'⼯作结束再设置回来,使其响应点击
Me.Button1.Enabled = True
'******************************
当按钮的Enalbe 属性设置为False时,其样式会变成灰⾊不太美观,使⽤AddHandler和RemoveHandler语句可以解决此问题,按钮还是跟正常的样式⼀样,只是在⼯作期间不会响应点击,代码如下:
'******************************
'暂时移除Button1按钮点击事件
RemoveHandler Button1.Click, AddressOf Button1_Click
'模拟长时间⼯作代码
Me.Label1.Text = "开始⼯作!"
For i As Integer = 0 To 100
'模拟长时间⼯作
Threading.Thread.Sleep(100)
Application.DoEvents()
'显⽰⼯作进度
Me.Label1.Text = i.ToString & "%"
Next
'⼯作结束后记得恢复Button1按钮点击事件
AddHandler Button1.Click, AddressOf Button1_Click
'******************************
有关AddHandler和RemoveHandler语句的使⽤请参阅《》。
三、异步调⽤委托解决窗体假死
据说Application.DoEvents()会影响程序性能,要尽量的少⽤,所以想着把原按钮Button1的单击事件代码改为如下代码:
'定义委托
马鞍山杀人案
Private Delegate Sub SubDelegate(ByVal text As String)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'运⾏计时器
intTime = 0
Me.Label2.Text = "开始⼯作!"
Me.Timer1.Enabled = True
'******************************
'暂时移除Button1按钮点击事件
RemoveHandler Button1.Click, AddressOf Button1_Click
'模拟长时间⼯作代码
'定义委托
济南铁路局Dim mi As MethodInvoker = New MethodInvoker(AddressOf LongTimeSub)
'异步调⽤委托
mi.BeginInvoke(Nothing, Nothing)
'⼯作结束后记得恢复Button1按钮点击事件
AddHandler Button1.Click, AddressOf Button1_Click
'******************************
'关闭计时器
Me.Timer1.Enabled = False
End Sub
Private Sub LongTimeSub()
Me.Invoke(New SubDelegate(AddressOf SetText), "开始⼯作")
For i As Integer = 0 To 100
'模拟长时间⼯作
Threading.Thread.Sleep(100)
'显⽰⼯作进度
'Me.Label1.Text = i.ToString & "%"
'改⽤委托
Me.Invoke(New SubDelegate(AddressOf SetText), i.ToString & "%")
Next
End Sub
Private Sub SetText(ByVal text As String) '此处是接受委托,执⾏函数
Me.Label1.Text = text
End Sub
长时间⼯作代码封装到⼀个LongTimeSub⽅法中,然后在 Button1_Click⽅法中使⽤异步调⽤委托的⽅式执⾏该⽅法,注意Label1显⽰也必须⽤委托,否则会报错:System.InvalidOperationException:“线程间操作⽆效: 从不是创建控件“Label1”的线程访问它。”,如图:
界⾯能正常显⽰进度了,但计时器并没有⼯作,⽽且多次重复点击按钮进度显⽰会很乱,⼀下80%,⼀下⼜75%,好像是每次点击就马上执⾏代码?我们在按钮的 Button1_Click⽅法结束处设置断点,运⾏发现执⾏代码时并没有在mi.BeginInvoke(Nothing, Nothing)这句卡住,⽽是直接向下运⾏到 Button1_Click⽅法结束。如图:
原来异步调⽤委托时,委托关联的LongTimeSub⽅法是在另⼀个线程中⼯作,并不会阻塞当前线程,所以当前线程中执⾏的
Button1_Click⽅法能够顺利的执⾏完毕。
把 Button1_Click⽅法中的AddHandler Button1.Click, AddressOf Button1_Click和Me.Timer1.Enabled = False语句搬到LongTimeSub⽅法中,成功解决问题,代码正如所想象的那样执⾏。代码修改如下:
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
'运⾏计时器
微软安全技术中心
intTime = 0
Me.Label2.Text = "开始⼯作!"
Me.Timer1.Enabled = True
'******************************
'暂时移除Button1按钮点击事件
RemoveHandler Button3.Click, AddressOf Button3_Click
'模拟长时间⼯作代码
'定义委托
Dim mi As MethodInvoker = New MethodInvoker(AddressOf LongTimeSub)
'异步调⽤委托
mi.BeginInvoke(Nothing, Nothing)
'******************************
End Sub
Private Sub LongTimeSub()
Me.Invoke(New SubDelegate(AddressOf SetText), "开始⼯作")
For i As Integer = 0 To 100
'模拟长时间⼯作
Threading.Thread.Sleep(100)
'显⽰⼯作进度
'Me.Label1.Text = i.ToString & "%"
'改⽤委托
玩笑莫泊桑
Me.Invoke(New SubDelegate(AddressOf SetText), i.ToString & "%")
Next
'⼯作结束后记得恢复Button1按钮点击事件
AddHandler Button3.Click, AddressOf Button3_Click
'关闭计时器
Me.Timer1.Enabled = False
End Sub
四、思考
如果要在长时间⼯作的代码弹出⼀个等待窗⼝来显⽰进度,⽽代码⼜要能继续往下执⾏,类似于正在加载⽹页、游戏啊等这样的效果,可否做到?

本文发布于:2024-09-21 18:40:23,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/599974.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:代码   按钮   点击   事件   程序   委托
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议