Invalid cross-thread access

jamie_pattison

Well-known member
Joined
Sep 9, 2008
Messages
116
Programming Experience
Beginner
I have an application that i cant figure why im receiving the error:

System.UnauthorizedAccessException was unhandled
Message=Invalid cross-thread access.

It seems the error kicks in when i try to load the items in the textbox, although if the code to load the items is elsewhere i receive a NullException. Any reason why this could be happening or what i could be doing wrong? I can access the feed with other applications?


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim request As HttpWebRequest = HttpWebRequest.Create("http://someInformation.xml")
request.BeginGetResponse(New AsyncCallback(AddressOf ResponseHandler), request)
End Sub

Private Sub ResponseHandler(ByVal AsyncResult As IAsyncResult)
Dim webReq As HttpWebRequest = DirectCast((AsyncResult.AsyncState), HttpWebRequest)
Dim webRes As HttpWebResponse = DirectCast(webReq.EndGetResponse(AsyncResult), HttpWebResponse)

If webRes.StatusCode = HttpStatusCode.OK Then
Dim xr As XmlReader = XmlReader.Create(webRes.GetResponseStream)
m_NewFeed = SyndicationFeed.Load(xr)
End If

webRes.Close()

TextBox1.Text = m_NewFeed.Items(1).Title.ToString
End Sub

Thanks
 
The point of asynchronous methods is to perform a task in the background, i.e. on a secondary thread. That means that your ResponseHandler is executed on a secondary thread, so you cannot access any UI elements, i.e. controls, directly. You must use delegation. Read this for instructions on how:

Accessing Controls from Worker Threads
 
Don't let the delegate thing throw you off. As jmcilhinney indicated, when you try access a form element from another thread you get the Cross-thread error, this is normal behavior. Using delagates can be confusing so I will try to give you a head start.

First of all, any control on your form that you want to update can be checked to see if it requires invoking. For example Textbox1.InvokeRequired. Which could be used as ....

If Textbox1.InvokeRequired Then

If the statement returns true then you need to use a delegate method to do whatever updates are required to the control. However, you won't want to write a delegate for each control on your form, just write a generic handler that can make the updates you want to any control that supports the property type you are updating.

So, lets say you want a method to update the Text property of any control that actually has a "Text" property. You can simply write a method as follows.

VB.NET:
    Public Shared Sub SetControlText(ByVal ctrl As Control, ByVal value As String)
        If ctrl.InvokeRequired Then
            ctrl.Invoke(New delSetControlText(AddressOf SetControlText), ctrl, value)
        Else
            ctrl.Text = value
        End If
    End Sub

Now in itself, the above is just a method. The delegate part comes in when you add the following line of code to your class.

VB.NET:
Public Delegate Sub delSetControlText(ByVal ctrl As Control, ByVal value As String)

You will notice the name is delSetControlText which is only for readability of the code. It could be any name you want. The important part is that it is created as Delegate Sub and has the same signature as the SetControlText method. Another words they both have the (ByVal ctrl As Control, ByVal value As String) signaturer.

So instead of your TextBox1.Text = m_NewFeed.Items(1).Title.ToString, you would use SetControlText(Textbox1, m_NewFeed.Items(1).Title.ToString).

You will find it is best to create generic delagate methods for the usual properties that you update at runtime. You may have noticed that the method was Shared because I generaly create a class and place all of the delegate methods in it. Making the methods shared allows them to be used anywhere in the appliaction without having to create an instance of the class.

For instance:

VB.NET:
Public Class UI

    Public Delegate Sub delSetControlText(ByVal ctrl As Control, ByVal value As String)
    Public Delegate Sub delSetControlBackColor(ByVal ctrl As Control, ByVal c As Color)
    Public Delegate Function delGetControlText(ByVal tb As TextBox) As String

    Public Shared Function GetControlText(ByVal ctrl As TextBox) As String
        If ctrl.InvokeRequired Then
            ctrl.Invoke(New delGetControlText(AddressOf GetControlText), ctrl)
        Else
            Return ctrl.Text
        End If
    End Function

    Public Shared Sub SetControlText(ByVal ctrl As Control, ByVal value As String)
        If ctrl.InvokeRequired Then
            ctrl.Invoke(New delSetControlText(AddressOf SetControlText), ctrl, value)
        Else
            ctrl.Text = value
        End If
    End Sub

    Public Shared Sub SetControlBackColor(ByVal ctrl As Control, ByVal c As Color)
        If ctrl.InvokeRequired Then
            ctrl.Invoke(New delSetControlBackColor(AddressOf SetControlBackColor), ctrl, c)
        Else
            ctrl.BackColor = c
        End If
    End Sub

End Class
So, SetControlText(Textbox1, m_NewFeed.Items(1).Title.ToString) would become UI.SetControlText(Textbox1, m_NewFeed.Items(1).Title.ToString)

hth
 
Back
Top