如何异步加载和显示图像 [英] How to load and show images asynchronously

查看:62
本文介绍了如何异步加载和显示图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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. The application should also know about EXIF data like ISO, aperture and so on, for which I use a DLL.

我已经定义了一个Photo类:

public class Photo {

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

我想在运行时存储在List<Photo>中的

.

that I want to store in a List<Photo> at runtime.

然后,我声明了带有图像控件和TextBlock的PhotoItem(XAML用户控件).对于创建的每个Photo,将创建​​一个将相应的Photo作为属性保存的PhotoItem:

I've then declared a PhotoItem (XAML User Control) with an Image control and a TextBlock on it. For every Photo created there will be one PhotoItem created that holds the corresponding Photo as a property:

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

通过Photo属性,PhotoItem知道要在哪里查找图像以及要显示的ISO等.

From this Photo property the PhotoItem knows where to look for the image and what ISO etc. to display.

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

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 PhotoItems to the window (still empty) and then run the metadata lookup and Image thumbnail loading for each of them. 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.

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

How would I make the PhotoItems update if metadata for the image is now available? 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. I know a bit about Bindings in XAML/WPF, but binding e.g. a TextBlock's text to the Photo.ISO variable would always display zero if the metadata was not gathered yet. In this case I would want to hide all the text detail on the PhotoItem.

另一方面,我还考虑过在PhotoItem中实现类似刷新"功能的方法,但是那样会重新加载图像,并且会花费很长时间(这可能是我最喜欢的WinForms方法) ,哈哈).

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).

任何人都可以给我一个实现此想法的想法吗?

Can anyone give me an idea of how to realize this?

提前谢谢!

推荐答案

让我们看一个没有UserControl的基本示例.

Let's take a look at a basic example without a UserControl.

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

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.

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

The class below also declares an Image property that holds an ImageSource derived object, which is loaded asynchronously.

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实例的集合:

The second part of the view model is a class that holds a collection of Photo instances:

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

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

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:

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

最后一部分是带有DataTemplate的可视化Photo的ListBox声明:

The last part is the declaration of the ListBox with a DataTemplate that visualizes a Photo:

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

现在您可以像这样在MainWindow的异步Loaded事件处理程序中填充Photos集合,例如:

Now you could fill the Photos collection for instance in an asynchronous Loaded event handler of the MainWindow like this:

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

这篇关于如何异步加载和显示图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