【问题标题】:Loading image in thread with WPF使用 WPF 在线程中加载图像
【发布时间】:2009-11-15 21:51:12
【问题描述】:

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

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

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

谢谢!

【问题讨论】:

    标签: c# wpf image list multithreading


    【解决方案1】:

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

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

    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 对其进行扩展,因为它们无法从单独的线程中访问。

    这种技术可以扩展为产生固定数量的工作人员来加载图像并分解它们之间的工作,因此会发生多个图像下载,但同时下载的数量是有限的,因此您最终不会有数百个线程。

    【讨论】:

      【解决方案2】:

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

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

      BackgroundWorker 也使得取消后台任务非常方便。

      您可能还想查看UI virtualization

      【讨论】:

      • 只要确保 SynchronizationContext.Current 在 BackgroundWorker 启动时有意义。 (应该是 WPF 上下文,但需要牢记)。
      【解决方案3】:

      您可以使用this asynchronous observable collection 将您的数据源绑定到您的列表框,并且仍然可以在另一个线程中加载您的数据。

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

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

      【讨论】:

        【解决方案4】:

        谢谢大家!

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

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

        如果应用程序文件夹中没有包含以下代码的 your_application_name.exe.config 文件 - .NET 可能需要很长时间才能检测到在应用程序第一次访问网络时冻结应用程序的代理:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-08
          • 2012-08-14
          • 1970-01-01
          • 1970-01-01
          • 2013-04-08
          • 2011-11-22
          相关资源
          最近更新 更多