简体   繁体   English

如何异步加载和显示图像

[英]How to load and show images asynchronously

I'm new to WPF but I've done C# for quite some time now and I am currently developing a simple window (Windows Desktop) that should visualize all photos in a directory. 我是WPF的新手,但是我已经做了C#相当一段时间了,而且我目前正在开发一个简单的窗口(Windows桌面),该窗口应该可视化目录中的所有照片。 The application should also know about EXIF data like ISO, aperture and so on, for which I use a DLL. 应用程序还应该了解EXIF数据,例如ISO,光圈等,为此我使用了DLL。

I have defined a Photo class: 我定义了一个Photo类:

public class Photo {

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

that I want to store in a List<Photo> at runtime. 我想在运行时存储在List<Photo>中。

I've then declared a PhotoItem (XAML User Control) with an Image control and a TextBlock on it. 然后,我声明了一个带有图像控件和一个TextBlock的PhotoItem (XAML用户控件)。 For every Photo created there will be one PhotoItem created that holds the corresponding Photo as a property: 对于创建的每张Photo ,都会创建一个将相应Photo作为属性保存的PhotoItem

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

From this Photo property the PhotoItem knows where to look for the image and what ISO etc. to display. 通过此Photo属性, PhotoItem知道在哪里查找图像以及要显示的ISO等。

Now to my problem. 现在到我的问题。 Because it would take way too long to load the Image itself as well as the metadata already if the user selects the directory, I want to first add all the PhotoItem s to the window (still empty) and then run the metadata lookup and Image thumbnail loading for each of them. 因为如果用户选择目录,加载图像本身以及元数据已经花费太长时间,所以我想先将所有PhotoItem添加到窗口中(仍然为空),然后运行元数据查找和图像缩略图为他们每个人加载。 Of course it would be best if these operations don't block the UI thread, hence I'm currently using one Task for gathering metadata and one for gathering the thumbnail. 当然,最好是这些操作不要阻塞UI线程,因此我目前正在使用一个Task来收集元数据,并使用一个Task来收集缩略图。

How would I make the PhotoItems update if metadata for the image is now available? 如果图像的元数据现在可用,我将如何更新PhotoItems? Basically how can you have one centralized location where all data is stored, to which Tasks can provide updates and from which the UI thread can build information. 基本上,您如何才能在一个集中的位置存储所有数据,Task可以向其提供更新,UI线程可以从该位置构建信息。 I know a bit about Bindings in XAML/WPF, but binding eg a TextBlock's text to the Photo.ISO variable would always display zero if the metadata was not gathered yet. 我对XAML / WPF中的绑定Photo.ISO了解,但是如果尚未收集元数据,例如将TextBlock的文本绑定到Photo.ISO变量将始终显示零。 In this case I would want to hide all the text detail on the PhotoItem . 在这种情况下,我想在PhotoItem上隐藏所有文本细节。

On the other hand I've also thought about implementing something like a 'Refresh' function inside the PhotoItem , but that would reload the image and would take a long time (this was probably my favorite WinForms way to do it, haha). 另一方面,我还考虑过在PhotoItem实现类似“刷新”功能的PhotoItem ,但是那样会重新加载图像,并且会花费很长时间(这可能是我最喜欢的WinForms方法,哈哈)。

Can anyone give me an idea of how to realize this? 谁能给我一个实现此想法的想法?

Thanks in advance! 提前致谢!

Let's take a look at a basic example without a UserControl. 让我们看一个没有UserControl的基本示例。

The first step is to create a view model to enable data binding. 第一步是创建一个视图模型以启用数据绑定。 You would make the Photo class implement the INotifyPropertyChanged interface to update bindings when property values change. 您将使Photo类实现INotifyPropertyChanged接口,以在属性值更改时更新绑定。

The class below also declares an Image property that holds an ImageSource derived object, which is loaded asynchronously. 下面的类还声明了一个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";
    }
}

The second part of the view model is a class that holds a collection of Photo instances: 视图模型的第二部分是一个类,其中包含Photo实例的集合:

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

For the typical data binding scenario, you would assign an instance of this class to the DataContext of your Window, either in code or in XAML: 对于典型的数据绑定方案,您可以通过代码或XAML将此类的实例分配给Window的DataContext

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

The last part is the declaration of the ListBox with a DataTemplate that visualizes a Photo : 最后一部分是带有可视化PhotoDataTemplate的ListBox的声明:

<ListBox ItemsSource="{Binding Photos}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Image Source="{Binding Image}" Width="100" Height="100"/>
                <StackPanel>
                    <TextBlock Text="{Binding ISO, StringFormat=ISO: {0}}"/>
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Now you could fill the Photos collection for instance in an asynchronous Loaded event handler of the MainWindow like this: 现在,您可以像这样在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();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM