0

Here is the problem. I have a view, that should display an image and some controls. User add new images, changes some options and click "finish". Images are large and very large (400-1500 MB Tiff) User should see the preview of image, but it is ok if it loading for 10-15 sec or even more, he have a job for this time. Image is binding through MVVM pattern like simple string (file will be always in local folder)

<Image Name="ImagePreview" Source="{Binding SFilePathForPreview,
         FallbackValue={StaticResource DefaultImage},
         TargetNullValue={StaticResource DefaultImage}}"
         HorizontalAlignment="Center" Width="200" Height="200"
         VerticalAlignment="Center" />

Problem is that all is hangs when user try to add a file for loading time. I understand that this case should be solved through multithreading - but have no idea how to implement this.

I tryed to update image from view in different thread like this:

Thread newThread = new Thread(LazyLoad);
newThread.Name = "LazyLoad";
newThread.Start(SFilePathForPreview);

public void LazyLoad(object SFilePath)
{            
    try
    {
        string path = (string)SFilePath;

        BitmapImage t_source = new BitmapImage();

        t_source.BeginInit();
        t_source.UriSource = new Uri(path);
        t_source.DecodePixelWidth = 200;
        t_source.EndInit();

        t_source.Freeze();
        this.Dispatcher.Invoke(new Action(delegate
        {
            ImagePreview.Source = t_source;
        }));
    }
    catch
    {
        //...
    }
}

But anyway at point

ImagePreview.Source = t_source;

everything hangs up until image fully loaded.

Is there a way to load a preview in the background and show it without those terrible hangs?

Hossein Golshani
  • 1,847
  • 5
  • 16
  • 27
Bruce
  • 3
  • 2
  • Kindly refer https://stackoverflow.com/questions/25070178/lazy-loading-images-in-virtualized-listbox. – Sham Sep 30 '18 at 14:34
  • Possible duplicate of [Async Program still freezing up the UI](https://stackoverflow.com/questions/52172918/async-program-still-freezing-up-the-ui) – Peregrine Oct 01 '18 at 08:43

3 Answers3

0

As you already mentioned, you are blocking the UI thread with the image load. You can use a WriteableBitmap class instance as the source for your Image. This will let you load the image on a background thread or async task. Here is a quick guide (not mine) on the issue.

https://www.i-programmer.info/programming/wpf-workings/527-writeablebitmap.html

Dan Barrett
  • 225
  • 1
  • 6
  • Please note that you can load instances of *all* classes derived from ImageSource in a background thread, not only WriteableBitmap. Just make sure their Freeze method is called. – Clemens Sep 30 '18 at 15:16
0

The probably most simple way of asynchronously loading an image is via an asynchronous Binding. You would not have to deal with Threads or Tasks at all.

<Image Source="{Binding Image, IsAsync=True}"/>

A possible view model could look like shown below, where you must make sure that the Image property getter can be called from a background thread.

public class ViewModel : ViewModelBase
{
    private string imagePath;
    private BitmapImage image;

    public string ImagePath
    {
        get { return imagePath; }
        set
        {
            imagePath = value;
            image = null;
            OnPropertyChanged(nameof(ImagePath));
            OnPropertyChanged(nameof(Image));
        }
    }

    public BitmapImage Image
    {
        get
        {
            lock (this)
            {
                if (image == null &&
                    !string.IsNullOrEmpty(imagePath) &&
                    File.Exists(imagePath))
                {
                    using (var stream = File.OpenRead(imagePath))
                    {
                        image = new BitmapImage();
                        image.BeginInit();
                        image.CacheOption = BitmapCacheOption.OnLoad;
                        image.DecodePixelWidth = 200;
                        image.StreamSource = stream;
                        image.EndInit();
                        image.Freeze();
                    }
                }
            }

            return image;
        }
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
-1

Another option would be using priortybinding with the highest priorty to the full image and a lower priority to the faster-loading preview image. MS has documented priority binding here:

https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-prioritybinding

Jan
  • 3,825
  • 3
  • 31
  • 51