I need to write a VB.Net 2008 applet to go through all the fixed-drives looking for some files. If I put the code in ButtonClick(), the UI freezes until the code is done:
The key is to seperate the UI code from the actual functionality code. The time-consuming functionality should run on a seperate thread. To achieve this, you can either:
Thread
object by
yourself Delegate
and use
asynchronous invokation (using
BeginInvoke
).BackgroundWorker
.As you mentioned, you should avoid Application.DoEvents()
. A proper breakdown of the application's functionality will allow you to create an application which is designed to be responsive, rather than creating a non-responsive application with DoEvents "fixes" (which is costly, considered bad practice, and implies a bad design).
Since your method doesn't return a value and doesn't update the UI, the fastest solution might be creating a Delegate and using "fire and forget" asynchronous invokation:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Call New Action(AddressOf DrivesIteration).BeginInvoke(Nothing, Nothing)
End Sub
Private Sub DrivesIteration()
Dim drive As DriveInfo
Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
Dim filepath As String
For Each drive In DriveInfo.GetDrives()
If drive.DriveType = DriveType.Fixed Then
filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
For Each filepath In filelist
DoStuff(...)
Next
End If
Next
End Sub
BTW, For..Next blocks no longer have to end with "Next (something)", it is obsolete - VB now infers the (something) by itself, so there is no need to state it explicitly.
A. put up a PROGRESS BAR... update it and .REFRESH it ... If all you want is to show that your not dead.
B. DoEvents is evil sounds A LOT like "NEVER USE A GOTO..." pleeeeze pleeeze pleeeze there are times and circumstances where any language's syntax can be harmful AND helpful. Why jump through a million hoops just to essentially do "A" above?
<soapbox>
If you know that something takes a LONG TIME and you also know that no other operations can take place WHILE YOUR WAITING (i.e. it is essentially a serial process) than if you do ANYTHING like that and push it into "the background" then you'll be sprinkling "ITS_OK_TO_CONTINUE" booleans all through the rest of your code just waiting for the file process to end anyway.... whats the point of that? All you've done is complicate your code for the sake of... hmm... "good programming?" Not in my book.
Who cares if DoEvents is "left over" from the ICE AGE. Its EXACTLY the right thing in MANY circumstances. For example: The framework gives you ProgressBar.Refresh but you'll see that its not exactly "working" unless you post-pend a few DoEvents after it.
</soapbox>
C. A background task is just that -- background; and you generally use it to operate on NON-SERIAL tasks or at least asynchronous tasks that MAY or MAY NOT update the foreground at some point. But I'd argue that anytime a so-called background task HALTS the foreground then it is (almost) by definition --- a FOREGROUND task; regardless of HOW LONG it takes.
Put the process into a separate thread.... ...using the BackgroundWorker component.
Disable UI components that should not be usable while the process workd.
Finished - the UI will still be responsive.
The BackgroundWorker is a good way to solve your problem. Actually the documentation states this:
The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.