sidwelle
asked on
UI controls don't update via Delegate
Please tell me what was wrong with the following snip, Thanks
Private Delegate Sub StatusDelegate(ByVal txt As String)
Public Function Status(ByVal txt As String) As Integer
'If Me.InvokeRequired Then
If Me.lblStatus.InvokeRequired Then
'Me.lblStatus.Invoke(New StatusDelegate(AddressOf Status))
'Status`.Invoke(New SetText(AddressOf SetLabelText), Text)
Me.Invoke(New StatusDelegate(AddressOf Status), Text)
Else
Me.lblStatus.Text = txt
'Me.lblStatus.Text = "test ..."
'Me.Text = "test ..."
Me.lblStatus.Update()
Application.DoEvents()
End If
End Function
ASKER
I made those changes, but I still don't get an update.
I can trace the call in to the 'Status' function and see the value of what was passed and assigne to the text property, but the never does update.
One Time I dragged the exection into the Invoke side of the If statment just to see the Invoke work, but it throws an error: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
Whats with that ?
Is it not call the main thread ?
I can trace the call in to the 'Status' function and see the value of what was passed and assigne to the text property, but the never does update.
One Time I dragged the exection into the Invoke side of the If statment just to see the Invoke work, but it throws an error: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
Whats with that ?
Is it not call the main thread ?
From where are you calling this function from? If you want to see the Invoke work it should be called from a different thread. Please show the code where you are calling it from.
ASKER
its just a simple thread call:
Dim listener As New Thread(AddressOf main)
listener.Start(port)
Dim listener As New Thread(AddressOf main)
listener.Start(port)
ASKER
If I set a breakpoint in the Status function, can hover over lblStatus.text and it show the value as assigned.
But the control does not paint !?
But the control does not paint !?
If the project is not to big and if you are allowed to zip up the complete project into a zip file and post to the web such as SkyDrive or some other location so that I can download it and try it.
If that is not possible please put together a small test project which show the issue you are having. Then post to the web. Do not post to EE because it will not allow some of the files types in the project.
If that is not possible please put together a small test project which show the issue you are having. Then post to the web. Do not post to EE because it will not allow some of the files types in the project.
I tried the below code and it is working as expected, DoEvents is not at all required:
Imports System.Threading
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim t As Thread
t = New Thread(AddressOf ThreadTask)
t.IsBackground = True
t.Start()
End Sub
Private Delegate Function StatusDelegate(ByVal txt As String) As Integer
Public Function Status(ByVal txt As String) As Integer
'If Me.InvokeRequired Then
If Me.lblStatus.InvokeRequired Then
'Me.lblStatus.Invoke(New StatusDelegate(AddressOf Status))
'Status`.Invoke(New SetText(AddressOf SetLabelText), Text)
Me.Invoke(New StatusDelegate(AddressOf Status), txt)
Else
Me.lblStatus.Text = txt
'Me.lblStatus.Text = "test ..."
'Me.Text = "test ..."
End If
Return 0
End Function
Private Sub ThreadTask()
Dim stp As Integer
While True
stp = stp + 1
Thread.Sleep(1000)
Status(stp.ToString())
End While
End Sub
End Class
ASKER
Here is a simple app that does not see to work.
Where is it?
ASKER
Try Again
ASKER
3rd Try
ASKER
Its not going to let me upload, so I just pasted it. Looks alot like yours.
Imports System.Threading
Public Class Form1
'Private WithEvents MyClass1 As New SomeClass
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'MyClass1.Starter()
'Starter()
Dim t As New Thread(AddressOf Main)
t.IsBackground = True
t.Start()
End Sub
Private Delegate Function StatusDelegate(ByVal txt As String) As Integer
Public Function Status(ByVal txt As String) As Integer
'If Me.InvokeRequired Then
If Me.Label1.InvokeRequired Then
'Me.lblStatus.Invoke(New StatusDelegate(AddressOf Status))
'Status`.Invoke(New SetText(AddressOf SetLabelText), Text)
Me.Invoke(New StatusDelegate(AddressOf Status), txt)
Else
Me.Label1.Text = txt
Me.Text = txt
Me.Label1.Update()
'Application.DoEvents()
'MsgBox(txt)
End If
End Function
Public Function Starter() As Integer
'Dim t As New Thread(AddressOf Main)
't.IsBackground = True
't.Start()
End Function
Public Shared Function Main() As Integer
Form1.Status("This is a test ...")
End Function
End Class
Public Class SomeClass
'Public Function Starter() As Integer
' Dim t As New Thread(AddressOf Main)
' t.IsBackground = True
' t.Start()
'End Function
'Public Shared Function Main() As Integer
' Form1.Status("This is a test ...")
'End Function
End Class
Imports System.Threading
Public Class Form1
'Private WithEvents MyClass1 As New SomeClass
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'MyClass1.Starter()
'Starter()
Dim t As New Thread(AddressOf Main)
t.IsBackground = True
t.Start()
End Sub
Private Delegate Function StatusDelegate(ByVal txt As String) As Integer
Public Function Status(ByVal txt As String) As Integer
'If Me.InvokeRequired Then
If Me.Label1.InvokeRequired Then
'Me.lblStatus.Invoke(New StatusDelegate(AddressOf Status))
'Status`.Invoke(New SetText(AddressOf SetLabelText), Text)
Me.Invoke(New StatusDelegate(AddressOf Status), txt)
Else
Me.Label1.Text = txt
Me.Text = txt
Me.Label1.Update()
'Application.DoEvents()
'MsgBox(txt)
End If
End Function
Public Function Starter() As Integer
'Dim t As New Thread(AddressOf Main)
't.IsBackground = True
't.Start()
End Function
Public Shared Function Main() As Integer
Form1.Status("This is a test ...")
End Function
End Class
Public Class SomeClass
'Public Function Starter() As Integer
' Dim t As New Thread(AddressOf Main)
' t.IsBackground = True
' t.Start()
'End Function
'Public Shared Function Main() As Integer
' Form1.Status("This is a test ...")
'End Function
End Class
Are you trying to post to EE?
As I stated in a previous post, "Do not post to EE because it will not allow some of the files types in the project.", That is Why I suggested to use A SkyDrive account which comes with windows live account which cost nothing to have. Or you could use some other website.
ASKER
If you can suggest a specific site, I will load it there.
Don't have a skyDive account ?
Don't have a skyDive account ?
ASKER
My code is almost identical to what is posted. Is there some setting in the project that I don't have ?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Removing the keyword "Shared" fixed the small app.
2 Conclusions.
1.) Main (threaded) function can't be "Shared". You explanation explains the err msg I was receiving (Invoke or BeginInvoke cannot be called on a control until the window handle has been created.)
2.) You must make the call back from the same FORM class ? (Not another class ?)
Thanks
2 Conclusions.
1.) Main (threaded) function can't be "Shared". You explanation explains the err msg I was receiving (Invoke or BeginInvoke cannot be called on a control until the window handle has been created.)
2.) You must make the call back from the same FORM class ? (Not another class ?)
Thanks
Did that answer this question?
ASKER
Thanks for the help.
Was not really sure what 'Shared' meant, I just copied it from another example.
Can I get your opinion on #2 ? I would like to be able to build another class or module and call back and update the form, if its possible.
Thanks
Was not really sure what 'Shared' meant, I just copied it from another example.
Can I get your opinion on #2 ? I would like to be able to build another class or module and call back and update the form, if its possible.
Thanks
Hi sidwelle;
In order to create and run the thread in another class and have it access Form1 you will need to pass it a reference to Form1 when you create the other class. Below is a code snippet to show how it can be done. Please note that I turned all your Function's to Sub's because non of them were returning any values.
In order to create and run the thread in another class and have it access Form1 you will need to pass it a reference to Form1 when you create the other class. Below is a code snippet to show how it can be done. Please note that I turned all your Function's to Sub's because non of them were returning any values.
Imports System.Threading
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' Create an instance of the class that will be running the thread. Note that I am passing a reference to this Form in the constructor.
Dim sClass As New SomeClass(Me)
' Start the thread in the SomeClass instance
sClass.Starter()
End Sub
Private Delegate Sub StatusDelegate(ByVal txt As String)
Public Sub Status(ByVal txt As String)
If Me.Label1.InvokeRequired Then
Me.Invoke(New StatusDelegate(AddressOf Status), txt)
Else
Me.Label1.Text = txt
Me.Text = txt
Me.Label1.Update()
End If
End Sub
End Class
Public Class SomeClass
' Variable that holds a reference to the Form that will be called to write to the label on.
Private callingForm As Form1
' Constructor being passed a reference to the Form that will be modified
Public Sub New(ByVal f As Form1)
callingForm = f
End Sub
Public Sub Starter()
Dim t As New Thread(AddressOf Main)
t.IsBackground = True
t.Start()
End Sub
' Note that I am using the reference to the other Form when calling Status.
Public Sub Main()
callingForm.Status("This is a test ...")
End Sub
End Class
ASKER
Thank you.
I will work with it tomorrow and post back.
I will work with it tomorrow and post back.
ASKER
I can get it to work, few points ...
1.) I don't exactly know why it wants "f as form1" and not "f as form" in my head it should accept either ?
I know that from1 will tie you back to a copy that exact form, but why is it so exclusive and not accept any form name ?
2.) When the Delegate is called it seems to release the second thread, it acts like it waits for a value to be returned, but when the execution gets back to the line in the second thread that called it, the value returned is "nothing" ?
I need to know how to get a value back to the line in the 2nd class that called the function in the IU class.
Again:
I need to know how to get the "Status" function to return a value to the 2nd thread.
I noticed that I can assign return values if the function is not delegated, I can even read form controls ?
Do I only need to use the invoke mechanism when updating a control ?
Thanks
1.) I don't exactly know why it wants "f as form1" and not "f as form" in my head it should accept either ?
I know that from1 will tie you back to a copy that exact form, but why is it so exclusive and not accept any form name ?
2.) When the Delegate is called it seems to release the second thread, it acts like it waits for a value to be returned, but when the execution gets back to the line in the second thread that called it, the value returned is "nothing" ?
I need to know how to get a value back to the line in the 2nd class that called the function in the IU class.
Again:
I need to know how to get the "Status" function to return a value to the 2nd thread.
I noticed that I can assign return values if the function is not delegated, I can even read form controls ?
Do I only need to use the invoke mechanism when updating a control ?
Thanks
To your questions:
1. Because you need to access the form you will be interacting with or an instance of it and Form is not the right form. Form doest not have a Status Sub and a StatusDelegate definition and if it does it's not the right one .
2. Not sure what you are saying here can you provide more details on this question.
To this question, "I need to know how to get the "Status" function to return a value to the 2nd thread.", Change the delegate as follows, where ReturnDataType is the datatype being returned:
Private Delegate Function StatusDelegate(ByVal txt As String) As ReturnDataType
and change the Status Sub to a Function as follows, where ReturnDataType is the same type as the delegate.:
Public Function Status(ByVal txt As String) As ReturnDataType
Inside the Status function place a Return statement with the variable being returned
To your question, "Do I only need to use the invoke mechanism when updating a control ?", According to the documentation when accessing a controls property there are only four functions that are thread safe and there are Invoke, BeginInvoke, EndInvoke, and CreateGraphics anything else can cause issues.
1. Because you need to access the form you will be interacting with or an instance of it and Form is not the right form. Form doest not have a Status Sub and a StatusDelegate definition and if it does it's not the right one .
2. Not sure what you are saying here can you provide more details on this question.
To this question, "I need to know how to get the "Status" function to return a value to the 2nd thread.", Change the delegate as follows, where ReturnDataType is the datatype being returned:
Private Delegate Function StatusDelegate(ByVal txt As String) As ReturnDataType
and change the Status Sub to a Function as follows, where ReturnDataType is the same type as the delegate.:
Public Function Status(ByVal txt As String) As ReturnDataType
Inside the Status function place a Return statement with the variable being returned
To your question, "Do I only need to use the invoke mechanism when updating a control ?", According to the documentation when accessing a controls property there are only four functions that are thread safe and there are Invoke, BeginInvoke, EndInvoke, and CreateGraphics anything else can cause issues.
ASKER
Tried using the "Return" statement, but the result is the same.
I had seen that before in other examples, just thought someone typed in some java ?
I had seen that before in other examples, just thought someone typed in some java ?
You are not using the Return statement to replace the txt variable are you?
ASKER
In the following example, a value of 1 is returned, I never did see a value of 2.
Even when I commented each of them out.
Even when I commented each of them out.
Private Delegate Function StatusDelegate(ByVal txt As String) As Integer
Public Function Status(ByVal txt As String) As Integer
If Me.Label1.InvokeRequired Then
Me.Invoke(New StatusDelegate(AddressOf Status), txt)
Return 1
Else
Me.Label1.Text = txt
Return 2
End If
End Function
Did the value in txt show up in the label?
ASKER
Yes.
I get the value of 1 with either:
return 1
or
Status = 1
When placed in the If statement before the else.
I get the value of 1 with either:
return 1
or
Status = 1
When placed in the If statement before the else.
Can you post the code that you are using so that I can drop it into a project to see what you are trying to do.
ASKER
The following example lets me pass any form name that I want: (it has to 'Me' if passing the form that you calling from)
Public Class Form2
Private Sub cmdShowFrm3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdShowFrm3.Click
Form3.ShowMe(Me)
End Sub
End Class
Public Class Form3
Private cf As Form
Public Function ShowMe(ByVal f As Form) As Integer
cf = f
Me.Show()
End Function
Private Sub Form3_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'Center me on top of calling form ...
Dim iMidWidth As Integer = cf.Left + cf.Width / 2
Dim iMidHeight As Integer = cf.Top + cf.Height / 2
Me.Left = iMidWidth - (Me.Width / 2)
Me.Top = iMidHeight - (Me.Height / 2)
End Sub
End Class
ASKER
Here is the other example:
Imports System.Threading
Public Class Form1
Private WithEvents MyClass1 As New SomeClass(Me)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
MyClass1.Starter(Me)
'Starter()
'Dim t As New Thread(AddressOf Main)
't.IsBackground = True
't.Start()
End Sub
Private Delegate Function StatusDelegate(ByVal txt As String) As Integer
Public Function Status(ByVal txt As String) As Integer
If Me.Label1.InvokeRequired Then
Me.Invoke(New StatusDelegate(AddressOf Status), txt)
'Return 1
Status = 1
Else
Me.Label1.Text = txt
Me.Text = txt
Beep()
'Return 2
Status = 2
End If
End Function
'Public Function Starter() As Integer
' Dim t As New Thread(AddressOf Main)
' t.IsBackground = True
' t.Start()
'End Function
'Public Function Main() As Integer
' Thread.Sleep(2000)
' Me.Status("This is a test ...")
'End Function
Public Class SomeClass
Private frm As Form1
Public Function Starter(ByVal f As Form1) As Integer
Dim t As New Thread(AddressOf Main)
t.IsBackground = True
t.Start()
End Function
Public Function Main() As Integer
Dim iRtn As Integer
Thread.Sleep(500)
iRtn = frm.Status("This is a test ...")
MsgBox("Value Returned: " & iRtn.ToString)
End Function
Public Sub New(ByVal f As Form1)
frm = f
End Sub
End Class
End Class
Which Form is your startup form?
ASKER
The Center example: Form2
OK using Form2 as the start up form. Click on the button and it opens Form3 on top of Form2. Form1 never gets created and no thread is called no label gets fill.
Do you have other code that creates Form1 and runs the thread?
Do you have other code that creates Form1 and runs the thread?
ASKER
No, I meant them to ack as two seperate projects.
With the Center example, I was trying to demonstrate that IDE does not force me to use the name of a specific form in the defination of the function:
With the Center example, I was trying to demonstrate that IDE does not force me to use the name of a specific form in the defination of the function:
Public Function ShowMe(ByVal f As Form) As Integer
To your statement, "With the Center example, I was trying to demonstrate that IDE does not force me to use the name of a specific form in the defination of the function:"; this is true but that is because all Form's inherits from Windows Form control they all have the same basic properties. So Form1, Form2 and Form3 can all be cast to a Form type. But what you can NOT do is have a function in Form3 let say and have that function called PrintDisplay. If you try the same thing then it will NOT work because a object of type Form does not have a function called PrintDisplay you would need to cast it to a type Form3.
The reason you was seeing only return of 1 you was only displaying the return value of the last call only. Make the below changes and you will see both.
The reason you was seeing only return of 1 you was only displaying the return value of the last call only. Make the below changes and you will see both.
Public Function Status(ByVal txt As String) As Integer
If Me.Label1.InvokeRequired Then
' When the next line is executed this thread waits until the else is done
Dim iRetMainThread As Integer = Me.Invoke(New StatusDelegate(AddressOf Status), txt)
' Comming back from GUI thread will print 2 here
MsgBox("Return value from main thread" & iRetMainThread)
' Now return 1 to the original caller of this function.
'Return 1
Status = 1
Else
Me.Label1.Text = txt
Me.Text = txt
Beep()
'Return 2
Status = 2
End If
End Function
Public Function Main() As Integer
Dim iRtn As Integer
Thread.Sleep(500)
iRtn = frm.Status("This is a test ...")
MsgBox("Value Returned Non GUI thread: " & iRtn.ToString)
End Function
ASKER
This is the line that I wanted to see, Simple now that you see it.
Status = Me.Invoke(New StatusDelegate(AddressOf Status), txt)
I want to thank you for your help. You have gone above and beyond most of the other experts that answer with a one liner.
If I have any more questions, I will post a new question.
Thanks
Status = Me.Invoke(New StatusDelegate(AddressOf Status), txt)
I want to thank you for your help. You have gone above and beyond most of the other experts that answer with a one liner.
If I have any more questions, I will post a new question.
Thanks
Thank you. The way I see it if I am to be of help then do it the right way by making sure what I post is clear and not just a one liner.
Have a great day.
Have a great day.
The Delegate type should match the type you are creating. So try it this way. Also when you execute the Status function and the Invoke is executed pass the same parameter as txt not Text.
Open in new window