This is something that I've been meaning to do for some time but never got around to previously. Earlier today - and not for the first time - I saw someone trying to display a modal wait dialogue while they did some work and going about it in very much the wrong way.
This demo provides a dialogue that you can display modally over another form while that form does some background work. You basically write a normal DoWork event handler for a BackgroundWorker, as well as optional ProgresssChanged and RunWorkerCompleted event handlers, and pass them to the dialogue for it to use as handlers for the events of its own BackgroundWorker. That means that your form creates and displays the dialogue, the dialogue then kicks off a BackgroundWorker and it invokes methods in your form to do the work. That way, this same dialogue can be used for any work you like without change.
The dialogue displays a Marquee ProgressBar by default and no Cancel button. If you provide a handler for the ProgressChanged event, it will display a Continuous ProgressBar so the actual progress can be displayed. Note that the Maximum of that ProgressBar is 100 so you must provide a genuine percentage when you call ReportProgress. If you set SupportsCancellation to True, the dialogue will also display a Cancel button. As always, it's up to you to process the cancellation request in your DoWork event handler.
I have replaced the original attachment with one that includes a few improvements, as well as a C# version of the project. This version was created in VS 2019 and targets .NET Framework 4.8. If you're using an earlier version, you should still just be able to add the forms as existing items to a project of your own, as long as you're not using too-old a version.
See post #9 for simple instructions on how to add this dialogue form to your existing project.
Last edited by jmcilhinney; Aug 14th, 2020 at 03:26 AM.
Reason: Updated attachment and provided links to simple instructions for beginners.
Actually, there's one small mistake in the original code I posted. The BackgroundWorker1_ProgressChanged method should not have a Handles clause on it. It won't stop things working but it means that that method will be executed twice when progress is enable.
Thanks for the example, I have one more request that I think would help me understand the full circle. If I wanted to add a label in the BackgroundWorkerForm and update the label from Form1 during the background work to add a label showing textual work along with the progress bar. I tried a few things using invoke and begininvoke but have not had any luck. Care to expand? Thank you!
Thanks for the example, I have one more request that I think would help me understand the full circle. If I wanted to add a label in the BackgroundWorkerForm and update the label from Form1 during the background work to add a label showing textual work along with the progress bar. I tried a few things using invoke and begininvoke but have not had any luck. Care to expand? Thank you!
In the ProgressChanged event handler, the sender parameter is a reference to the wait dialogue. You can use that to access any members of that dialogue you like. If you want to be able to update a Label on that dialogue form then add a method that does that to the class and call it in that event handler.
Adding BackgroundWorkerForm to an Existing Project
If you have an existing project that you would like to add this wait dialogue to but you're not sure how, you can follow the instructions below.
1. Open the Solution Explorer, right-click your project and select Add -> Existing Item.
2. Navigate to the project folder for the demo project and select the BackgroundWorkerForm.vb file. The BackgroundWorkerForm type should be added to your project.
3. Open your own form and add the following field declaration to it, importing the System.ComponentModel namespace if required:
vb.net Code:
Private WithEvents BackgroundWorkerForm As BackgroundWorker
4. Use the navigation bar at the top of the code window to create the required event handlers.
4a. Select BackgroundWorkerForm in the middle drop-down and then select DoWork in the right-hand drop-down.
4b. Repeat the step above for ProgressChanged and RunWorkerCompleted if required.
5. Delete the field declaration that you added in step 3.
6. Delete the Handles clauses from the event handlers you created in step 4.
7. Add the appropriate code to each of the event handlers just as you would for any BackgroundWorker. If you intend for the dialogue's ProgressBar to display actual progress, your calls to ReportProgress must pass values in the range 0 to 100.
8. Where you want to display the dialogue and perform the work in your own code, create an instance of the BackgroundWorkerForm with the appropriate event handlers and show it, e.g.
vb.net Code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Using waitDialogue As New BackgroundWorkerForm(AddressOf BackgroundWorkerForm_DoWork)
waitDialogue.ShowDialog()
End Using
End Sub
9. If you would like to be able to cancel the work before it completes, set the SupportsCancellation property to True, e.g.
vb.net Code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Using waitDialogue As New BackgroundWorkerForm(AddressOf BackgroundWorkerForm_DoWork,
AddressOf BackgroundWorkerForm_ProgressChanged,
AddressOf BackgroundWorkerForm_RunWorkerCompleted) With {.SupportsCancellation = True}
waitDialogue.ShowDialog()
End Using
End Sub
Last edited by jmcilhinney; Nov 25th, 2020 at 12:36 AM.
Re: Adding BackgroundWorkerForm to an Existing Project
Hello jmcilhinney, very useful code, thank you very much.
I tried it in combination of OpenFileDialog() but seems not working, there is an "Exception thrown: 'System.Threading.ThreadStateException' in System.Windows.Forms.dll" do you know how to resolve it ?
Re: Adding BackgroundWorkerForm to an Existing Project
Originally Posted by t0ret0s
Hello jmcilhinney, very useful code, thank you very much.
I tried it in combination of OpenFileDialog() but seems not working, there is an "Exception thrown: 'System.Threading.ThreadStateException' in System.Windows.Forms.dll" do you know how to resolve it ?
If it didn't work then you did it wrong. I don't know what you did so I have no idea what you did wrong. Did someone tell you I was psychic?
That said, why would you try it with an OpenFileDialog in the first place? The whole point of this is to give the user something to look at and provide feedback while work is performed in the background. How exactly is an OpenFileDialog background work?
Re: Adding BackgroundWorkerForm to an Existing Project
Originally Posted by jmcilhinney
If it didn't work then you did it wrong. I don't know what you did so I have no idea what you did wrong. Did someone tell you I was psychic?
That said, why would you try it with an OpenFileDialog in the first place? The whole point of this is to give the user something to look at and provide feedback while work is performed in the background. How exactly is an OpenFileDialog background work?
The idea is to check some files into the computer, in case the exact file is missing, I want to let user select it.
This is the first approach code :
Code:
Dim worker = DirectCast(sender, BackgroundWorker)
'Simulate some time-consuming work.
For i = 0 To 100
If worker.CancellationPending Then
e.Cancel = True
Exit For
End If
If reportProgressCheckBox.Checked Then
worker.ReportProgress(i)
End If
If i = 50 Then
If MsgBox("Do you want to select it manually ?", CType(vbYesNo + vbQuestion, Global.Microsoft.VisualBasic.MsgBoxStyle), "test test test test") = vbYes Then
Dim myStream As Stream = Nothing
Dim openFileDialog1 As New OpenFileDialog()
openFileDialog1.Title = "File " & "2_333333"
openFileDialog1.InitialDirectory = "C:\"
openFileDialog1.Filter = "pdf files (*.pdf)|*.pdf|All files (*.*)|*.*|" & ".Test" & "|*" & ".Test"
openFileDialog1.FilterIndex = 2
openFileDialog1.RestoreDirectory = True
openFileDialog1.ShowDialog() 'here the error is generated
If openFileDialog1.ShowDialog() = DialogResult.OK Then
Try
myStream = openFileDialog1.OpenFile()
If (myStream IsNot Nothing) Then
myStream.Close()
MsgBox(openFileDialog1.FileName)
End If
Catch Ex As Exception
MessageBox.Show("Cannot read file from disk. Original error: " & Ex.Message)
Finally
If (myStream IsNot Nothing) Then
myStream.Close()
End If
End Try
End If
End If
End If
Thread.Sleep(100)
Next
Another thing : if I use modal wait, is not possible to debug in case of error or exception, is it correct ?
Re: Adding BackgroundWorkerForm to an Existing Project
Originally Posted by t0ret0s
The idea is to check some files into the computer, in case the exact file is missing, I want to let user select it.
This is the first approach code :
Code:
Dim worker = DirectCast(sender, BackgroundWorker)
'Simulate some time-consuming work.
For i = 0 To 100
If worker.CancellationPending Then
e.Cancel = True
Exit For
End If
If reportProgressCheckBox.Checked Then
worker.ReportProgress(i)
End If
If i = 50 Then
If MsgBox("Do you want to select it manually ?", CType(vbYesNo + vbQuestion, Global.Microsoft.VisualBasic.MsgBoxStyle), "test test test test") = vbYes Then
Dim myStream As Stream = Nothing
Dim openFileDialog1 As New OpenFileDialog()
openFileDialog1.Title = "File " & "2_333333"
openFileDialog1.InitialDirectory = "C:\"
openFileDialog1.Filter = "pdf files (*.pdf)|*.pdf|All files (*.*)|*.*|" & ".Test" & "|*" & ".Test"
openFileDialog1.FilterIndex = 2
openFileDialog1.RestoreDirectory = True
openFileDialog1.ShowDialog() 'here the error is generated
If openFileDialog1.ShowDialog() = DialogResult.OK Then
Try
myStream = openFileDialog1.OpenFile()
If (myStream IsNot Nothing) Then
myStream.Close()
MsgBox(openFileDialog1.FileName)
End If
Catch Ex As Exception
MessageBox.Show("Cannot read file from disk. Original error: " & Ex.Message)
Finally
If (myStream IsNot Nothing) Then
myStream.Close()
End If
End Try
End If
End If
End If
Thread.Sleep(100)
Next
Another thing : if I use modal wait, is not possible to debug in case of error or exception, is it correct ?
That makes no sense at all. Like I said, the whole point of this is to give the user something to look at while the application is doing work in the background and they can't use the UI. If you expect them select a file using a dialogue then that is the very definition of using the UI so why are you even trying to use this dialogue or do anything in the background? There's no reason for you to be using this dialogue so don't. That is the last I will say on the matter.