The way I would handle something like this would be to put all the doFairSavings() functionality on a background thread. I don't think I would make the calling form invisible either. Instead I would put some progress information, or a "busy" animation on it. Then, when doFairSavings() is complete, I would have it call back into the UI thread using a delegate and me.invoke() so I could update any form elements I needed to without worrying about cross thread errors. Then I would start whatever forms need to be shown.
I'm not sure what you may know about this subject, so I'm going go over everything you will need to know to understand the example I'm going to give (at the bottom).
Ok, a little about threads.
You should think of them as a separate program that is connected to yours only by shared memory. This means that public variables you declare will be accessible to any threads you create, but accessing these variables by multiple threads can be tricky. Multiple threads writing to a variable can cause the data contained in those variables to become corrupt, or in some cases cause numeric variables to contain values you didn't want, and didn't expect. So when reading to or writing to variables from multiple threads, you should wrap those calls in a synclock statement.
When you create a thread in vb.net, you assign the thread a Sub or Function to live in, like this:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim thisNewThread As New Threading.Thread(AddressOf BackgroundThread)
thisNewThread .Start()
End Sub
Private Sub BackgroundThread()
End Sub
In the code above, when you click on Button1 a new thread will be created and begin to run in the Sub BackgroundThread(). The thread has access to any variables in that Sub's scope - meaning if BackgroundThread() is in a form you'd created, then it would have access to any functions, private or public variables and objects declared in that form, or public variables and objects declared in your modules.
One rule of thumb for your created threads is that they can not access controls on your form. Controls on your form were created by the UI thread, and only the UI thread can read from or write to those controls. If you try to change the text of your form's textbox from the new thread, a cross thread violation exeption will (probably) be thrown in your application.
We get around this by using DELEGATES, .InvokeRequired() and .Invoke().
A delegate is a way for you to save the address of a Sub or function and pass it to a function that lives elsewhere. Then the method that has the delegate can use it, and program execution returns to the saved function - even if the code that's using the delegate is within another class or object.
It works like this:
Public Class Form1
Private Delegate Sub OurPrototype(ByVal message As String)
Private theCallback As OurPrototype
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
theCallback = AddressOf Callback
theCallback("hello")
End Sub
Private Sub Callback(ByVal message As String)
MsgBox(message)
End Sub
End Class
This is all the code from a new form in a new project. At the top, we've created a delegate prototype. It's the signature, or format of the sub or function that will be contained in delegates created using this prototype. You'll see that it matches the Sub that we assigned to our delegate in the button click event. If you were to click the button, a message box would pop up containing the text "hello".
I know it doesn't seem very useful right now, but you could pass that delegate as a parameter to an object you've created from one of your classes, and when the delegate is called from the depths of that class, program execution would return to that function, and you'd see the message box containing the text "hello".
You can also use it to change threads using .Invoke() - and that's really where the magic is.
Most controls and all forms contain the .Invoke() and .InvokeRequired() functions. .InvokeRequired() returns a boolean value indicating weather or not your current thread is the UI thread. If it's not, and you need to be on the UI thread, you use .Invoke(), like this:
Private Sub Callback(ByVal message As String)
If Me.InvokeRequired Then
Me.Invoke(theCallback, message)
Else
Me.Text = message
End If
End Sub
Here's our callback function again, only this time if it's called by our new thread, me.InvokeRequired() will be True. If that's the case, then the callback sub will be run AGAIN, but this time it will be on the UI thread. The second time through, .InvokeRequired() will be False, and the code in the else portion of that if statement will run - setting the text on your form to whatever's in the message string (in our case it will be "hello").
Ok, that's the background stuff you should know to understand this example code... Here it is:
Private Delegate Sub OurPrototype(ByVal message As String)
Private theCallback As OurPrototype
Private Sub frmOWTMain_KeyUp(ByVal sender As Object, ByVal e As
System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
If e.KeyCode = Keys.Enter Then
Me.Hide()
If Me.cbFare.Checked Then
theCallback = AddressOf Callback
Dim bgThread as New Thread(AddressOf ourThread)
bgThread.Start()
End If
End If
End Sub
Private Sub ourThread()
theCallback("you can incluse information in this string about what form to show here")
End Sub
Private Sub Callback(ByVal message As String)
If Me.InvokeRequired Then
Me.Invoke(theCallback, message)
Else
Me.cbFare.Checked = false
If message = "whateverForm" Then whateverForm.show()
Me.Show()
End If
End Sub
Doing it like this, your form will not show until all the work is done.
By the way - I hope you realize that this is nowhere NEAR all the information on threading and delegates. It's just the basics. If you want to know more, try Google or MSDN. There's a lot more to know about both.
-Pete