Visual Studio 2010 and .NET Framework 4 Training Kit

Апрельский Training Kit по Visual Studio 2010 и .NET Framework 4, очеь рекомендую для ознакомления всем :)

Silverlight Toolkit

The Silverlight Toolkit is a collection of Silverlight controls, components and utilities made available outside the normal Silverlight release cycle. A product of the Microsoft Silverlight product team, the Silverlight Toolkit adds new functionality quickly for designers and developers, and provides the community an efficient way to help shape product development by contributing ideas and bug reports. It includes full open source code, unit tests, samples and documentation for over 26 new controls covering charting, styling, layout, and user input.

Overview: http://www.silverlight.net/content/samples/sl3/toolkitcontrolsamples/run/default.html

Facebook Toolkit: Could not deserialize data returned from server

Подробное обсуждение проблемы тут

Там же и ссылка на скачивание сборки для ASP.NET, но для Silverlight нет, потому рекомендую сразу качать бету 3.1, хотя в сборке 48944 фикс проблемы тоже присутствует

Кстати говоря, после распаковки не забудьте разанлочить файлы dll (правой кнопой мыши по файлу -> в правом нижнем углу кнопка unlock, если ее нет, то все уже в порядке)

System.UnauthorizedAccessException: Invalid cross-thread access error

Столкнулся с проблемой - в Silverlight нельзя изменять свойства компонент во время обработки результатов какого нить асинхронного вызова (в отдельном потоке). Казалось бы - вроде все одно - контрол и код едины, ни каких постбеков на сервер с передачей состояний нет, а вот и не работает, получаем - System.UnauthorizedAccessException: Invalid cross-thread access error

В Silverlight очень просто это обходится - при помощи класса Dispatcher.

Вырезка из msdn:
"Объект Dispatcher поддерживает очередь рабочих элементов для заданного потока с приоритетами.

Когда для потока создается объект Dispatcher, он становится единственным объектом Dispatcher, который может быть связан с потоком, даже если этотDispatcher остановлен.

При попытке приложения получить CurrentDispatcher для текущего потока, в случае, если с потоком не связан объект Dispatcher, выполняется создание объекта Dispatcher.

Если Dispatcher остановлен, его невозможно перезапустить.

В WPF объект DispatcherObject доступен только для Dispatcher, с которым он связан. Например, фоновый поток не может обновить содержимое элемента управления Button, связанного с Dispatcher потока Пользовательский интерфейс. Чтобы фоновый поток получил доступ к свойству Content элемента управления Button, он должен делегировать работу объекту Dispatcher, связанному с потоком Пользовательский интерфейс. Это делается с помощью метода Invoke или BeginInvoke. Метод Invoke является синхронным, а BeginInvoke — асинхронным. Данная операция добавлена в очередь объектаDispatcher с заданным DispatcherPriority.

Если BeginInvoke вызывается для остановленного Dispatcher, свойству состояния возвращаемого объекта DispatcherOperation присваивается значениеAborted.

Все методы объекта Dispatcher за исключением DisableProcessing являются независимыми от потока."

Итак, теперь становится ясно в чем проблема, и для ее решения необходимо чтобы фоновый поток передал обязанности диспатчеру связанному с пользовательским интерфейсом.

для этого необходимо лишь поместить часть кода в this.Dispatcher.BeginInvoke и больше ничего не менять, для примера я немного изуродовал свой код и представляю ниже:

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
GameState.OnLoggined += (obj, erg) =>
{
GameState.FacebookAPI.Users.GetInfoAsync((info, s, ex) =>
{

this.Dispatcher.BeginInvoke(() =>
{
if (info != null)
litUserName.Text = info[0].name;
else
litUserName.Text = "Guest";
});

}, null);
};

}

В данном примере, на загрузке контрола асинхронно выполняется функция GetInfoAsync, по ее завершении мы имеем данные пользователя, авторизовавшегося в нашем приложении. Я всего навсего хотел было вывести имя пользователя в компоненту litUserName, но, как оказалось, не имел доступа к ним. Для получения доступа мне достаточно было обернуть часть кода в this.Dispatcher.BeginInvoke(() => { /* my code */ } ); и все решилось само собой, т.к. код выполняется теперь в контексте потока отвечающего за пользовательский интерфейс (или типа того)