Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid cross-thread access in Silverlight app

I am using the Hammock framework to make asyncronous service calls from a Silverlight application to Rest services. In the 'completed' callback I am updating an ObservableCollection that is bound to a combobox on the view.

An 'Invalid cross-thread access' exception is being thrown in the 'OnPropertyChanged' event handler.

Is this becasue Hammock is not executing the callback on the UI thread? If not, why not? That would seem to be functionality that the framework should handle. Am I missing something? I sure do not want to handle the invoking of the UI thread myself in each completed handler.

public void LoadMyData()
{
    var request = new RestRequest();
    request.Path = "MyRestUrlText";

    var callback = new RestCallback(
      (restRequest, restResponse, userState) =>
      {
        var visibleData = new ObservableCollection<MyDataType>();

        var myData = JsonConvert.DeserializeObject<MyDataType[]> restResponse.Content);

        foreach (var item in myData)
            visibleData .Add(item);

        this.MyBoundCollection = visibleData;
        OnPropertyChanged("MyBoundCollection");
    });

    var asyncResult = _restClient.BeginRequest(request, callback);
}

Thanks

like image 420
Jim Reineri Avatar asked Jul 08 '11 11:07

Jim Reineri


1 Answers

For bound properties and properties that are collections (and not children in observable collections) it is only the OnPropertyChanged that needs to be on the UI thread. The properties can change earlier, but the UI will not change bindings until OnPropertyChanged is called.

All our ViewModels derive from a ViewModelBase we created that implements a helper SendPropertyChanged like below (so we never have to worry about cross-threading).

All our notify properties call that instead of calling OnPropertyChanged directly.

It also exposes a generally useful OnUiThread method so you can execute arbitrary code on the UI thread:

protected delegate void OnUiThreadDelegate();

public event PropertyChangedEventHandler PropertyChanged;

public void SendPropertyChanged(string propertyName)
{
    if (this.PropertyChanged != null)
    {
        this.OnUiThread(() => this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));
    }
}

protected void OnUiThread(OnUiThreadDelegate onUiThreadDelegate)
{
    if (Deployment.Current.Dispatcher.CheckAccess())
    {
        onUiThreadDelegate();
    }
    else
    {
        Deployment.Current.Dispatcher.BeginInvoke(onUiThreadDelegate);
    }
}

If you are not using MVVM, a) apologies and b) shame on you :)

like image 88
Gone Coding Avatar answered Oct 05 '22 20:10

Gone Coding