为什么<Image Source ='...'>这么慢,我该怎么办呢?

Uri*_*Uri 7 wpf caching

考虑以下示例XAML文件,其中显示Facebook的前1000人,从markz开始作为第4人.请注意,这只是一个示例.任何具有1000个元素的窗口,无论您如何构建它,都是一个很好的演示.

<Window x:Class="SO.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:clr="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <ListBox ItemsSource="{Binding}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Image Source="{Binding}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>
Run Code Online (Sandbox Code Playgroud)

而背后的代码:

public partial class MainWindow : Window
{
    public MainWindow() {
        InitializeComponent();
        string[] urls = new string[1000];
        for (int i = 0; i < 1000; ++i) {
            urls[i] = "http://graph.facebook.com/" + i + "/picture";
        }
        this.DataContext = urls;
    }
}
Run Code Online (Sandbox Code Playgroud)

在非常合理的桌面和高速连接上,程序非常慢.尝试使用ScrollBar滚动...说到中间,需要30秒.点击"主页"和"结束"键将花费大量时间.

这不是第一次只能获取图像到缓存的问题.来回看看已经呈现的图片有点快,但通常很慢.似乎没有任何内容存储在缓存中,关闭应用程序并重新启动它,一切都很慢.

等效的HTML代码快速消失.有些慢,第一次,但后来一切都很快.

到底是怎么回事?元素是否使用任何缓存?列表是否对当前未显示的图像进行预取?无论如何要告诉它吗?真的,我唯一的解决方案是自己管理Bitmap对象,以及缓存和预取逻辑吗?如果是这样,我可以合并以前的任何工作?

编辑(摘要):

  1. 关闭虚拟化的@HB答案会给你最好的结果.窗口加载后立即呈现整个列表框,并且不重新计算图像
  2. @Phil代码效果很好,它可以提高性能,特别是在来回时.
  3. 没有任何其他代码,WPF将不会在调用之间缓存图像.使用WinINET缓存.虽然HTTP Header中的请求附带了Cache指令,但WPF对它没有任何作用.

H.B*_*.B. 6

ListBoxes默认情况下虚拟化项目,因此如果向下滚动,则会动态创建项目.首先,它需要下载图像,然后对其进行解码.如果您滚动浏览了所有图像,它们可能会被缓存,但ListBox仍然会重新创建Image控件,因此每次都需要再次解码图像.

您可以通过将VirtualizingStackPanel.IsVirtualizing附加属性设置为falseon 来关闭虚拟化,ListBox然后立即加载所有内容,或者您​​可以更改VirtualizationModeto Recycling,然后Images(和包含ListBoxItems)一旦创建就不会被丢弃.