2

I have a ListView item which contains datas and images from a http GET request. I can display all of data in the ListView, except the picture. For getting the image I have to make a separate http GET request. I can display an image with this code:

private async void DisplayPicture()
{
    var ims = new InMemoryRandomAccessStream();
    var dataWriter = new DataWriter(ims);
    dataWriter.WriteBytes(App.answer.picture);
    await dataWriter.StoreAsync();
    ims.Seek(0);
    BitmapImage bitmap = new BitmapImage();
    bitmap.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
    bitmap.SetSource(ims);
}

But this doesn't work if I would like to use in a ListView with Binding. Here is the code what I tried:

public class BinaryToImageSourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value != null && value is byte[])
        {
            var bytes = value as byte[];
            var ims = new InMemoryRandomAccessStream();
            var dataWriter = new DataWriter(ims);
            dataWriter.WriteBytes(bytes);
            //await dataWriter.StoreAsync();
            ims.Seek(0);
            BitmapImage bitmap = new BitmapImage();
            bitmap.SetSource(ims);
            //var ims = new MemoryStream(bytes);
            //var image = new BitmapImage();
            //image.SetSource(stream);
            //stream.Close();
            return bitmap;
        }
        return null;
    }
    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

The main problem is that I get the image in byte[] (bytearray) from the server, and only the above code can display it on WP8.1. So I have to use the dataWriter.StoreAsync() method, but if I use it, I have to use async, which must be void. But the void return value is not good for me due to the binding.

You can see the original code what I uncommented, but I cannot use it, because the input value for image.SetSource() must be a RandomAccessStream. So I don't have any idea how I can solve this problem.

Speederer
  • 371
  • 5
  • 14
  • Are you just downloading the image file from the web? Or that byte stream something from a webservice and kinda unique? – Chubosaurus Software Nov 24 '14 at 02:15
  • It is a user avatar, what I can reach with this url: https://myapi.mywebpage.com/Image/Downloadavatar?Email=user@example.com So it's coming from a webservice and each ListView item contains a unique picture. The picture downloads maybe take a longer time, but I will try to solve it later. – Speederer Nov 24 '14 at 07:18
  • jpg? png? gif? Base64 encoded? other? – Chubosaurus Software Nov 24 '14 at 07:41

1 Answers1

4

If you want to make binding and use asynchronous method, then one way to make it work is to set DataContext to Task and bind to its Result. Stepen Cleary wrote a nice article about that. You will also find some useful information in his answer here.

Basing on that answer I've build a sample, which I think you can modify to fulfill your needs. Write a Converter which will return TaskCompletionNotifier (see Stephen's answer above):

public class WebPathToImage : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null) return null;
        // the below class you will find in Stephen's answer mentioned above
        return new TaskCompletionNotifier<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)
    {
        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;
    }
}

then you can define binding in XAML:

<Image DataContext="{Binding ImageFromWeb, Converter={StaticResource WebPathToImage}}" Stretch="Uniform" 
       Source="{Binding Result}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="2"/>

Everything should work when you set ImageFromWeb:

ImageFromWeb = @"http://www.onereason.org/wp-content/uploads/2012/02/universe-300x198.jpg";
Community
  • 1
  • 1
Romasz
  • 29,662
  • 13
  • 79
  • 154
  • Hi, can you help me again? :) I would like to use this function to display a simple picture. So it works very well for example in a ListView, but only when I use binding. I tried to set the ImageFromWeb from c#, but it's not available there. Have you any idea? – Speederer Dec 09 '14 at 23:27
  • And one more question: In the GetImage() method I would like to change the System.Net.HttpClient to Windows.Web.Http.HttpClient. But the GetStreamAsync() is missing from it. Is there other function instead of this? – Speederer Dec 09 '14 at 23:32
  • @Speederer If you don't need binding, then *BitmapImage* has for example *SetSourceAsync* method. As for HttpClient there is *GerAsync* method and then you get a *HttpResponseMessage* with *Content*. Also please don't ask new questions in comments - it's not their purpose. – Romasz Dec 10 '14 at 06:12
  • Okay, thanks! I will try that. And I won't ask again in comments. – Speederer Dec 10 '14 at 10:48
  • 1
    It still working in 2019, latest version of UWP (1809). Very good solution. – Bruno Feb 26 '19 at 11:20