Question Form not responsive

snw1969

Member
Joined
Dec 19, 2008
Messages
9
Programming Experience
10+
I'm running into an issue with a form being called programmatically. The scenario is as follows:

My app listens to events being generated from a remote server. When the remote server fires an event my app captures the event message and makes an asynchronous call to a function that parses out the message. If the message has particular data contained in it, then it pops up a form with that data for the user sitting in a call center to view.

The issue that I am running into is that the form being called, once it displays on the desktop with the information is unresponsive. I can't modify the data, I can't close the window, I can't do anything with it. So I'm not sure how to go about resolving this.

My instinct tells me that because the form is being called from within the asynchronous function, it is holding on to the form somehow and not releasing it back to the user and therefore they can't do anything with it.

Now, I can move the form around the screen, but then it gets the "Not Responding" in the title bar...

Any ideas?

Thanks,

Shawn

(Also, I forgot to mention - this is not an MDI application)
 
Last edited:
You should not be manipulating UI elements from any thread other than the UI thread. If you want to display a form to the user then you should be doing so on the UI thread. You can use delegation to marshal a method call to the UI thread in order to display the form. This explains how.

I would guess that your form is unresponsive because the background thread is busy doing its work, which is exactly why UI work should be done on the UI thread only: so the thread isn't too busy doing other things to update the UI.
 
You should not be manipulating UI elements from any thread other than the UI thread. If you want to display a form to the user then you should be doing so on the UI thread. You can use delegation to marshal a method call to the UI thread in order to display the form. This explains how.

I would guess that your form is unresponsive because the background thread is busy doing its work, which is exactly why UI work should be done on the UI thread only: so the thread isn't too busy doing other things to update the UI.

Hey! Some other posters pointed me to your write up on how to accomplish this last night. When I came home I attempted to do as you said, although with a form I am not sure I'm calling it correctly. If I post the code here, can you tell me if it looks right?

(I actually posted this issue both here and at the other forum you have directed me to here.)

Thanks,

Shawn
 
A form is a control like any other. You simply use the Invoke and InvokeRequired members of your main form as I've demonstrated and, in the method you invoke, display this other form. By all means post your code and I'll take a look.
 
A form is a control like any other. You simply use the Invoke and InvokeRequired members of your main form as I've demonstrated and, in the method you invoke, display this other form. By all means post your code and I'll take a look.

Thanks John (?). So what I did was attempt to follow your code examples as closely as I could based on how I understood the concepts you had presented. So if there are any errors in my code, it is going to be related to my lack of grasping the concepts and not to your instruction.

Following your lead, I first created a routine that would actually call the form as follows:

VB.NET:
Expand Collapse Copy
    Private Sub ShowForm(ByVal FormName As String, ByVal userData As Object)
        Select Case FormName
            Case "Residential"
                ' Launch the Residential form...
                With Residential
                    .Show()

                    .txtTimeStamp.Text = DateTime.Now().ToString()
                    .txtAccountNumber.Text = userData.Item("Acct#").ToString()
                    .txtANI.Text = userData.Item("ANI").ToString()
                    .txtReceiptNumber.Text = userData.Item("RCPT#").ToString()
                    .txtCustomerName.Text = userData.Item("Name").ToString()
                    .txtCompanyName.Text = userData.Item("CO").ToString()

                    .Refresh()
                End With

            Case "Outage"
                Outage.Show()
                Outage.txtTimeStamp.Text = DateTime.Now().ToString()
                Outage.Refresh()

            Case "Welcome"
                Welcome.Show()
                Outage.Refresh()

            Case Else
                ' Do nothing...
        End Select
    End Sub

Second, I created a delegate invoker based upon the above routines signature/interface...

VB.NET:
Expand Collapse Copy
    Private Delegate Sub ShowFormInvoker(ByVal FormName As String, ByVal userData As Object)

Third, I modified my existing code as follows (again I removed all the other case statements in order to focus just on the one that I was having trouble with. NOTE: I highlighted the specific code in red in this block.):

