Sentropie
Sentropie

Reputation: 259

Loading Images asynchronous in C#

I'm working on a C# WPF application which loads a lot of images and displays it as thumbnails. I'd like to do it in a multi-threaded way. Therefore I tried to implement a BackgroundWorker.

The code of the BackgroundWorker's DoWork():

string[] files = e.Argument as string[];
foreach (string file in files)
{
    ImageModel image = new ImageModel();
    image.FilePath = file;
    _importWorker.ReportProgress(1, image);
    _imageCollectionVM.Images.Add(image); // also tried this one in ReportProgress()
}

In my XAML code I bind to the BitmapImage property of ImageModel. (AsyncState=True doesn't help.) Here I get this error: "DependencySource" and "DependencyObject" have to be in the same thread.

<Image Source="{Binding BitmapImage}" />

If I comment this out, the image seems to be imported but I cannot access it, e.g. by selecting it in a ListView. In its SelectionChanged it says then that this object is possessed by another thread.

How do I solve these problems? Thanks in advance!

Upvotes: 4

Views: 7923

Answers (3)

Jason Coyne
Jason Coyne

Reputation: 6636

You must marshall the update to the GUI to the main thread. Basically you can only multi-thread the loading of the images from disk, but the actual update of the GUI must be done single threaded.

There are many ways to do this, and many questions on stackoverflow address it. Here are a few to get you started

Update UI from background Thread

Update BindingList<> from a background Thread?

Is it evil to update a pictureBox from a background C# thread?

How to use a BindingList for this

How do you correctly update a databound datagridview from a background thread

Upvotes: 2

Mare Infinitus
Mare Infinitus

Reputation: 8162

In a similar situation i did the following:

  • create an ImageProvider class which actually does the image loading work
  • let the viewmodel of the image Bind to an ImageSource in my ItemViewModel
  • let this ImageSource be Lazy

    // // pseudocode here

    Lazy lazy = new Lazy(imageProvider.LoadImage(this.imagePath))

    // in the ImageViewModel ...

    imageSource { get { return lazy.Value; } }

Upvotes: 0

General Grey
General Grey

Reputation: 3688

BackGround Worker is good imo for large tasks, but if it something simple like what you are doing I would prefer to do it like this

start with a list of Image

List<Image> Images =new List<Image>();

then run this

Task.Factory.StartNew( () =>
{
    string[] files = e.Argument as string[];
    foreach (string file in files)
    {
        ImageModel image = new ImageModel();
        image.FilePath = file;
       // _importWorker.ReportProgress(1, image);

      this.BeginInvoke( new Action(() =>
         {
            Images.Add(image);
         }));
     }
 });

No guarantee I have the right number of brackets in that code.

Upvotes: 1

Related Questions