Monday, July 21, 2014

Walk-through for creating a ValueConverter from byte[] to ImageSource

This is a walk-through for how to create a ValueConverter that you can use in code or in Xaml to convert an image stored in an array of bytes (byte[]) to an ImageSource-object and how to bind it.

This comes in handy when you store images in a SQLite database and want to display them in a Xamarin.Forms application.

Prereqs

  • You have a Xamarin Mobile App project
  • You have an image in a byte array

The post is nothing fancy, just the whole thing gathered in one post, instead of four other pieces.

Create the converter

In the core project (main project or what ever you call it), create a folder called Converters and add a class called ByteArrayToImageSourceConverter. Copy the code below into that file

using System;
using System.Globalization;
using System.IO;
using Xamarin.Forms;

namespace YourApp.Converters
{
    public class ByteArrayToImageSourceConverter : IValueConverter
    { 
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
            {
                return null;
            }

            byte[] bytes = value as byte[];
            return ImageSource.FromStream(() => new MemoryStream(bytes));
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}


You need a ViewModel with an image property

Add a model of your liking. One of the properties must be a byte array so that you'd have something to bind to. The code below is just a sample model. Since we implement INotifyPropertyChanged, the image will automatically update if you set the Image property on the ViewModel.

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace YourApp.ViewModels
{
    public class YourViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public string Notes { get; set; }

        private byte[] _image;
        public byte[] Image 
        { 
            get 
            { 
                return _image; 
            } 
            set 
            { 
                _image = value; 
                OnPropertyChanged(); 
            } 
        }

        void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Use the converter in Xaml

There are a couple of hoops that you need to get through to use this converter in Xaml. 
  • You need to import the namespace (xmlns:local="clr....)
  • You must reference the converter as a static resource ()
  • In the binding for the image, declare that you want the converter to be used (Converter={StaticResource bic}})

xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourApp.Converters;assembly=YourApp"
                       x:Class="YourApp.Views.YourPage"
             Title="A cool title"
             Icon="new44.png">

  <ContentPage.Resources>
    <ResourceDictionary>
      <local:ByteArrayToImageSourceConverter x:Key="bic" />
    </ResourceDictionary>
  </ContentPage.Resources>
 
  <StackLayout VerticalOptions="FillAndExpand">
    <Image x:Name="image" Source="{Binding Image, Converter={StaticResource bic}}" />

  </StackLayout>
</ContentPage>

Resource on xaml namespaces

Use the converter in code

You can also create the binding in code. In the constructor of your page, after InitalizeComponent, define the binding like the example below.

 var binding = new Binding("Image", BindingMode.Default, new ByteArrayToImageSourceConverter());
            image.SetBinding(Image.SourceProperty, binding);

That's about it. Nothing fancy, just all in one place if needed.

1 comment:

  1. How we can call Dispose on MemoryStream? I tried to make use of using statement, but, If we call dispose, then we face "Cannot access a closed Stream." error.

    ReplyDelete