Home > C#, MVVM, WinRT > How to convert a byte array to image in a Windows Store app

How to convert a byte array to image in a Windows Store app

15/04/2013

Suppose we’re devoloping a C# Windows Store app with the MVVM pattern and we have a ViewModel containing a byte array that represents an image. We can define a converter to directly bind it to an Image control:

public class ByteArrayToImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null || !(value is byte[]))
            return null;

        using (InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream())
        {
            // Writes the image byte array in an InMemoryRandomAccessStream
            // that is needed to set the source of BitmapImage.
            using (DataWriter writer = new DataWriter(ms.GetOutputStreamAt(0)))
            {
                writer.WriteBytes((byte[])value);

                // The GetResults here forces to wait until the operation completes
                // (i.e., it is executed synchronously), so this call can block the UI.
                writer.StoreAsync().GetResults();
            }

            var image = new BitmapImage();
            image.SetSource(ms);
            return image;
        }
    }

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

However, converting a byte array to a stream can be costly: because converters are synchronous, this solution can block the UI (the problem, in particular, is at line 18, when we store the buffer in the DataWriter, and we need to call the GetResults method to force to execute it synchronously).

A better solution is to give the ViewModel the responsability to asynchronously load the image as soon as the byte array is provided. Take a look to the following code:

public class MainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private byte[] imageBuffer;
    public byte[] ImageBuffer
    {
        get { return imageBuffer; }
        set 
        { 
            imageBuffer = value;
            var loadImageTask = LoadImageAsync();
        }
    }

    private ImageSource imageSource;
    public ImageSource ImageSource
    {
        get { return imageSource; }
        set 
        { 
            imageSource = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("ImageSource"));
        }
    }

    private async Task LoadImageAsync()
    {
        using (InMemoryRandomAccessStream ms = new InMemoryRandomAccessStream())
        {
            // Writes the image byte array in an InMemoryRandomAccessStream
            // that is needed to set the source of BitmapImage.
            using (DataWriter writer = new DataWriter(ms.GetOutputStreamAt(0)))
            {
                writer.WriteBytes(imageBuffer);
                await writer.StoreAsync();
            }

            var image = new BitmapImage();
            await image.SetSourceAsync(ms);
            ImageSource = image;
        }
    }
}

When we set the ImageBuffer property, the LoadImageAsync method is invoked, that in turns writes the byte array to an InMemoryRandomAccessStream and uses it to set the source of a BitmapImage. Finally, we set the ImageSource propriety of the ViewModel, that raises the PropertyChanged event.

In this way, all we have to do is to bind the Source property of an Image control to the ImageSource property in the ViewModel:

<Image Source="{Binding ImageSource}" Height="150" Width="150" />

Every time we change the ImageBuffer proprerty, the corresponding new image will be automatically shown in the control.

Categories: C#, MVVM, WinRT
%d bloggers like this: