Daniel
Daniel

Reputation: 1124

Using Task.Factory in Image class

I am trying to make images load in different thread but image is never updated.

public class MyImage : System.Windows.Controls.Image
{
  public MyImage()
  {
    this.Loaded += new RoutedEventHandler(MyImage_Loaded);
  }
  void MyImage_Loaded(object sender, RoutedEventArgs e)
  {
    //mypath=@"c:\test.jpg";
    var t = Task<ImageSource>.Factory.StartNew(() => GetImage(mypath));
    Source = t.Result;
  }

Error on Source

Following works but it is the UI thread:

Source = GetImage(mypath);

I tried the same with BackgroundWorker but the result is the same. Is it possible to do it like this without MVVM?

Upvotes: 0

Views: 707

Answers (2)

dkozl
dkozl

Reputation: 33384

Because you create ImageSource on different thread then the one you want to use it on you get

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

exception and to solve your issue you should call Freeze() on your ImageSource. However even though you load image on different thread your code blocks UI thread until it's ready:

var t = Task<ImageSource>.Factory.StartNew(() => GetImage(mypath));
Source = t.Result; //this line blocks UI thread until Result is ready

If you want to avoid that change it to:

Task.Factory.StartNew(() =>
{
   var source = GetImage(mypath);
   source.Freeze();
   this.Dispatcher.Invoke(() => this.Source = source);
});

This should load image on different thread and update Source when it's ready without blocking UI thread

Upvotes: 3

G. Stoynev
G. Stoynev

Reputation: 7791

Try returning a byte[] or a stream from your GetImage (or from its result), then use Dispatcher.Invoke to call a helper on the UI thread that reconstructs the image from the byte[] or stream and updates MyImage's Source.

This is my working POC. You want to examine it for proper disposal of the stream and so on. (Note: disp is the Dispatcher to "post" to the UI)

Task.Factory.StartNew(() =>
                {
                    var result = GetImage();
                    var bitmap = new BitmapImage();
                    var stream = result;    
                    bitmap.BeginInit();
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;
                    bitmap.StreamSource = stream;

                    bitmap.EndInit();
                    bitmap.Freeze();
                    stream.Dispose();
                    disp.Invoke(new Action(()=> MyImage.Source = bitmap));
                });

And the GetImage():

    private MemoryStream GetImage()
    {
        System.Threading.Thread.Sleep(35000);
        var filePath = @"C:\temp\2013-01-08-235341.jpg";
        MemoryStream memoryStream = null;
        if (File.Exists(filePath))
        {
            memoryStream = new MemoryStream();

            byte[] fileBytes = File.ReadAllBytes(filePath);
            memoryStream.Write(fileBytes, 0, fileBytes.Length);
            memoryStream.Position = 0;
        }

        return memoryStream;
    }

Upvotes: 0

Related Questions