lolveley
lolveley

Reputation: 1719

display an image retrieved from its URI

I have some problems while trying to serialize an object containing a BitmapSource field.

In fact, at the beginning, I had an ImageSource but it seems not to be serializable. So I tried to use the associated property to store a string and to convert & convert back the image to/from the string.but no image is displayed now :-(

here is the XAML image tag:

<Image 
                        x:Name="bookCover"
                        Grid.Row="0"
                        Grid.RowSpan="1"
                        Grid.Column="0"
                        MaxWidth="200"
                        MaxHeight="320"
                        Margin="5"
                        Source="{Binding Image}" SizeChanged="bookCover_SizeChanged" >

                    </Image>

here is the Image property & field in the model class:

public string _image;

public BitmapSource Image
        {
            get => Base64ToImage(_image);
            set
            {
                _image =ImageToBase64(value);
                OnPropertyChanged("Image");
            }
        }

and their associated methods:

public static string ImageToBase64(BitmapSource bitmap)
        {

            var encoder = new JpegBitmapEncoder();
            var frame = BitmapFrame.Create(bitmap);
            encoder.Frames.Add(frame);
            using (var stream = new MemoryStream())
            {
                encoder.Save(stream);
                return Convert.ToBase64String(stream.ToArray());
            }
        }

        public static  BitmapSource Base64ToImage(string base64)
        {
            byte[] bytes = Convert.FromBase64String(base64);
            using (var stream = new MemoryStream(bytes))
            {
                return BitmapFrame.Create(stream);
            }

        }

and here my program retrieves an image from internet via its URI and stores it:

var myUri=new Uri(book0.LargeImage.URL);
bookToInsert.Image = new BitmapImage(myUri);

thank you.

Upvotes: 0

Views: 565

Answers (1)

Clemens
Clemens

Reputation: 128136

When you create a BitmapSource from a stream that is closed immediately after creation, you have to set BitmapCacheOption.OnLoad. Otherwise the stream would have to be kept open until the BitmapSource is actually used, e.g. displayed in an Image element.

public static BitmapSource Base64ToImage(string base64)
{
    using (var stream = new MemoryStream(Convert.FromBase64String(base64)))
    {
        return BitmapFrame.Create(
            stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
    }
}

A couple of notes:

  1. base64 encoding and decoding seems unnecessary. Better serialize a byte array.
  2. Do not initially create a BitmapImage from the URL. Better directly get the image buffer by a web request.
  3. You can bind the Source property of an Image element not only to an ImageSource, but also to a string, Uri or byte array. So your property may be declared as byte[] Image.

Try a much simpler view model like this:

public class ViewModel : INotifyPropertyChanged
{
    private byte[] image;

    public byte[] Image
    {
        get => image;
        set
        {
            image = value;
            OnPropertyChanged(nameof(Image));
        }
    }
}

and set the Image property like this:

var webClient = new WebClient();
viewModel.Image = webClient.DownloadData(book0.LargeImage.URL);

Or asynchronously:

var httpClient = new HttpClient();
viewModel.Image = await httpClient.GetByteArrayAsync(book0.LargeImage.URL);

Upvotes: 1

Related Questions