0

I'm trying to load a BitmapImage in a background thread and then set the (WPF) image source to this BitmapImage.

I'm currently trying something like this:

public void LoadPicture()
{
    Uri filePath = new Uri(Directory.GetCurrentDirectory() + "/" + picture.PictureCacheLocation);
    if (Visible && !loaded)
    {
        if (File.Exists(filePath.AbsolutePath) && picture.DownloadComplete)
        {
            BitmapImage bitmapImage = LoadImage(filePath.AbsolutePath);
            image.Dispatcher.Invoke(new Action<BitmapImage>((btm) => image.Source = btm), bitmapImage);

            loaded = true;
        }
    }
}

But I get an InvalidOperationException because the background thread owns the BitmapImage. Is there a way to give the ownership of the BitmapImage or a copy to the UI Thread?

I need to load the bitmap image in the background thread because it may block for a long time.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
DeGandalf
  • 11
  • 3
  • 3
    Use `async/await` instead of fiddling with `Invoke`. This hasn't been necessary since 2012. Besides, `bitmapImage` would be owned by a background thread only if `LoadPicture` was called by a background thread, or `LoadImage` used a thread. Post the missing code – Panagiotis Kanavos Jun 03 '21 at 10:00
  • Besides, you can create a `BitmapImage` that loads data from a file directly with `var bitmapImage=new BitmapImage(filePath);`. Use `Path.Combine` instead of concatenating the strings directly – Panagiotis Kanavos Jun 03 '21 at 10:06
  • Since the code is using the *current* folder, there's no need to concatenate. `var filePath=new Uri(Path.GetAbsolutePath(picture.PictureCacheLocation));` – Panagiotis Kanavos Jun 03 '21 at 10:22
  • Something similar to [this](https://stackoverflow.com/a/12826608/1136211) may be useful. Freeze and return the BitmapImage from the Task action – Clemens Jun 03 '21 at 10:23
  • Also, `picture.DownloadComplete` seems to indicate that you first download an image, then write it to file, and then create a BitmapImage from that file. Be aware that you can create a BitmapImage directly from a remote URL. – Clemens Jun 03 '21 at 11:01
  • I still don't perfectly understand how async/await works, so I could be wrong. But in my case I'm calling this code from an event which is (I at least think) not the UI Thread, so I need to call Dispatcher.Invoke to execute it in the UI thread, right? Also thanks for the Path.GetAbsolutePath , though I could only find Path.GetFullPath. And I can not load the bitmap directly, as there is a bug where this blocks the file even after setting the source=null And I can't use a direct uri, as I need authentication to access the image. Still thanks for the answer :) – DeGandalf Jun 04 '21 at 11:41

1 Answers1

0

All work with the DependencyObject should happen in one thread.
Except for frozen instances of Frеezable.

It also makes no sense (in this case) to pass a parameter to Invoke - it is better to use a lambda.

There is also the danger of the Dispatcher self-locking, since you are not checking the flow.

    public void LoadPicture()
    {
        Uri filePath = new Uri(Directory.GetCurrentDirectory() + "/" + picture.PictureCacheLocation);
        if (Visible && !loaded)
        {
            if (File.Exists(filePath.AbsolutePath) && picture.DownloadComplete)
            {
                BitmapImage bitmapImage = LoadImage(filePath.AbsolutePath);

                bitmapImage.Freeze();

                if (image.Dispatcher.CheckAccess())
                    image.Source = bitmapImage;
                else
                    image.Dispatcher.Invoke(new Action(() => image.Source = bitmapImage));

                loaded = true;
            }
        }
    }

Objects of type Frеezable do not always allow themselves to be frozen.
But your code is not enough to identify possible problems.
If you fail to freeze, then show how the LoadImage (Uri) method is implemented.

EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • `image.Dispatcher.CheckAccess()` makes no sense when you know you are in a thread other than the UI thread. – Clemens Jun 03 '21 at 10:28
  • I agree. But from the shown code, I could not draw a similar conclusion. TС writes that he had a problem when calling a method from a background thread. But he didn't write that he always calls this method only from a background thread. – EldHasp Jun 03 '21 at 10:35
  • I don't really need this anymore, as I found a bug why LoadImage blocks so long, so I could load it in the Invoke. But this is still a very usefull answer, thanks. – DeGandalf Jun 04 '21 at 11:31
  • Also can you elaborate a bit how the access of the dispatcker works and when it self locks? This method is sometimes called from the ui thread, but it should never be called from another Dispatcher.Invoke – DeGandalf Jun 04 '21 at 11:54