0

In my app, when a Button is clicked a Command is triggered that downloads the image (asynchronously.. I hope) and the strings, name and character, specified below. The function to download these items is called from a DispatcherTimer as such:

timer.Tick += myMethodToDownloadCastInfo;

Within this method I have:

 timer.Stop();
 List<CastWithPic> castWithPicList = new List<CastWithPic>();
 // Add name and id for each CastWithPic in the list
 // ..

 _viewModel.SelectedItem.castInfoWithPics = castWithPicList; 

// Download all the required pics
var tasks = new List<Task>();
foreach (CastWithPic cast in castWithPicList)
{
   tasks.Add(downloadBitmap(cast.profilePath, cast.id));
}

await Task.WhenAll(tasks);

Within the downloadBitmap function I set the object SelectedItem's castInfoWithPics to hold the image value.

It is at this point that I am receiving the error:

The calling thread cannot access this object because a different thread owns it.

SelectedItem is declared in my ViewModel.

SelectedItem Class

public List<CastWithPic> castInfoWithPics { get; set; }

CastWithPic Class

public string name
    {
        get { return (string)this.GetValue(nameProperty); }
        set
        {
            this.SetValue(nameProperty, value);
        }
    }

    public static readonly DependencyProperty nameProperty = DependencyProperty.Register(
                                                            "name", typeof(string),
                                                            typeof(CastWithPic));

    public string character
    {
        get { return (string)this.GetValue(characterProperty); }
        set
        {
            this.SetValue(characterProperty, value);
        }
    }

    public static readonly DependencyProperty characterProperty = DependencyProperty.Register(
                                                            "character", typeof(string),
                                                            typeof(CastWithPic));

    public Bitmap image
    {
        get { return (Bitmap)this.GetValue(imageProperty); }
        set
        {
            this.SetValue(imageProperty, value);
        }
    }

    public static readonly DependencyProperty imageProperty = DependencyProperty.Register(
                                                            "image", typeof(Bitmap),
                                                            typeof(CastWithPic));

downloadBitmap

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
RequestState myRequestState = new RequestState();
myRequestState.request = request;

// Start the asynchronous request.
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(RespCallback), Tuple.Create(myRequestState, actorID));

// this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, DefaultTimeout, true);

// The response came in the allowed time. The work processing will happen in the  
// callback function.
allDone.WaitOne();

My question is:

Why don't I get an error when I assign, _viewModel.SelectedItem.castInfoWithPics = castWithPicList; in the timer method but I get an error in the Task method.

Isn't the timer also starting a new thread?

In this case is the proper way to resolve the issue to use the solution suggested here?

The calling thread cannot access this object because a different thread owns it

Community
  • 1
  • 1

2 Answers2

1

I think _viewModel.SelectedItem.castInfoWithPics = castWithPicList needs to be set in the UI thread. Your donloadBitmap funciton uses a callback to process the response and that runs on a ThreadPool thread.

You can use Dispatcher.Invoke to force work to occur on a UI thread. Try putting this in your callback and putting the appropriate code in the commented section:

Application.Current.Dispatcher.BeginInvoke( 
  DispatcherPriority.Background,
  new Action(() => /* set selected item */));

For a better solution you can use async-await which restores the current SynchronizatonContext and yo do not need to use Dispatcher.Invoke. HttpClient provides an async friendly API you can use.

Here is a simple example setting a TextBox value.

 private async Task DownloadUrlAsync(string url)
 {
    using (HttpClient httpClient = new HttpClient())
    {
        textBox1.Text = await httpClient.GetStringAsync(url);
    }            
 }    
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61
  • 1
    It's a good guess. Hard to say for sure when the OP didn't provide the necessary details, like which timer object he is using or what the error message is. – Robert Harvey Jan 24 '15 at 04:38
-2

You have to freeze your object and the problem is solved. Here is a sample:

videoIcon = new BitmapImage();
videoIcon.BeginInit();
videoIcon.UriSource = new Uri(@"C:\Data\Image1.jpg");
videoIcon.DecodePixelWidth = 14;
videoIcon.EndInit();
videoIcon.Freeze();

Without the Freeze() I received the same error message you received. With Freeze() the problem is solved.
More info is here: https://msdn.microsoft.com/en-us/library/ms750509(v=vs.110).aspx
"A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot."

Edgar
  • 2,527
  • 2
  • 19
  • 40
  • I don't think this has anything to do with the OP's problem. – Robert Harvey Jan 24 '15 at 04:33
  • Robert Harvey, you seem to be an export and maybe you are right. But I had a similar problem and after considerable research I found the Freeze() solution which worked for me. Maybe Giri can try to freeze the images and let us know if this solves the problem. – Edgar Jan 24 '15 at 04:44