2011-11-03 117 views
4

考虑这个例子:更新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)) 

但它并没有做的工作。
最好的办法是什么?

回答

1

除了创建自己的线程外,还可以使用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 
1

将启用按钮的属性绑定到VM中的属性(比如ProcessComplete)。

使用按钮onclick事件触发虚拟机中启动漫长流程的方法。在进程运行时保持ProcessComplete False,然后在完成时将其设置为True。

2

按钮单击事件由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欢迎使用属性,而不是上的按钮。