使用WPF在线程中加载图像

我正在尝试制作一个显示来自互联网图片的列表框。 这些项目是通过将itemsource绑定到包含图像URL和其他一些属性(title,desc等等)的模型来提供的。

不幸的是,列表加载速度非常慢,因为WPF在显示列表之前尝试从Web下载所有图片,并使应用程序冻结15到25秒。

我已经读过我应该在另一个post中加载图片,但我不知道应该在哪里做以及如何做? 是否更好地直接在模型中加载所有图片(通过仅为此创建一个线程池 – 但问题是它不是模型/模型视图的真正部分)或者更好地创建一个将直接更新的后台线程列表何时有数据?

谢谢 !

简单的方法就是像这样设置Binding.IsAsync属性:

  

每次访问propertyThatComputesImageSource都将从ThreadPool线程完成。 如果线程使用ImageCacheOptions.OnLoad创建图像,它将阻塞,直到加载图像。 因此,UI将立即启动,图像将在后台加载,并在可用时显示。

Binding.IsAsync是10或20个图像的一个很好的解决方案,但如果你有数百个图像并且加载延迟很长,那么它可能不是一个好的解决方案,因为你最终可能会有数百个线程。 在这种情况下,直接使用ThreadPool将数据加载到数据绑定之外:

 ThreadPool.QueueUserWorkItem((state) => { foreach(var model in _models.ToArray()) model.ImageSource = LoadOneImage(model.ImageUrl); }); 

如果模型的属性是DependencyProperty,则可能需要使用Dispatcher.Invoke或两个扩展,因为无法从单独的线程访问它们。

这种技术可以扩展为产生固定数量的工作者来加载图像并打破它们之间的工作,从而发生多个图像下载,但同时下载的数量是有限的,所以你最终不会有数百个线程。

一种非常简单的方法是在视图模型中使用System.ComponentModel.BackgroundWorker ( 更多信息 )。 这是一个简单的例子:

 using (BackgroundWorker bg = new BackgroundWorker()) { bg.DoWork += (sender, args) => FetchImages(viewModelObjectsNeedingImages); bg.RunWorkerAsync(); } 

BackgroundWorker还可以非常方便地取消后台任务。

您可能还想查看UI虚拟化 。

您可以使用此异步可观察集合将数据源绑定到ListBox,并仍然能够在另一个线程中加载数据。

有关如何编写此类线程的示例,请查看BackgroundWorker文档。

此外,您可能需要考虑延迟加载图像,即只加载可见的图像,并随时加载更多图像。 这样,您可以获得两个好处:不必在获取线程中的图像时阻止UI,并且您可以重复使用您的集合,一次只能保存一些图像,从而防止在内存中填充大量图像一旦你计划展示,比如说,几千。 请查看此处了解如何实施此类虚拟化的详细信息。

感谢大家 !

所有解决方案都应该工作:)在我的情况下,在ListBoxItem的图像上使用IsAsync是足够好的(最多有50个项目)。 实际上,它并没有从占用太多时间的网络中检索图像!

不幸的是我的问题出在其他地方……这与.NET 3.5中的代理检测错误有关,导致应用程序加载速度非常慢:

如果应用程序文件夹中没有任何带有以下代码的your_application_name.exe.config文件 – .NET可能需要花费大量时间来检测在第一次访问网络时冻结应用程序的代理: