0

In my application, I call a process to update software - which is stored within its own class. Even thou I have been writing Application.DoEvents() in few places for some reason the label in the update form is not updating and the form itself is inactive.

Namespace software

Public Class updater 

    Public Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Boolean 
        Application.DoEvents()
        Thread.Sleep(1000)

        frmUpdate.lblResult.Text = "Update is about to begin"
        Thread.Sleep(1000)
        frmUpdate.lblResult.Text = "Downloading data"
        Thread.Sleep(1000)

        Application.DoEvents()

        frmUpdate.lblResult.Text = "About to start the writing process" 
        Application.DoEvents()

        frmUpdate.lblResult.Text = "Software was updated, please restart your device."
    End Function

End Class

End Namespace
Vlad Vladimir Hercules
  • 1,781
  • 2
  • 20
  • 37

1 Answers1

6

I can't figure out why you were calling DoEvents in those specific locations, since none of them will have any visible effect where they are. The first one happens before any labels are changed, so allowing the form to refresh there is pointless. The others are at the very end, after all the long-running work is already done (the three sleeps). So, while they will allow the form to refresh before execution leaves the method, it will very very shortly be leaving the method anyway, so there's no point in doing it there either. The only place where it would even be applicable to call DoEvents would be between two long running things. For instance, if you did this, you'd notice a difference:

Public Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Boolean 
    Thread.Sleep(1000)
    frmUpdate.lblResult.Text = "Update is about to begin"
    Application.DoEvents()

    Thread.Sleep(1000)
    frmUpdate.lblResult.Text = "Downloading data"
    Application.DoEvents()

    Thread.Sleep(1000)
    frmUpdate.lblResult.Text = "About to start the writing process" 
    frmUpdate.lblResult.Text = "Software was updated, please restart your device."
End Function

You need to understand, in .NET WinForms (as well as in WPF), the UI is running on a single thread. What I mean by that is, if one of your event handlers contains code that takes a long time to complete, the UI will be frozen for the entire time that event handler is executing. The UI refreshing is completely blocked until the last event handler finishes doing whatever it was doing. DoEvents is somewhat of a hack way of getting around that (and a dangerous hack, at that). Each time you call DoEvents, it returns control back to the form to handle whatever else it has queued up to do (such as repainting the screen and handling user input) and then execution is returned to the original event handler so it can continue where it left off. That means, each time you call DoEvents, it allows the form to repaint at that point, but the event handler still blocks the UI in between all of the DoEvents.

As others have already implied, using DoEvents is highly discouraged. Not only is it less effective, it can cause all sorts of unexpected behavior. In pre-.NET versions of VB (e.g. VB6), DoEvents was often the only option, but in .NET, multi-threading is relatively easy. There may be occasions where DoEvents is legitimately still useful, but they should be very few and far between and only implemented with great care and caution.

There are two recommended ways of implementing multi-threading in WinForm applications. The original method, which still works well, is to use a BackgroundWorker component (you'll find it in the WinForm designer tool-box). The BackgroundWorker raises an event on a different thread so that you can do all your long-running work in that event handler, without it blocking the UI. Then, when it's all done, it raises another event, back on the UI thread so that you can update the UI after the work is complete.

The newer method, which is much cleaner and easier to read, but a little more sophisticated, is to use the Async and Await keywords to make all your long-running methods, and your event handler that calls them, asynchronous.

Steven Doggart
  • 43,358
  • 8
  • 68
  • 105