如何异步加载和显示图像

我是WPF的新手,但我已经做了很长一段时间的C#,我目前正在开发一个简单的窗口(Windows桌面),它应该可视化目录中的所有照片。 应用程序还应该知道我使用DLL的EXIF数据,如ISO,光圈等。

我已经定义了一个Photo类:

 public class Photo { public string FileName { get; set; } public int ISO { get; set; } ... } 

我想在运行时存储在List中。

然后我宣布了一个带有Image控件和TextBlock的PhotoItem (XAML用户控件)。 对于创建的每张Photo ,将创建一个PhotoItem ,将相应的Photo保存为属性:

 public partial class PhotoItem : UserControl { ... public Photo Photo { get; set; } ... } 

从这个Photo属性, PhotoItem知道在哪里查找图像以及要显示的ISO等。

现在我的问题。 因为如果用户选择目录,加载Image本身以及元数据将花费太长时间,我想首先将所有PhotoItem添加到窗口(仍为空),然后运行元数据查找和图像缩略图加载他们每个人。 当然最好是这些操作不会阻止UI线程,因此我目前正在使用一个Task来收集元数据,一个用于收集缩略图。

如果图像的元数据现在可用,我将如何更新PhotoItems? 基本上,您如何拥有一个存储所有数据的集中位置,任务可以向其提供更新,UI线程可以从中创建信息。 我对XAML / WPF中的Bindings Photo.ISO了解,但是如果尚未收集元数据, Photo.ISO TextBlock的文本绑定到Photo.ISO变量将始终显示为零。 在这种情况下,我想隐藏PhotoItem上的所有文本细节。

另一方面,我也考虑过在PhotoItem实现类似’Refresh’function的PhotoItem ,但这会重新加载图像并且需要很长时间(这可能是我最喜欢的WinForms方法,哈哈)。

任何人都可以告诉我如何实现这一点?

提前致谢!

让我们看一下没有UserControl的基本示例。

第一步是创建视图模型以启用数据绑定。 您可以使Photo类实现INotifyPropertyChanged接口,以在属性值更改时更新绑定。

下面的类还声明了一个Image属性,该属性包含一个异步加载的ImageSource派生对象。

 public class Photo : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public string FileName { get; set; } private string iso = string.Empty; public string ISO { get { return iso; } set { iso = value; NotifyPropertyChanged(nameof(ISO)); } } private ImageSource image; public ImageSource Image { get { return image; } set { image = value; NotifyPropertyChanged(nameof(Image)); } } public async Task Load() { Image = await Task.Run(() => { using (var fileStream = new FileStream( FileName, FileMode.Open, FileAccess.Read)) { return BitmapFrame.Create( fileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); } }); ISO = "1600"; } } 

视图模型的第二部分是一个包含Photo实例集合的类:

 public class ViewModel { public ObservableCollection Photos { get; } = new ObservableCollection(); } 

对于典型的数据绑定方案,您可以在代码或XAML中将此类的实例分配给Window的DataContext

    

最后一部分是ListBox的声明,其中包含一个可视化PhotoDataTemplate

             

现在你可以在MainWindow的异步Loaded事件处理程序中填充Photos集合,如下所示:

 private async void Window_Loaded(object sender, RoutedEventArgs e) { var vm = (ViewModel)DataContext; foreach (var file in Directory.EnumerateFiles(...)) { vm.Photos.Add(new Photo { FileName = file }); } foreach (var photo in vm.Photos) { await photo.Load(); } }