Wpf 为什么<;图像源=''&燃气轮机;这么慢,我该怎么办?

Wpf 为什么<;图像源=''&燃气轮机;这么慢,我该怎么办?,wpf,caching,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/wi

考虑下面的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>

以及背后的代码:

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;
    }
}
公共部分类主窗口:窗口
{
公共主窗口(){
初始化组件();
字符串[]URL=新字符串[1000];
对于(int i=0;i<1000;++i){
URL[i]=”http://graph.facebook.com/“+i+”/图片”;
}
this.DataContext=URL;
}
}
在一个非常合理的桌面和高速连接上,程序非常慢。正在尝试使用滚动条滚动。。。说中间,需要30秒。按“开始”和“结束”键将花费大量时间

这不是第一次仅将图像获取到缓存的问题。来回观看已经呈现的图片稍微快一些,但通常非常慢。看起来缓存中没有存储任何内容,关闭应用程序并重新启动,一切又变慢了

相当于HTML代码的速度非常快。第一次有些慢,但后来一切都很快

发生了什么事?元素是否使用任何缓存?列表是否对当前未显示的图像进行预取?有什么办法可以告诉它去做吗?我唯一的解决方案是自己管理位图对象,以及缓存和预取逻辑吗?如果是的话,我以前的工作可以合并吗

编辑(摘要):

  • @H.B.关闭虚拟化的答案将为您提供最佳结果。加载窗口后,将立即渲染整个列表框,并且不会重新计算任何图像
  • @Phil代码工作得很好,它提高了性能,尤其是在来回运行时
  • 如果没有任何附加代码,WPF将不会在调用之间缓存映像。WinINET缓存未使用。尽管请求在HTTP头中带有缓存指令,但WPF对此不做任何处理

  • listboxs
    默认情况下虚拟化项目,因此如果向下滚动,项目将动态创建。首先它需要下载图像,然后对其进行解码。如果您已滚动浏览所有图像,它们可能会被缓存,但
    列表框
    仍将重新创建
    图像
    控件,因此每次都需要对图像进行解码


    您可以通过将
    列表框上的设置为
    false
    来关闭虚拟化,然后立即加载所有内容,或者您可以将更改为
    回收
    ,然后是
    图像
    (并包含
    列表框项
    )创建后不会被丢弃。

    另一种方法是添加自己的图像缓存,这样图像只需下载一次

    使用我的示例,您可以将其放入构造函数中

    this.DataContext = new ViewModel();
    
    下面的类将存储url,然后在首次访问image属性时下载图像

    public class CachingImage
    {
        private readonly Uri _uri;
        public CachingImage(string uriString)
        {
            _uri = new Uri(uriString, UriKind.RelativeOrAbsolute);
        }
    
        private BitmapImage _image;
    
        public ImageSource Image
        {
            get
            {
                if (_image == null)
                {
                    _image = new BitmapImage(_uri);
                    _image.DownloadCompleted += (sender, args) => ((BitmapImage)sender).Freeze();
                }
    
                return _image;
            }
        }
    }
    
    这是视图模型

    public class ViewModel
    {
        public ViewModel()
        {
            Images = Enumerable.Range(1, 1000).Select(i => new CachingImage("http://graph.facebook.com/" + i + "/picture"));
        }
    
        public IEnumerable<CachingImage> Images { get; private set; }
        ...
    
    公共类视图模型
    {
    公共视图模型()
    {
    Images=Enumerable.Range(11000)。选择(i=>newcachingimage(“http://graph.facebook.com/“+i+”/图片”);
    }
    公共IEnumerable映像{get;private set;}
    ...
    
    当然,您需要稍微更改xaml

    <ListBox ItemsSource="{Binding}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Image Source="{Binding Image}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>