Hi Sam, Armin.
My opinion is that both approaches for initializing the form data -- in
a sepparate thread or in the Activate event -- have pros and cons.
I usually used the Activate event, but in VB.Net I noticed (at least in
my machine) that sometimes the Form is yet to show completely when the
Activate event fires, requiring a non-wanted call to DoEvents (which I
usually have gripes with).
Because I'm just discovering .Net, I tend to use some idioms that are
still remanescent of VB classic. That is the case of the initialization
done in the Activate event, or the use of Show in the Load event. I
recently discovered that in Net 2.0 I'd better use the Shown event,
which is fired only once, when the Form is first shown... ;-)
Anyway, my more recent approach has been to perform initialization in a
separate thread. Of course, this adds a lot of issues regarding
concurrency and UI updating, but I have a few rules of thumb:
a) Have some AsyncXXX methods at Form level that perform a group of UI
related tasks, so when they're called from outside the UI thread they
return as soon as possible after having done the most work possible;
practical examples of this are methods for updating the progress bar
(AsyncUpdateProgress), reporting connection with the DB
(AsyncShowConnection), and the like.
b) If the volume of data that would be put in the UI (grid data,
listbox data, etc) is somewhat heavy, I gather everything in specific
structures and just update the UI when the worker thread returns
(which, on my setup, raises an event on the UI thread, because I
usually use a BackgroundWorker or something like it).
Notice that I didn't have yet necessity to do extensive UI updates
while performing asynchronous work, so there may be situations where
I'll have to consider the trade offs, but my usual experience has been
that a paralel worker thread does wonders to the responsiveness of the
UI.
That being said, I'd like to make a few suggestions to Sam's approach:
Because you know that you'll have a modal Progress form that will be
shown while background operations occur, it's a good idea to isolate
the asynchronous work in a way that *the Progress form* launches it
while entering modal. One possibility could be a AsyncWork class, whose
Work method is called by a ProgressForm.DoWork method, as bellow:
'In a Progress form
Private WithEvents Worker As AsyncWork
Sub DoWork(Work As AsyncWork)
Worker = Work
If Worker IsNot Nothing then
Worker.Work '<- Starts asynchronally
ShowDialog '<- shows modally
End If
End Sub
Sub Progress_FormClosing(...) Handles Me.FormClosing
If Worker.IsBusy Then
'Asks if the user wants to cancel
E.Cancel = True
Worker.PauseWork
If MsgBox("Stop this work?", _
MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
Worker.Cancel = True
End if
Worker.ContinueWork
End If
End Sub
Sub Worker_Progress(...) Handles Worker.Progress
'Updates the progress
If InvokeRequired Then
'Calls itself with BeginInvoke
Else
'...init, update or otherwise finish the progress
End if
End Sub
Sub Worker_Finished(...) Handles Worker.Finished
'Finishes the work
If InvokeRequired Then
'Calls itself with BeginInvoke
Else
'Running in the UI thread. Close the form
DialogResult = 'SomeValue
Close
End if
End Sub
Likewise, just as you know that you will have some heavy concurrent
work, it pays to isolate this in a class with a specific protocol,
namely: a cancel flag; progress events; a finished event, a Busy flag,
and so on. One approach would be to consider the BackgroundWorker
object from Net 2.0. Since it seems you preffer (or have) to keep your
current pre-Net 2.0 setup, it may be advisable that you cook one
background worker yourself...
Imports SysThread = System.Threading
Class MustInheirt AsyncWork
Private mCancel As Boolean
Private mBusy As Boolean
Private mLock As New Object
Private mResult As Boolean
Public Event Progress(...)
Public Event Finished(...)
Protected MustOverride Function DoWork As Boolean
Public ReadOnly Property IsBusy As Boolean
Get: Return mBusy: End Get
End Property
Public ReadOnly Property Result as Boolean
Get: Return mResult: End Get
End Property
Public Property Cancel As Boolean
Get
Return mCancel
End Get
Set(ByVal Value As Boolean)
mCancel = Value
End Set
End Property
Public Sub PauseWork
SysThread.Monitor.Enter(mLock)
End Sub
Public Sub ContinueWork
SysThread.Monitor.Exit(mLock)
End Sub
Protected Function CancelWork As Boolean
'Must be called periodically from descendants
PauseWork
ContinueWork
Return mCancel
End Sub
Public Sub Work
Dim T As New SysThread.Thread(AddressOf LaunchWork)
mBusy = True
mCancel = False
mResult = False
T.Start
End Sub
Private Sub LaunchWork
Try
mResult = DoWork
Finally
mBusy = False
RaiseEvent Finished(...)
End Try
End Sub
Protected Sub OnProgress(...)
'Called by descendants to report progress
RaiseEvent Progress(...)
End Sub
End Class
Then, inside your form, you might have:
Private Class InitWork
Inherits AsyncWork
Protected Overrides Function DoWork As Boolean
Dim Result As Boolean
OnProgress(...) 'Initializes the progress
For X = 1 to AVeryLargeValue
If CancelWork Then
Exit For
End if
'Do some work
'...
OnProgress(...) 'show some progress
Next
OnProgress(...) 'Finish the progress
Return Result
End Function
End Class
Private Sub Form1_Load(...) Handles Me.Load
Show() ':-D
Dim Progress As New ProgressForm
ProgressForm.DoWork(New InitWork)
End Sub
HTH.
Regards,
Branco.
P.S: As allways, this is just AirCode. Your results may vary... ;-)