VB.NET:
Expand Collapse Copy
    Private Function CheckReturnMessage(ByVal response As IMessage) As Boolean
        Dim knownEvent As Boolean = False

        ' Check the event coming in and let's handle it...
        Select Case response.Id
            Case EventEstablished.MessageId
                Dim theEvent As EventEstablished
                Dim userData As KeyValueCollection
                Dim callerType As String

                Log.WriteLogFile(response.ToString() & NewLine)

                ' Cast the message to an EventEstablished object...
                theEvent = DirectCast(response, EventEstablished)
                userData = theEvent.UserData

                If Not userData Is Nothing Then
                    Log.WriteLogFile(userData.ToString())

                    callerType = userData.Item("CallType").ToString()

                    If callerType = "" Then
                        callerType = "Data Not Available."
                    End If

                    If theEvent.CallType = CallType.Inbound Then
                        Try
                            Select Case callerType
                                Case "Residential"
[COLOR="Red"]                                    If Residential.InvokeRequired Then
                                        Me.Invoke(New ShowFormInvoker(AddressOf ShowForm), "Residential", userData)
                                    Else
                                        With Residential
                                            .Show()

                                            .txtTimeStamp.Text = DateTime.Now().ToString()
                                            .txtAccountNumber.Text = userData.Item("Acct#").ToString()
                                            .txtANI.Text = userData.Item("ANI").ToString()
                                            .txtReceiptNumber.Text = userData.Item("RCPT#").ToString()
                                            .txtCustomerName.Text = userData.Item("Name").ToString()
                                            .txtCompanyName.Text = userData.Item("CO").ToString()

                                            .Refresh()
                                        End With
                                    End If[/COLOR]

                                Case "Outage"
 [COLOR="Red"]                                   If Outage.InvokeRequired Then
                                        Me.Invoke(New ShowFormInvoker(AddressOf ShowForm), "Outage", Nothing)
                                    Else
                                        With Outage
                                            .Show()
                                            .txtTimeStamp.Text = DateTime.Now().ToString()
                                            .Refresh()
                                        End With
                                    End If
[/COLOR]
                            End Select
                        Catch ex As Exception

                        End Try
                    End If

                    knownEvent = True

                End If

        End Select

        Return knownEvent
    End Function

I believe I implemented this correctly. I could be wrong, but I think I have it right. :)

I guess the one concern/question I have is in regard to the InvokeRequired method. If the forms have not been instantiated yet, will the InvokeRequired give me the boolean value? Since I'm trying to instantiate the form based upon a condition being met, I'm hoping that the InvokeRequired will give me the value to execute the right code.

Thanks again for any advice.

Shawn
 
InvokeRequired will not give you correct information here in any case. Default forms are thread specific, meaning each thread has its own instance. There is no need for you to check "InvokeRequired" here since you know it is required. So replace all your code in red with the Me.Invoke call and it will work properly.
 
InvokeRequired will not give you correct information here in any case. Default forms are thread specific, meaning each thread has its own instance. There is no need for you to check "InvokeRequired" here since you know it is required. So replace all your code in red with the Me.Invoke call and it will work properly.

Hi John -

Shouldn't that be Me.BeginInvoke() since I don't want to block the current thread from executing? My understanding (admittedly limited) is that Me.Invoke will execute synchronously thus halting the current asynchronous thread that is making the call whereas Me.BeginInvoke will execute the delegate without waiting for the execution to finish. So, I suppose I'm asking - which of the following code blocks would be the one I should use?

This one:
VB.NET:
Expand Collapse Copy
Me.Invoke(New ShowFormInvoker(AddressOf ShowForm), "Residential", userData)

or this one:
VB.NET:
Expand Collapse Copy
                                    Dim result As IAsyncResult

                                    result = Me.BeginInvoke(New ShowFormInvoker(AddressOf ShowForm), "Residential", userData)

                                    While Not result.IsCompleted
                                        ' Do nothing...
                                    End While

                                    Me.EndInvoke(result)

