2009-11-15 92 views
1

我正在試圖製作一個顯示來自Internet的圖片的列表框。通過將itemsource綁定到包含圖像URL和其他一些屬性(標題,desc等)的模型來提供項目。在WPF線程中加載圖片

不幸的是,該列表加載非常緩慢,因爲WPF試圖在顯示列表之前從網上下載所有圖片,並使應用程序凍結15到25秒。

我讀過,我應該加載在另一個線程中的圖片,但我不知道我應該怎麼做,以及如何?直接在模型中加載所有圖片(通過僅爲此創建線程池 - 但問題在於它不是模型/模型視圖的一部分),還是更好地創建後臺線程,以便直接更新列出何時有數據?

謝謝!

回答

0

一個非常簡單的方法是在視圖模型中使用System.ComponentModel.BackgroundWorkermore info)。這裏有一個簡單的例子:

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

BackgroundWorker也使得取消後臺任務非常方便。你可能也想看看UI virtualization

+0

只要確保SynchronizationContext.Current是有意義的在BackgroundWorker啓動時。 (應該是WPF的上下文,但只是要記住)。 – 2009-11-16 02:38:34

0

您可以使用this asynchronous observable collection將您的數據源綁定到您的ListBox,並且仍然能夠將數據加載到另一個線程中。

有關如何編寫此類線程的示例,請參閱BackgroundWorker文檔。

此外,您可能需要考慮延遲加載您的圖片,也就是說,只加載可見的圖片以及任何時候的圖片。這樣,您可以獲得兩個好處:在線程中獲取圖像時不必阻塞UI,並且可以重複使用收藏夾一次只保存幾張圖像,從而防止在大量圖像中填充內存有一次,如果你計劃展示的話,就是幾千。請查看here以瞭解如何實現此類虛擬化的詳細信息。

5

最簡單的辦法是剛剛剛剛設置的Binding.IsAsync屬性是這樣的:

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

propertyThatComputesImageSource每個訪問將從一個線程池的線程來完成。如果線程使用ImageCacheOptions.OnLoad創建圖像,它將阻塞直到圖像加載。因此UI將立即啓動,圖像將在後臺加載並在可用時顯示。

Binding.IsAsync對於十個或二十個圖像來說是一個很好的解決方案,但如果您有數百個圖像並且加載延遲很長,可能不是一個好的解決方案,因爲最終可能會有數百個線程。在這種情況下,完全通過直接使用線程池加載數據綁定的外側的圖像:

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

這可能需要用一個Dispatcher.Invoke或兩個延長如果模型的屬性是的DependencyProperty,因爲它們不能被訪問從一個單獨的線程。

這種技術可以擴展到產生固定數量的工作人員來加載圖像和打破他們之間的工作,因此多個圖像下載正在發生,但同時下載的數量是有限的,所以你不會結束數百線程。

0

感謝大家!

所有的解決方案應該工作:)在我的情況下使用ListBoxItem的圖像IsAsync是足夠好的(最多有50個項目)。事實上,它不會從網絡中檢索花費太多時間的圖像!

不幸的是我的問題是別的地方......它涉及到:在.NET 3.5的代理檢測這會導致應用程序非常緩慢加載錯誤:

如果沒有任何your_application_name.exe.config文件在下面的代碼的應用程序文件夾 - .NET會佔用大量的時間來檢測其凍結申請第一次,它訪問網絡代理:

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