UnauthorizedAccessException: недопустимый сквозной доступ в приложении Silverlight (XAML/С#)
Относительно новый для С# и хотел попробовать поиграть с ним с помощью стороннего 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>
и вот код С#
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";
}
Я предполагаю, что это вызвано тем фактом, что обратный вызов выполняется в отдельном потоке, чем пользовательский интерфейс? Каков нормальный поток для борьбы с этими типами взаимодействий в С#?
Спасибо.
Ответы
Ответ 1
Полезный способ получить доступ к перекрестным потокам с помощью вызова 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);
}
}
тогда вы можете обернуть любые вызовы, обновляющие пользовательский интерфейс, где вы можете быть в фоновом потоке, например:
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");
}
Таким образом, вам не нужно знать, находитесь ли вы в фоновом потоке или нет, и это позволяет избежать накладных расходов на добавление методам для каждого элемента управления, чтобы проверить это.
Ответ 2
Другой способ сделать это - использовать функцию this.Dispatcher.CheckAccess()
. Это то же самое, что и в WPF, но оно не отображается в вашем intellisense, поэтому вы, возможно, не видели его. Что вы делаете, это проверить, есть ли у вас доступ к потоку пользовательского интерфейса, и если вы не хотите, чтобы вы рекурсивно возвращались к потоку пользовательского интерфейса.
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 я использовал общую функцию, которая также требует обновления элемента управления пользовательского интерфейса, а также самого обновления. Таким образом, у меня нет одной из этих функций для каждого элемента управления на странице, которое может потребоваться для обновления с результаты фонового потока.
Ответ 3
Как вы уже догадались, silverlight выполняет асинхронный веб-запрос в отдельном потоке, чтобы избежать замораживания пользовательского интерфейса.
Чтобы отправлять сообщения в поток пользовательского интерфейса, вы можете использовать метод Deployment.Current.Dispatcher.BeginInvoke(delegate, object []).