Update async user interface?

Consider the following example:

Private Sub Button_Click( sender As Button, e As RoutedEventArgs) Handles btn.Click sender.IsEnabled = False Thread.Sleep(5000) sender.IsEnabled = True End Sub 

In my scenario, Button_Click is the team delegate in the virtual machine, and Thread.Sleep is some lengthy process (about 2-10 seconds).

I want that when the user invokes the command, he must immediately update the user interface by disabling the button so that the user cannot execute it during its execution, and then perform this operation, and then, when the operation is completed, unlock the button.

I tried wrapping the middle row as follows:

 Dispatcher.BeginInvoke(Sub() Thread.Sleep(5000)) 

But it did not help. What is the best way to do this?

+4
source share
3 answers

Instead of creating your own stream, you can also use BackgroundWorker Control. By invoking the RunWorkerAsync method, the DoWork event is raised in another thread.

By calling the "CancelAsync" method from your user interface thread, you can set the background worker to "Pending Cancellation" (then the property of the "Cancel request" property). In your long background thread, you can check this property (for example, if you have a loop: exit the loop as soon as CancellationPending is true). This is a pretty nice feature to safely interrupt a stream.

In addition to Backgroundworker, you can also report the progress of the stream (for example, for use in the ProgressBar)

Example:

 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 

In relation to your question, the code should be:

 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
source

The button click event is handled by the user interface thread, so when you call thread.sleep , you make the user interface thread thread, and you do not see any changes until the method completes.

Therefore, you need to start the process in a new thread, and when the process ends, make changes to the user interface using the dispatcher.

For instance:

 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 

Using MVVM, you will bind the button property IsEnabled to a boolean property in your viewModel and update the virtual machine instead of the button itself.

+2
source

Bind the button enabled property to a property in your virtual machine (e.g. ProcessComplete).

Use the onclick button to launch a method on your virtual machine that launches your lengthy process. Store ProcessComplete False while the process is running, and then set it to True at the end.

+1
source

Source: https://habr.com/ru/post/1379463/


All Articles