图像控件加载BitmapImage时,UI冻结

时间:2018-09-13 11:11:55

标签: c# wpf multithreading mvvm

我的WPF MVVM应用程序通过Webclient.DownloadFileAsync(url, fileLocation)从给定URL异步加载图像。该过程进行得很顺利,下载图片时完全不会冻结。但是,当我向用户展示图像文件时,就会出现问题-应用程序变得无响应。

下载文件后,我将图像文件分配给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 Source="{Binding Image, IsAsync=True}" Margin="3"/>

下载图像并将其呈现给用户时会出现问题。图像越大,向用户呈现图像的时间就越多,并且应用程序无响应的时间也就越多。

我当时尝试在应用程序冻结以检查线程并获取the following info时单击“暂停”,但不幸的是它没有向我提供任何信息。

任何帮助将不胜感激!

修改

值得一提的是,在引发PropertyChanged事件之后而不是在之后,应用程序变得无响应。也许与向UI渲染图像有关?

1 个答案:

答案 0 :(得分:-1)

我花了几天时间找到解决此问题的简单方法。我需要在不冻结UI的情况下高质量显示一百多个图像。

我尝试了对绑定的各种修改,等等,最后,只有在Image出现在界面元素树中之前,通过代码和Source属性集的创建,才能创建Image控件。

在XAML中,只有空的ContentControl:

    <ContentControl x:Name="ImageContent"/>

在代码中:

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

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