2013-04-16 123 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)); 
    });