2

My WPF MVVM application loads an image from the given URL asynchronously, through Webclient.DownloadFileAsync(url, fileLocation). That process goes fine and smooth, no freezes at all when downloading a picture. But the problem occurs when I present the image file to the user - an application becomes unresponsive.

After file is downloaded, I assign the image file to the BitmapImage:

public async void LoadFileToBitmapImage(string filePath)
    {
        _isDownloading = false;
        await FileToBitmapImage(filePath);
    }

public Task FileToBitmapImage(string filePath)
    {
        return Task.Run(() =>
        {
            var executableLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            var imageLocation = Path.Combine(executableLocation, filePath);
            var bi = new BitmapImage();
            bi.BeginInit();
            bi.UriSource = new Uri(imageLocation);
            bi.EndInit();
            bi.Freeze();
            Image = bi;
        });
    }

Image.cs:

private BitmapImage _image;
public BitmapImage Image
    {
        get => _image;
        set
        {
            _image = value;
            NotifyOfPropertyChange("Image");
        }
    }

XAML Image Binding:

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

The problem occurs when the image is downloaded and presenting it to the user. The bigger an image, the more time it takes to present an image to the user and the more time an application is unresponsive.

I tried clicking pause at that very time when the application freezes to check threads and get the following info and unfortunately it doesn't provide me with any information.

Any help will be much appreciated!

Edit

Worth noting that application becomes unresponsive after PropertyChanged event is raised, not before. Maybe it's something to do with rendering an image to the UI?

  • You can't assign the image on any thread other than the dispatcher thread. The UI freezes because of that. You need to marshall the assignment back to the dispatcher. – Enigmativity Sep 13 '18 at 11:43
  • @Peregrine I tried your solution, but no luck. Still freezes with with both 5000x5000 and 20000x10000 pictures. Worth noting that both my code and yours freezes after PropertyChanged event is raised, not before. Maybe it's something to do with rendering an image to the UI? – Edward Ablekimov Sep 13 '18 at 13:12
  • 2
    Assuming that your monitor doesn't support a 20000x10000 resolution, I suggest that you generate a thumbnail version of each image of an appropriate size for such usage, otherwise the image control is doing a lot of work for the scaling. – Peregrine Sep 13 '18 at 13:19
  • @Peregrine Great idea. Will try to resize it to the adequate size and then load to ui. – Edward Ablekimov Sep 13 '18 at 13:51
  • Making the bitmap smaller is certainly something worth doing. But as far as your actual question goes, you should know that the bitmap doesn't actually get loaded into the object until first needed, unless you change the cache settings. See marked duplicate. If that does not address the problem, post a new question in which you've provided a good [mcve] that actually reproduces the problem. – Peter Duniho May 26 '20 at 02:20

2 Answers2

0

first, if you save the image, change the binding to a string/uri directly, no BitmapImage, no nned to create it, Wpf handle that for you public BitmapImage Image ===> public Uri Image

and remove FileToBitmapImage.

GCamel
  • 612
  • 4
  • 8
  • Thanks for pointing that out, I've fixed that. Though the problem persists. – Edward Ablekimov Sep 13 '18 at 12:31
  • how big is your image ? – GCamel Sep 13 '18 at 12:32
  • I tried using images of different sizes. From 2048x1024 to 20000x10000. Lag on the 2k image persists, but it's too little to be aware of. Situtation changes when there's a very large file. – Edward Ablekimov Sep 13 '18 at 13:13
  • too big, there is no way...or have to load them before like here - https://github.com/TheCamel/CBR/blob/master/CBR.Core/Models/Book/Page.cs - then change back your proerty to bitmapImage. Is it just an image viewer ? or something more complicated ? – GCamel Sep 13 '18 at 13:31
-1

I spent a few days to find a simple solution to this problem. I needed to display over a hundred images in high quality without UI freezing.

I tried various modifications of the binding and so on in the end only the creation of the Image control through the code and set of the Source property worked before Image appeared in the tree of interface elements.

In XAML only empty ContentControl:

    <ContentControl x:Name="ImageContent"/>

In code:

    static readonly ConcurrentExclusiveSchedulerPair _pair = new  ConcurrentExclusiveSchedulerPair();

    // Works for very big images
    public void LoadImage(Uri imageUri)
    {
        var image = new System.Windows.Controls.Image(); // On UI thread
        RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);

        Task.Factory.StartNew(() =>
        {
            var source = new BitmapImage(imageUri); // load ImageSource

            Dispatcher.RunOnMainThread(() =>
            {
                image.Source = source; // Set source while not in UI

                // Add image in UI tree
                ImageContent.Content = image; // ImageContent is empty ContentControl
                ImageContent.InvalidateVisual();
            });
        }, default, TaskCreationOptions.LongRunning, _pair.ExclusiveScheduler);
    }

Works better with loading image with CacheOption OnLoad.

    public static ImageSource BitmapFromUri(Uri source)
    {
        if (source == null)
            return new BitmapImage(source);

        using (var fs = new FileStream(source.LocalPath, FileMode.Open))
        {
            var bitmap = new BitmapImage();
            bitmap.BeginInit();
            bitmap.StreamSource = fs;
            bitmap.CacheOption = BitmapCacheOption.OnLoad;
            bitmap.EndInit();
            bitmap.Freeze();
            return bitmap;
        }
    }
elamaunt
  • 19
  • 1
  • 'Load a large BitmapImage asynchronously' is not an answer. The bug is not with loading but with displaying in wpf. – elamaunt May 26 '20 at 09:32