在运行时将大图像显示在视图中

Bringing large image into view at runtime

问题来了。 我有一个视图,它应该显示一个图像和一些控件。 用户添加新图像,更改一些选项并单击 "finish"。 图片很大而且非常大 (400-1500 MB Tiff) 用户应该看到图像的预览,但如果它加载 10-15 秒甚至更长时间也没关系,他这次有工作。 图像通过 MVVM 模式绑定,如简单字符串(文件将始终在本地文件夹中)

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

问题是当用户尝试为加载时间添加文件时全部挂起。 我知道这种情况应该通过多线程解决 - 但不知道如何实现。

我尝试像这样从不同线程的视图中更新图像:

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
    {
        //...
    }
}

但无论如何

ImagePreview.Source = t_source;

在图像完全加载之前一切都挂起。

有没有办法在后台加载预览并显示它而不会出现那些可怕的挂起?

正如您已经提到的,您在加载图像时阻塞了 UI 线程。您可以使用 WriteableBitmap class 实例作为图像的来源。这将使您可以在后台线程或异步任务上加载图像。这是关于这个问题的快速指南(不是我的)。

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

异步加载图像的最简单方法可能是通过异步绑定。您根本不需要处理线程或任务。

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

可能的视图模型如下所示,您必须确保可以从后台线程调用 Image 属性 getter。

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

另一种选择是使用 priortybinding,将最高优先级绑定到完整图像,将较低优先级绑定到加载速度更快的预览图像。 MS 在此处记录了优先级绑定:

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