[RESOLVED] Thread Form and Owner
Hi
I have a little form that has to do a time consuming job, so the correct way to tell the user that the process it's running it's using a progress bar...
So i decided to create a new form with 2 labels and a progress bar, to show over the little window, that show a title the progress and the number of lines already processed.
Everything is working, except the Z order of this form, i can set the form to be top most, but this is not the correct way, so i think that the correct way it's setting the owner of this new form. The only problem it's the cross threaded exceptions.
How can i set the owner of the form without the exceptions?
Should i look to the CheckForIllegalCrossThreadCalls?
vb.net Code:
Dim maxVal As Integer = 30
Dim newForm As New FormProgress()
Dim newThr As New Threading.Thread(AddressOf newForm.ShowDialog)
newThr.Start()
newForm.SetMaxValue(maxVal) 'Set the progress var
newForm.SetTitle("Test title...")
For i As Integer = 1 To maxVal
Threading.Thread.Sleep(500)
newForm.SetCurrentValue(i)
Next
newForm.CloseForm()
Re: Thread Form and Owner
There can only be 1 UI thread per application. That's the only thread where you can use the methods and properties of forms and form controls. So it's the time-consuming job that belongs in a separate thread, not the progress bar. A convenient way to report progress from a processing thread is with a BackgroundWorker. Here's how you could code it:
Code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
newForm.Show(Me)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'here's your time consuming process
For i As Integer = 1 To maxValue
Threading.Thread.Sleep(500)
Dim progress As Integer = CInt(i * 100 / maxValue)
BackgroundWorker1.ReportProgress(progress)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'This event is raised on the UI thread when progress is reported:
newForm.SetCurrentValue(e.ProgressPercentage)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
newForm.Hide()
End Sub
Progress is reported as a percentage, so you may need to adjust your SetCurrentValue sub accordingly. Note that I use Show(Me) to show an Owned Form. This allows the user to continue doing things on the main form while the progress form is showing. You can't do that with ShowDialog.
BB
Re: Thread Form and Owner
Thanks boops boops
I know how to use the background worker and that's not what i intend to do...
The main form should remain blocked, it's a critical operation, so i need someway to tell the user that the operation it's running.
Like i wrote i can make the form the top most, but then it'll be over other applications and i don't like that.
The other option by setting the owner of the form, i can bypass the cross thread error, setting the CheckForIllegalCrossThreadCalls = False, before starting the new thread and then everything works fine... I just don't want to use this, but if there isn't any other way...
Thanks
Re: Thread Form and Owner
Re: Thread Form and Owner
Quote:
Originally Posted by
boops boops
There can only be 1 UI thread per application. That's the only thread where you can use the methods and properties of forms and form controls.
That's not strictly true. You can perform UI operations on any thread. The restriction is that you can only access the handle of a control on the thread that created it. We use the term "UI thread" to refer to the thread on which the application's startup form is created because that's the thread that almost all UI operations are performed. If you create a splash screen in your app though, it is created on a different thread, which is how it can be displayed while the startup form is still being created.
Re: Thread Form and Owner
Quote:
Originally Posted by
mickey_pt
Thanks boops boops
I know how to use the background worker and that's not what i intend to do...
The main form should remain blocked, it's a critical operation, so i need someway to tell the user that the operation it's running.
Like i wrote i can make the form the top most, but then it'll be over other applications and i don't like that.
The other option by setting the owner of the form, i can bypass the cross thread error, setting the CheckForIllegalCrossThreadCalls = False, before starting the new thread and then everything works fine... I just don't want to use this, but if there isn't any other way...
Thanks
A BackgroundWorker is perfect for this... if you use it properly. What you can do is design a form that has a ProgressBar on it and contains a BackgroundWorker. You then declare a constructor in that form that takes a DoWorkEventHandler delegate as an argument. In your main form, you write a DoWork event handler but without a Handles clause and when you create the progress form you pass a delegate for that method to it. That method is then used as the handler for the DoWork event of the BackgroundWorker in the progress form. E.g.
vb.net Code:
Imports System.Threading
Imports System.ComponentModel
Public Class MainForm
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Using dialogue As New WorkingDialogue(AddressOf BackgroundWorker_DoWork)
dialogue.ShowDialog()
End Using
MessageBox.Show("Task complete")
End Sub
Private Sub BackgroundWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
For i = 1 To 100
Thread.Sleep(100)
worker.ReportProgress(i)
Next
End Sub
End Class
vb.net Code:
Imports System.ComponentModel
Public Class WorkingDialogue
Public Sub New(doWorkHandler As DoWorkEventHandler)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
AddHandler Me.BackgroundWorker1.DoWork, doWorkHandler
End Sub
Private Sub WorkingDialogue_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Me.Close()
End Sub
End Class
Re: Thread Form and Owner
THanks JMC for yout time.
Always thinking outside the box... :)
But it have one problem, I can't make it to work, did you tested your code? It looks like it ignores the Sleep, or in my case the time consumption operation...
Sorry
Re: Thread Form and Owner
Yep, tested and passed. Is the DoWork event handler being executed? If not then you need to determine why not. If so then there's obviously something wrong with your code in that event handler. Post exactly what you're using.
Re: Thread Form and Owner
Hi
I just created a new project and write everything like in the sample code... The only difference was the form name and the proc name for the delegate...
Anyway:
vb.net Code:
Imports System.Threading
Imports System.ComponentModel
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Using frrrr As New Form2(AddressOf doWork)
frrrr.ShowDialog()
End Using
MsgBox("Work is done!", MsgBoxStyle.Information)
End Sub
Private Sub doWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
For i As Integer = 1 To 100
Thread.Sleep(1000)
worker.ReportProgress(i)
Next
End Sub
End Class
vb.net Code:
Imports System.ComponentModel
Public Class Form2
Public Sub New(doWork As DoWorkEventHandler)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
AddHandler Me.BackgroundWorker1.DoWork, doWork
End Sub
Private Sub Form2_Load(sender As Object, e As System.EventArgs) Handles Me.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Me.Close()
End Sub
End Class
Re: Thread Form and Owner
Oh...
Forgot the ReportProgress property... Change it to True, and now it works! :/
THanks