views:

28

answers:

1

Hi eberybody,

I have this piece of code in my Silverlight project:

private void button1_Click(object sender, RoutedEventArgs e)
{
    string baseUri = "http://foo.bar";
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(baseUri));
    request.BeginGetResponse(new AsyncCallback(ReadCallback),request);
}
private void ReadCallback(IAsyncResult asynchronousResult)
{
    HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
    using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream()))
    {
        string resultString = streamReader1.ReadToEnd();
        MessageBox.Show(resultString);
    }
}

When I run the code, I get the following exception:

unauthorizedaccessexception
Invalid cross-thread access.

I thought It was a problem with the returned string. But, even if I put this:

MessageBox.Show("foobar");

the same exception rises. I think that the problem comes from the ReadCallback function that cannot perform such actions.

Can you help me?

Thank you,

Regards

+2  A: 

I think this is because certain parts of Silverlight cannot be invoked by code and instead must originate from user input; I think the MessageBox falls in this category. So unless the MessageBox is called as a result of the user pressing a button or hyperlink, it's not going to work.

Note that it doesn't have to be in the handler for the button or hyperlink click event; anywhere further down the call hierarchy works. What I basically did when I needed to initiate such actions as a result of an async service call, for instance, is I dispalyed a ChildWindow containing a hyperlink saying 'click here to open your x' or whatever the user was trying to do (I think I used it to open a server-generated PDF or similar file).

Also, if the MessageBox can indeed be called without all he malarkey I just wrote about, the Cross-thread access exception can be solved with a call to Dispatcher.Invoke or Dispatcher.BeginInvoke passing a delegate that does the actual action. That's because asynchronous actions (obviously) take place in a separate thread, but Silverlight, like WPF and like WinForms before them, needs to access its controls from only one thread; invoking using the control's Dispatcher delegates the actions to the correct thread.

To use the BeginInvoke solution, try this instead of simply calling MessageBox.Show:

string resultString = streamReader1.ReadToEnd();
Dispatcher.Invoke(new Action(() => MessageBox.Show("")));

That's assuming the code is in a Control code-behind (UserControl, Window, anything), so the Dispatcher property is available. You can use BeginInvoke instead of Invoke if you want to allow the user interface time to properly update itself, but usually the difference is imperceptible.

Alex Paven
@Alex Paven: I think you missed the actual question. The MessageBox was the red-herring he added to the cross-thread problem afterwards.
Enough already
Yep, also updated my answer immediately to include Dispatcher.Invoke, but I totally understand I have a convoluted and verbose style which may trigger TLDR reactions.
Alex Paven
+1 for the final paragraph the `Dispatcher.BeginInvoke` is what is needed.
AnthonyWJones
+1 for correct use of TLDR in a sentence :P
Enough already
Thanks for your answer. Can you please show me how can I handle this exception with the Dispatcher.BeginInvoke functionality?
Zakaria
Edited the answer to include an example of (Begin)Invoke. Forgot to mention that both Invoke and BeginInvoke allow to specify a priority of the request, but you can find that out on MSDN as well. They also allow parameters to be passed to the delegates. Edit: also corrected the syntax.
Alex Paven