UnauthorizedAccessException:Silverlight应用程序中的无线跨线程访问(XAML / C#)

时间:2010-08-06 00:58:26

标签: c# silverlight

相对较新的C#,并希望尝试使用它的一些第三方Web服务API。

这是XAML代码

    <Grid x:Name="ContentGrid" Grid.Row="1">
        <StackPanel>
            <Button Content="Load Data" Click="Button_Click" />
            <TextBlock x:Name="TwitterPost" Text="Here I am"/>
        </StackPanel>
    </Grid>

这是C#代码

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://api.twitter.com/1/users/show/keykoo.xml");
        request.Method = "GET";

        request.BeginGetResponse(new AsyncCallback(twitterCallback), request);
    }

    private void twitterCallback(IAsyncResult result)
    {
        HttpWebRequest request = (HttpWebRequest)result.AsyncState;
        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
        TextReader reader = new StreamReader(response.GetResponseStream());
        string strResponse = reader.ReadToEnd();

        Console.WriteLine("I am done here");
        TwitterPost.Text = "hello there";
    }

我猜这是因为回调在一个单独的线程上执行而不是UI?在C#中处理这些类型的交互的正常流程是什么?

感谢。

3 个答案:

答案 0 :(得分:29)

使用CheckAccess调用轻松进行跨线程访问的一种有用方法是将实用程序方法包装在静态类中 - 例如

public static class UIThread
{
    private static readonly Dispatcher Dispatcher;

    static UIThread()
    {
        // Store a reference to the current Dispatcher once per application
        Dispatcher = Deployment.Current.Dispatcher;
    }

    /// <summary>
    ///   Invokes the given action on the UI thread - if the current thread is the UI thread this will just invoke the action directly on
    ///   the current thread so it can be safely called without the calling method being aware of which thread it is on.
    /// </summary>
    public static void Invoke(Action action)
    {
        if (Dispatcher.CheckAccess())
            action.Invoke();
        else
            Dispatcher.BeginInvoke(action);
    }
}

然后你可以包装任何更新UI的调用,你可能在这样的后台线程上:

private void twitterCallback(IAsyncResult result)   
{   
        HttpWebRequest request = (HttpWebRequest)result.AsyncState;   
        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);   
        TextReader reader = new StreamReader(response.GetResponseStream());   
        string strResponse = reader.ReadToEnd();   

        UIThread.Invoke(() => TwitterPost.Text = "hello there");
}   

这样你就不必知道你是否在后台线程上,并且它避免了为每个控件添加方法来检查它的开销。

答案 1 :(得分:2)

另一种方法是使用this.Dispatcher.CheckAccess()功能。它与您在WPF中获得的相同,但它不会出现在您的intellisense中,因此您可能没有看到它。你所做的是检查你是否有权访问UI线程,如果你没有,你可以在UI线程上递归回复

private void twitterCallback(IAsyncResult result)
{
    HttpWebRequest request = (HttpWebRequest)result.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
    TextReader reader = new StreamReader(response.GetResponseStream());
    string strResponse = reader.ReadToEnd();

    Console.WriteLine("I am done here");
    ////TwitterPost.Text = "hello there";
    postMyMessage(TwitterPost.Text);
}

private void postMyMessage(string text)
{
    if (this.Dispatcher.CheckAccess())
        TwitterPost.Text = text;
    else
        this.Dispatcher.BeginInvoke(new Action<string>(postMyMessage), text);
}

这只是一个非常简单的示例,一旦您拥有多个控件,就会无法管理。对于一些WPF / Silverlight的东西,我使用了一个泛型函数,它也需要更新UI控件以及更新本身,这样我就没有这些函数中的一个可用于页面上每个可能需要更新的控件后台线程的结果。

答案 2 :(得分:1)

正如您所猜测的,silverlight在一个单独的线程上异步执行所有Web请求,以避免冻结UI。

要将消息发送到UI线程,您可以使用Deployment.Current.Dispatcher.BeginInvoke(delegate, object[])方法。