6

I'm developing a Windows Phone 8.1 app. I have a screen with a list of news' titles with thumbnails.

First I'm making asynchronous http request to get news collection in JSON (satisfying the NotifyTaskCompletion pattern)

NewsCategories = new NotifyTaskCompletion<ObservableCollection<NewsCategory>>(_newsService.GetNewsCategoriesAsync());

NewsCategory:

public class NewsCategory : ObservableObject
{
    ...
    public string Title { get;  set; }
    public ObservableCollection<News> Items { get;  set; }
}

News:

public class News : ObservableObject
{
    ...
    public string Title { get; set; }
    public string ImagePath { get; set; }
}

So far it works perfectly, but as soon as I get the ImagePath property, I would like to download and display the given image. I've found a solution to do it asynchronously here: WP 8.1 Binding image from a http request - so that when xaml gets the image path, it calls a converter class (BinaryToImageSourceConverter), also using the NotifyTaskCompletion pattern.

The problem occurs in the following method:

private async Task<BitmapImage> GetImage(string path)
{
    HttpClient webCLient = new HttpClient();
    var responseStream = await webCLient.GetStreamAsync(path);
    var memoryStream = new MemoryStream();
    await responseStream.CopyToAsync(memoryStream);
    memoryStream.Position = 0;
    var bitmap = new BitmapImage();
    await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream());
    return bitmap;
}

When the first await is called, the debugger never reach the next line and the method never returns. The string path variable has proper content.

So far I have tried to use ConfigureAwait(false), but it doesn't work in my case.

I have also found out in the topic: Deadlock while using async await , that:

When you're using ConfigureAwait(false), you tell your program you dont mind about the context. It can solve some deadlocking problems, but isn't usually the right solution. The right solution is most likely never to wait for tasks in a blocking way, and being asynchronous all the way.

I don't know where I could have that stuff in a blocking way. What can be the reason for that deadlock?

And if it's all about wrong approach, do you know any pattern that would be more appropriate for downloading thumbnails to a collection of items?

Thank you for your help.


update: how is GetImage invoked: It's like in topic: WP 8.1 Binding image from a http request

public class WebPathToImage : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null) return null;
        return new NotifyTaskCompletion<BitmapImage>(GetImage((String)value));
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    { throw new NotImplementedException(); }

    private async Task<BitmapImage> GetImage(string path)
    {
        using (var webCLient = new HttpClient())
        {
            webCLient.DefaultRequestHeaders.Add("User-Agent", "bot");
            var responseStream =  await webCLient.GetStreamAsync(path).ConfigureAwait(false);
            var memoryStream = new MemoryStream();
            await responseStream.CopyToAsync(memoryStream);
            memoryStream.Position = 0;
            var bitmap = new BitmapImage();
            await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream());
            return bitmap;  
        }
    }
}

and in xaml:

<Image 
DataContext="{Binding ImagePath, Converter={StaticResource WebPathToImage}}"
Source="{Binding Result}" 
Stretch="UniformToFill" 
Height="79" Width="79"/>
Community
  • 1
  • 1
K.A.
  • 69
  • 5

2 Answers2

0

Try maybe to call you GetImage into Task.Factory.Run to force the task to be executed in another thread.

Attention also at you Bitmap creation you could have problem with it because the bitmap is not created in the UI thread.

Mickael Thumerel
  • 516
  • 4
  • 14
0

First, if you are on Windows Phone 8.1 then it is recommended to use the HttpClient from the namespace Windows.Web.Http and not from System.Net.Http. Some reasons are explained on this link.

My answer uses the new Windows.Web.Http.HttpClient so your GetImage method can look like this:

        private async Task<BitmapImage> GetImage(string path)
        {
            using (var webCLient = new Windows.Web.Http.HttpClient())
            {
                webCLient.DefaultRequestHeaders.Add("User-Agent", "bot");
                var responseStream = await webCLient.GetBufferAsync(new Uri(path));
                var memoryStream = new MemoryStream(responseStream.ToArray());
                memoryStream.Position = 0;
                var bitmap = new BitmapImage();
                await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream());
                return bitmap;
            }
        }

I have tested this method on a sample URL and it displayed correctly in an Image control.

Second, why don't you just let the Image control handle downloading the BitmapImage without the converter, so that your XAML looks like this:

<Image
    Source="{Binding ImagePath}" 
    Stretch="UniformToFill" 
    Height="79" 
    Width="79" />

This way your ImagePath can be a path to a resource from the internet and a local resource.

Community
  • 1
  • 1
Kristian Vukusic
  • 3,284
  • 6
  • 30
  • 46