4

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!

Sentropie
  • 249
  • 1
  • 4
  • 14

3 Answers3

2

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

Community
  • 1
  • 1
Jason Coyne
  • 6,509
  • 8
  • 40
  • 70
1

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.

General Grey
  • 3,598
  • 2
  • 25
  • 32
  • I suppose I have to use the `BeginInvoke` method of `this.Dispatcher`, right? Actually, I already have an ObservableCollection to which I bind the ListView's item source (updated original post). But If I do `imageCollectionVM.Images.Add(image);` inside the Action, I get the "DependencySource and DependencyObject should be in the same thread" error message. – Sentropie Jun 12 '12 at 18:34
  • If you are trying to use an ObservableCollection, see the last link I posted in my answer, it has a BindingList<> which can be updated from a background thread – Jason Coyne Jun 12 '12 at 18:44
0

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; } }

Mare Infinitus
  • 8,024
  • 8
  • 64
  • 113