从Windows 8 / WinRT中的其他线程更新UI元素

时间:2012-11-29 01:21:25

标签: c# asynchronous windows-8 windows-runtime

我在Windows 8“Metro”应用程序中有一个按钮和一个文本块。单击该按钮时,它通过HttpWebRequest调用Web服务。

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
}

我想使用从WebRequest返回的数据更新info.Text,但是它会导致错误:“应用程序调用了为不同线程编组的接口。”我理解这是因为它没有从UI线程调用。

我找到了涉及DispatcherSynchronizationContextawait关键字的各种不同解决方案。

最简单/最好的方法是什么?

3 个答案:

答案 0 :(得分:4)

像Damir一样,我们确实应该使用async / await模式,但有时只需更新工作线程(任务)中的UI。这是使用当前的CoreDispatcher完成的,该调度将调用分派给UI线程:

private void ResponseCallback(IAsyncResult asyncResult)
{
   ...

   this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
   {
      info.Text = responseString;
   });
}

答案 1 :(得分:2)

在Windows应用商店应用中,您确实应该为所有异步调用使用异步等待模式 - 这是最简单,最有效的方式。您可以重写现有代码以使用此模式,如下所示:

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
}

每个await关键字之后的代码实际上就像它在回调中一样,但它总是在UI线程上执行,因此您不必担心它。

Windows应用商店应用的API中的所有IO绑定操作都以异步方式提供。你应该更喜欢它们而不是同步的,即使两者都可以防止阻塞UI线程 - 例如上面的ReadToEndAsync示例。

答案 2 :(得分:1)

最简单,最好:

private async void buttonGo_Click(object sender, RoutedEventArgs e)
{
  using (var client = new HttpClient())
  {
    info.Text = await client.GetStringAsync("http://localhost/");
  }
}

在幕后,await会捕获当前SynchronizationContext并在该上下文中恢复该方法。 Win8 / WinRT SynchronizationContext依次使用Dispatcher