I am trying to create a Progress form for my program that loads in a seperate thread so that screen updates and animations on the form are smooth and timely. I have had some success with the following code.
The Form Used as a "Template" for the threaded progress form:
Public
Class frmDebug
Private m_Thread As Threading.Thread
Private m_CloseByCode As Boolean
Public Shadows Sub AsyncShow()
m_Thread = New Threading.Thread(AddressOf ThreadMain)
m_Thread.Start()
End Sub
Private Sub ThreadMain()
UctlProgressSpinner1.Style = 1
UctlProgressSpinner1.Active = True
Application.Run(Me)
End Sub
Public Shadows Sub AsyncClose()
If Me.InvokeRequired Then
Me.BeginInvoke(New MethodInvoker(AddressOf AsyncClose))
Else
m_CloseByCode = True
MyBase.Close()
m_CloseByCode = FalseEnd If
End Sub
Private Sub txtStatus_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
Application.DoEvents()
End Sub
Private Delegate Sub ShowProgressDelegate( ByVal myText As String)
Public Sub AsyncShowProgress(ByVal myText As String)
If Me.InvokeRequired Then
Me.BeginInvoke( New ShowProgressDelegate(AddressOf AsyncShowProgress), _ New Object() {myText} )
Else
If myText = "done" Then
UctlProgressSpinner1.Active = False
Button1.Visible = TrueElse
txtStatus.AppendText(myText)
End If
End If
End Sub
Protected Overrides Sub OnClosing(ByVal e As System.ComponentModel.CancelEventArgs)
MyBase.OnClosing(e)
e.Cancel = Not m_CloseByCode
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Me.AsyncClose()
End Sub
End Class
The Code from my class that shows the above form
Public Sub CreateUser()
frmMyDebugThread = New frmDebug
frmMyDebugThread.AsyncShow()
AddHandler Me.Progress, AddressOf DebugProgress
RaiseEvent Progress("Starting User Creation" & vbNewLine)
End Sub
Public Sub DeleteUser()
frmMyDebugThread = New frmDebug
frmMyDebugThread.AsyncShow()
AddHandler Me.Progress, AddressOf DebugProgress
RaiseEvent Progress("Deleting User" & vbNewLine)
End Sub
The Code from a Module which contains the object declarations and event handler sub routine
Public frmMyDebugThread As New frmDebugPublic Sub DebugProgress(ByVal myText As String)
frmMyDebugThread.AsyncShowProgress(myText)
End Sub
Everything works great the first time the form appears when called from the CreateUser() method however if I then try to run the DeleteUser() Method I get the following error message:
Cross-thread operation not valid: Control 'txtStatus' accessed from a thread other than the thread it was created on.
This error occurs on the following line of code from frmDebug.
Application.Run(
Me)HELP! ME! If anyone has some thoughts it would be appreciated
Patrick

Seperate Thread progress form
elsonidoq
Create a class and name it something like ThreadSafeUpdater
Define a property TextBox
Define a property Value
Define a WrokerMethodDelegate for a WorkerMethod
Create a constructor that accepts TextBox and Value // optional, just makes thing easier
Create a method1 then will call BeginInvoke (read more on Invoker) on the WorkerMethod
Define WorkerMethod that will update the TextBox
In your async process, everytime you will need to update the textbox. You define an instance of the above class and pass the textbox object and the new value to it. Then call method1().
If you ever need some other parameter just add it as a property.
BTW, Application.Run(Me) should this be Me.ShowDialog()
VijayG
Hi Peter,
Well I have had some success with the background worker but had to move some DIM statements out of my class and then pass them to the class from the form to get it to work. Hopefully this will have no adverse effects, only testing will tell. For those of you watching this thread this is what frmDebug now looks like.
Public
Class frmDebugPrivate Sub txtStatus_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
End Sub
Private Delegate Sub ShowProgressDelegate( ByVal myText As String)
Public Sub ShowProgress(ByVal myText As String)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
End Sub
Private Sub frmDebug_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
End Class
The Button_Click event on my main form is as follows
Private
Sub cmdCreateRemoveAccount_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdCreateRemoveAccount.Click frmMyDebug = New frmDebugSelect Case cmdCreateRemoveAccount.Text
End Sub
From the Backgroundworker dowork handler I do this
Private Sub bwTasksCreate_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwTasksCreate.DoWorkmyTasksUser.Create(frmMyDebug)
End Sub
My Class method is defined as follows and the frmDebug method so that you can update the txtStatus safely
Public Sub Create(ByRef frmMyDebug As frmDebug)
end sub
Lewis Lynn
hi,
Is your problem solved
Thank you,
Bhanu.
just*Do*It
Ok then I guess you could give me some help with doing it the right way, let me try to explain the app.
1. A button on a form is clicked
2. The button executes the .CreateUser() or .DeleteUser() methods of a class I have created which has an instance that was created in the form.load() event
3. These methods have parameters both required and optional that are passed
Can this class method be launched as a background worker process and if so do you have some sample code.
Can this class method in a background worker thread access the frmDebug class , (with some changes) but I would suspect I would still have to use the AsyncShowProgress method to update the txtStatus on the form
Some clean sample code would really help as I have tried some many different things that I think maybe confused as to what I need to do it the right way.
Serega
Basically, it's a fluke that it works at all. And, with the randomization features in Vista, my guess is it will completely fail if you try to run it in Vista. It's like allocating x bytes of memory, initializing them to values you need, freeing the memory, then accessing the memory depending on those values still being there. Sometimes they'll still be there, sometimes they won't; it would depend on alot of things outside of your control.
I'd like to give you a better outlook on this; but, Windows just doesn't support this sort of thing, although it lets you try and do it.
SpoBo
While I now understand alot more about Threading in Visual Basic and I understand the proper way of doing it now, I am way to far in to change what I have done and since the code I have works, all be it only the first time, I really would like to find why it won't work the second time through.
MShanahan
Windows only supports one GUI thread. A user can only deal with one form at a time, there's really no reason to put form on a background thread.
You should create a background worker thread that communicates it's progress back to the form. This is best done with the BackgroundWorker class. If you use the Thread class you have to make sure changes to the form are done in the GUI thread, which can be check with Control.InvokeRequired. Usually, a setter method is provided to modify form data which checks the InvokeRequired property and calls BeginInvoke where needed.