Make WPF Image load async

前端 未结 2 1148
[愿得一人]
[愿得一人] 2020-11-27 17:58

I would like to load Gravatar-Images and set them from code behind to a WPF Image-Control. So the code looks like

imgGravatar.Source = GetGravatarImage(emai         


        
相关标签:
2条回答
  • 2020-11-27 18:55

    I can't see why your code would block the UI, as BitmapImage supports downloading image data in the background. That's why it has an IsDownloading property and a DownloadCompleted event.

    Anyway, the following code shows a straightforward way to download and create the image entirely in a separate thread (from the ThreadPool). It uses a WebClient instance to download the whole image buffer, before creating a BitmapImage from that buffer. After the BitmapImage is created it calls Freeze to make it accessible from the UI thread. Finally it assigns the Image control's Source property in the UI thread by means of a Dispatcher.BeginInvoke call.

    ThreadPool.QueueUserWorkItem(
        o =>
        {
            var url = GravatarImage.GetURL(
               "http://www.gravatar.com/avatar.php?gravatar_id=" + email);
            var webClient = new WebClient();
            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));
        });
    

    EDIT: today you would just use async methods:

    var url = GravatarImage.GetURL(
        "http://www.gravatar.com/avatar.php?gravatar_id=" + email);
    var httpClient = new HttpClient();
    var responseStream = await httpClient.GetStreamAsync(url);
    var bitmapImage = new BitmapImage();
    
    using (var memoryStream = new MemoryStream())
    {
        await responseStream.CopyToAsync(memoryStream);
    
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.StreamSource = memoryStream;
        bitmapImage.EndInit();
        bitmapImage.Freeze();
    }
    
    image.Source = bitmapImage;
    
    0 讨论(0)
  • I suggest you to use a Binding on your imgGravatar from XAML. Set IsAsync=true on it and WPF will automatically utilize a thread from the thread pool to pull your image. You could encapsulate the resolving logic into an IValueConverter and simply bind the email as Source

    in XAML:

    <Window.Resouces>
        <local:ImgConverter x:Key="imgConverter" />
    </Window.Resource>
    
    ...
    
    
    <Image x:Name="imgGravatar" 
           Source="{Binding Path=Email, 
                            Converter={StaticResource imgConverter}, 
                            IsAsync=true}" />
    

    in Code:

    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;
            }
    
        }
    }
    
    0 讨论(0)
提交回复
热议问题