繁体   English   中英

使用WPF在线程中加载图像

[英]Loading image in thread with WPF

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

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

我读过我应该将图片加载到另一个线程中,但是我不知道应该在哪里以及如何做? 将所有图片直接加载到模型中是更好的方法(仅为此创建线程池-但问题是它实际上不是模型/模型视图的一部分),还是更好的方法是创建将直接更新模型的后台线程列出什么时候有数据?

谢谢 !

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

<Image ImageSource="{Binding propertyThatComputesImageSource, IsAsync=true}" />

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

Binding.IsAsync是用于十个或二十个图像的很好的解决方案,但是如果您有数百个图像并且加载延迟很长,则可能不是一个好的解决方案,因为您最终可能会拥有数百个线程。 在这种情况下,直接使用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可能会花费大量时间来检测代理,该代理会在首次访问网络时冻结应用程序:

<configuration>   
  <system.net>
    <defaultProxy enabled="false"/>   
  </system.net> 
</configuration>

暂无
暂无

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

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