更新UI异步?
考虑这个例子:更新UI异步?
Private Sub Button_Click(
sender As Button, e As RoutedEventArgs) Handles btn.Click
sender.IsEnabled = False
Thread.Sleep(5000)
sender.IsEnabled = True
End Sub
我的方案的Button_Click
是VM中的命令代表,以及Thread.Sleep
是一些长期运行的进程(约2-10秒)。
我想,当用户调用该命令时,应立即更新禁用该按钮的UI,以便用户在运行时无法执行该操作,然后执行该操作,然后在操作完成时解除对按钮的阻止。
我试图包裹中间线如下所示:
Dispatcher.BeginInvoke(Sub() Thread.Sleep(5000))
但它并没有做的工作。
最好的办法是什么?
除了创建自己的线程外,还可以使用BackgroundWorker控件。 通过调用Method“RunWorkerAsync”,DoWork事件get在另一个线程中调用。
通过从您的UI线程调用方法“CancelAsync”,您可以将Backgroundworker设置为“Cancellation Pending”(控件的属性“CancellationPending”为true)。在长时间运行的后台线程中,您可以检查该属性(例如,如果您有循环:一旦CancellationPending为真,退出循环)。这是安全中止线程的一个很好的功能。
除了与BackgroundWorker的,你也可以报告线程的进展(如使用的进度)
例子:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
'** Set to true if you want the ReportProgress Event
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.WorkerSupportsCancellation = True
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim i As Integer
Dim n As Integer = 100
Dim iLastPerc As Integer
While Not BackgroundWorker1.CancellationPending AndAlso i < n
'** Do your time consuming actions here
Threading.Thread.Sleep(500)
If Math.Floor((i/n) * 100) > iLastPerc Then
'** If the Progress has changed. Report
iLastPerc = CInt(Math.Floor((i/n) * 100))
BackgroundWorker1.ReportProgress(iLastPerc)
End If
i += 1
End While
End Sub
Private Sub btnStart_Click(sender As System.Object, e As System.EventArgs) Handles btnStart.Click
'** Run the Backgroundworker
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'** Update the ProgressBar
ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'** Worker is done. Check for Exceptions or evaluate the Result Object if you like
End Sub
Private Sub btnCancel_Click(sender As System.Object, e As System.EventArgs) Handles btnCancel.Click
'** Cancel the worker
BackgroundWorker1.CancelAsync()
MsgBox("Finished!")
End Sub
End Class
关于你问题的代码应该是:
Private Sub btn_Click(sender As Button, e As RoutedEventArgs) Handles btn.Click
sender.IsEnabled = False
Using bw As New BackgroundWorker()
AddHandler bw.DoWork, Sub(s, ea) Thread.Sleep(5000)
AddHandler bw.RunWorkerCompleted, Sub(s, ea) sender.IsEnabled = True
bw.RunWorkerAsync()
End Using
End Sub
将启用按钮的属性绑定到VM中的属性(比如ProcessComplete)。
使用按钮onclick事件触发虚拟机中启动漫长流程的方法。在进程运行时保持ProcessComplete False,然后在完成时将其设置为True。
按钮单击事件由UI线程处理,因此,当您调用thread.sleep
时,您将使UI线程处于睡眠状态,并且在方法结束前您看不到任何更改。
因此,您需要在新线程上运行进程,并在进程结束时使用调度程序更改UI。
例如:
Private event TaskEnded()
Private Sub Button_Click(sender As Button, e As RoutedEventArgs) Handles btn.Click
btn.IsEnabled = False
dim l as new Thread(sub()
Thread.Sleep(5000)
RaiseEvent TaskEnded
End Sub)
l.start()
End Sub
Private Sub bla() Handles Me.TaskEnded
dispatcher.BeginInvoke(sub()
btn.IsEnabled = True
end sub)
End Sub
的MVVM方式,您会在按钮IsEnabled
属性为布尔属性绑定在你的视图模型,并直接更新VM欢迎使用属性,而不是上的按钮。