Although this second way looks like it will block as well since it's polling for the IsCompleted to come back True...So, in the chance that it is the second example as opposed to the first, where would be a good place to put my polling code so as not to block the current calling thread? Or do I even need to utilize the EndInvoke() method? Also, because I'm calling the Invoke method on the Me - is that a different thread than the asynchronous thread that it is being called from? If so, then I guess it wouldn't block the calling thread...ugh, this is a little confusing for me. :)

Shawn
 
Form.Show is not a blocking call so your invoke returns quickly.

As for Control.BeginInvoke you don't need Control.EndInvoke (unless you're collecting a return value). Only for Delegate.BeginInvoke a matching Delegate.EndInvoke is required.
 
Form.Show is not a blocking call so your invoke returns quickly.

As for Control.BeginInvoke you don't need Control.EndInvoke (unless you're collecting a return value). Only for Delegate.BeginInvoke a matching Delegate.EndInvoke is required.

So then I should use the first bit of code then, and I'll be ok?

VB.NET:
Expand Collapse Copy
Me.Invoke(New ShowFormInvoker(AddressOf ShowForm), "Residential", userData)

Shawn
 
Ok - the good news is that I can pop up the form and close it without any error. So that is a huge step forward from last week.

Now the issue is that there is data in textboxes on the form that I am not able to edit or interact with. Is there something else that I ought to be doing in order to have access to those fields? They are not set to disabled, so I though I should be able to click in them and edit data.

Also, while data being populated in the textboxes, the textboxes themselves are transparent and show my desktop...odd behavior.

Thanks,
Shawn
 
Ok - after more testing I have discovered the following:

Form 1 (Residential) displays with the textboxes being transparent. And when I click in the textbox, my desktop has focus. When I click the form itself (outside the textboxes) I am able to tab into a textbox and edit or enter data...

Form 2 (Outage) displays with the textboxes as normal and I am able to interact with them as expected.

The difference between the two forms is that with Residential, I'm passing in data from the Asynchronous thread to the UI thread and pre-populating the textboxes with that data. I do not need to do this for the Outage form and therefore I'm not seeing this strange phenomena.

Any ideas on why this might be happening? I do have one idea that I'm going to try. Instead of passing in the data object from the asynchronous thread, I'm going to pass change the signature of my Delegate method and pass in all the pieces of data ByVal. There are only 5 items I need to hand across so it's not a ton of data. I'll see if that works, but I would like to get it to work with the object instead.

Shawn
 
Ok - after more testing I have discovered the following:

Form 1 (Residential) displays with the textboxes being transparent. And when I click in the textbox, my desktop has focus. When I click the form itself (outside the textboxes) I am able to tab into a textbox and edit or enter data...

Form 2 (Outage) displays with the textboxes as normal and I am able to interact with them as expected.

The difference between the two forms is that with Residential, I'm passing in data from the Asynchronous thread to the UI thread and pre-populating the textboxes with that data. I do not need to do this for the Outage form and therefore I'm not seeing this strange phenomena.

Any ideas on why this might be happening? I do have one idea that I'm going to try. Instead of passing in the data object from the asynchronous thread, I'm going to pass change the signature of my Delegate method and pass in all the pieces of data ByVal. There are only 5 items I need to hand across so it's not a ton of data. I'll see if that works, but I would like to get it to work with the object instead.

Shawn

Ok - passing the data in byval did not seem to make any difference...

So now I am stuck. :)

Shawn
 
This has officially been resolved, so thanks to everyone that helped out with their suggestions and ideas.

The last problem I had with the textboxes being transparent was an odd one, but I fixed it. The form had been created by someone else using VS2008 Express and then they emailed it to me. The other forms they created worked just fine except for this one. So I created a brand new form, created all new controls on it to layout it out the same as the old one. Moved all the code over and voila - it works. So there must have been something residual that happened in the old form that was effecting the behavior of the textboxes.

Tedious but easy fix.

Shawn
 
Back
Top