Reputation: 3027
I have a button and a textblock in a Windows 8 "Metro" application. When the button is clicked it calls a webservice via HttpWebRequest.
private void buttonGo_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://localhost/");
req.BeginGetResponse(ResponseCallback, req);
}
private void ResponseCallback(IAsyncResult asyncResult)
{
HttpWebRequest req = (HttpWebRequest)asyncResult.AsyncState;
HttpWebResponse res = (HttpWebResponse)req.EndGetResponse(asyncResult);
Stream streamResponse = res.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
info.Text = responseString; // Can't do this as outside the UI thread
}
I want to update info.Text
with the data returned from the WebRequest, however it causes an error: "The application called an interface that was marshalled for a different thread." I understand that this is because it is not being called from the UI thread.
I have found various different solutions involving Dispatcher
, SynchronizationContext
, the await
keyword.
What is the easiest/best way to do this?
Upvotes: 1
Views: 5743
Reputation: 3023
Like Damir said we really should use the async/await pattern but sometimes it's simply necessary to update the UI in a worker thread (task). This is done using the current CoreDispatcher that dispatches the invocation to the UI thread:
private void ResponseCallback(IAsyncResult asyncResult)
{
...
this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
info.Text = responseString;
});
}
Upvotes: 4
Reputation: 17855
In a Windows Store app you really should use the async await pattern for all asynchronous calls - it is the simplest and most effective way. You can rewrite your existing code to use this pattern as follows:
private async void buttonGo_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://localhost/");
HttpWebResponse res = (HttpWebResponse)(await req.GetResponseAsync());
Stream streamResponse = res.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
// all IO operations should be called asynchronously
string responseString = await streamRead.ReadToEndAsync();
info.Text = responseString; // This way the code runs on UI thread
}
The code after each await keyword is effectively behaving as if it was in a callback but it always executes on the UI thread so that you don't have to worry about it.
All IO bound operations in APIs for Windows Store apps are available in asynchronous flavor. You should prefer them over synchronous ones even when both are available to prevent blocking of UI thread - such as in ReadToEndAsync
example above.
Upvotes: 2
Reputation: 456497
Easiest and best:
private async void buttonGo_Click(object sender, RoutedEventArgs e)
{
using (var client = new HttpClient())
{
info.Text = await client.GetStringAsync("http://localhost/");
}
}
Under the covers, await
captures the current SynchronizationContext
and resumes the method in that context. The Win8/WinRT SynchronizationContext
in turn uses the Dispatcher
.
Upvotes: 1