2013-04-16 155 views
7

我想加載Gravatar-Images並將它們從後面的代碼設置爲WPF圖像控件。 因此,代碼看起來像使WPF圖像加載異步

imgGravatar.Source = GetGravatarImage(email); 

凡GetGravatarImage樣子:

BitmapImage bi = new BitmapImage(); 
bi.BeginInit(); 
bi.UriSource = new Uri(GravatarImage.GetURL("http://www.gravatar.com/avatar.php?gravatar_id=" + email) , UriKind.Absolute); 
bi.EndInit(); 
return bi; 

不幸的是,當網絡連接速度較慢該鎖定GUI。有沒有一種方法來分配圖像源,並讓它在後臺加載圖像而不會阻塞UI?

謝謝!

回答

0

你可能想看看這個問題:

Link

,我會建議啓動一個Asynchrone任務,並提出一個需要異步完成這項任務的代碼。

19

我建議你在XAML的imgGravatar上使用Binding。設置IsAsync = true就可以了,WPF將自動利用線程池中的線程來拉取圖片。你可以封裝解決邏輯組合成的IValueConverter並簡單綁定的電子郵件源

在XAML

<Window.Resouces> 
    <local:ImgConverter x:Key="imgConverter" /> 
</Window.Resource> 

... 


<Image x:Name="imgGravatar" 
     Source="{Binding Path=Email, 
         Converter={StaticResource imgConverter}, 
         IsAsync=true}" /> 
在代碼

public class ImgConverter : IValueConverter 
{ 
    public override object Convert(object value, ...) 
    { 
     if (value != null) 
     { 
      BitmapImage bi = new BitmapImage(); 
      bi.BeginInit(); 
      bi.UriSource = new Uri( 
       GravatarImage.GetURL(
        "http://www.gravatar.com/avatar.php?gravatar_id=" + 
         value.ToString()) , UriKind.Absolute 
       ); 
      bi.EndInit(); 
      return bi;     
     } 
     else 
     { 
      return null; 
     } 

    } 
} 
+1

的BitmapImage也有[與URI參數的構造函數(http://msdn.microsoft.com/en-us/library/ms602473.aspx),這將節省您的BeginInit在/ EndInit調用。 – Clemens

+0

+1不知道,很好的提示。我只是複製了作者的代碼,並沒有考慮如何優化實際下載,而是專注於以優雅的方式解決UI塊問題(當然,如果代碼不是異步的,它會阻止)。我認爲對於重複使用,轉換器也可以從綁定中獲得完整的url,使其更具可重用性,而不是電子郵件。我不喜歡使用'ThreadPool',因爲你必須隨時管理調度。這使得它對我來說不夠純粹的MVVM應用程序。 – JanW

+0

你能否給出一個解釋,爲什麼創建一個BitmapImage會在不被異步調用時阻塞。爲什麼然後它有'IsDownloading'屬性和'DownloadCompleted'事件。如果您只是將XAML中的Image控件的「Source」屬性設置爲引用Web上大圖像的URL,則UI將不會被阻止。相反,WPF會在後臺下載圖像,並在下載完成後立即顯示。 – Clemens

6

我不明白爲什麼你的代碼會阻止UI,因爲BitmapImage支持在後臺下載圖像數據。這就是爲什麼它有一個IsDownloading屬性和一個DownloadCompleted事件。

無論如何,下面的代碼顯示了一個直接的方式來完全下載和創建一個單獨的線程(從ThreadPool)的圖像。它使用一個WebClient實例下載整個圖像緩衝區,然後從該緩衝區創建一個BitmapImage。在創建BitmapImage後,它會調用Freeze以使其可從UI線程訪問。最後,它通過調用Dispatcher.BeginInvoke來在UI線程中分配Image控件的Source屬性。

ThreadPool.QueueUserWorkItem(
    o => 
    { 
     var webClient = new WebClient(); 
     var url = GravatarImage.GetURL("http://www.gravatar.com/avatar.php?gravatar_id=" + email); 
     var buffer = webClient.DownloadData(url); 
     var bitmapImage = new BitmapImage(); 

     using (var stream = new MemoryStream(buffer)) 
     { 
      bitmapImage.BeginInit(); 
      bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 
      bitmapImage.StreamSource = stream; 
      bitmapImage.EndInit(); 
      bitmapImage.Freeze(); 
     } 

     Dispatcher.BeginInvoke((Action)(() => image.Source = bitmapImage)); 
    });