Home > front end >  How to load and save image as byte array in a wpf mvvm project
How to load and save image as byte array in a wpf mvvm project

Time:05-26

I have an Image in the view which is bound to a byte array in the view model.

 <Image 
        Source="{Binding InputImage, Converter={StaticResource imageConvertor}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
        Panel.ZIndex="1"
        x:Name="UploadImage"
        Grid.Column="0"
        Grid.Row="0"
        Stretch="Uniform"
        Margin="5"
        />


private byte[] inputImage;

public byte[] InputImage
{
    get { return inputImage; }
    set 
    { 
        inputImage = value; 
        OnPropertyChanged(nameof(InputImage));
    }
}

The user loads an image via a button which executes this command

OpenFileDialog op = new OpenFileDialog();
op.Title = "Select a picture";
op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png|"  
   "JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|"  
   "Portable Network Graphic (*.png)|*.png";

if (op.ShowDialog() == true) { UploadImage.Source = new BitmapImage(new Uri(op.FileName)); }

The problems is that when I try to load an image the converter receives null as parameter. If I remove the converter from the binding in the xaml file it gets loaded successfully to the view but it cannot be bound to the property in the view model.

This is the implementation of the converter

public class ByteArrayToBitmapImageConverter : IValueConverter
{
    public object Convert(object? value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            return null;
        }
        else
            return ToImage(value as byte[]);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            return null;
        }
        else
            return ToBinary(value as Image);
    }

    public static Image ToImage(Byte[] binary)
    {
        Image image = null;
        if (binary == null || binary.Length < 100) return image;

        using (MemoryStream ms = new MemoryStream(binary))
        {
            image = Image.FromStream(ms);
        }
        return image;
    }

  public static Byte[] ToBinary(Image image)
    {
        if (image == null) return null;
        using (MemoryStream memoryStream = new MemoryStream())
        {
            image.Save(memoryStream, ImageFormat.Png);
            return memoryStream.ToArray();
        }
    }
}

What causes the null inputs?

CodePudding user response:

You are using the WinForms Image class instead of the WPF BitmapSource. BitmapSource is a subclass of ImageSource, which is the type of the Image element's Source property.

Your converter should look like this:

public class ByteArrayToBitmapSourceConverter : IValueConverter
{
    public object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ToBitmapSource(value as byte[]);
    }

    public object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ToByteArray(value as BitmapSource);
    }

    public static BitmapSource ToBitmapSource(byte[] buffer)
    {
        BitmapSource bitmap = null;

        if (buffer != null)
        {
            using (var stream = new MemoryStream(buffer))
            {
                bitmap = BitmapFrame.Create(
                    stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
            }
        }

        return bitmap;
    }

    public static byte[] ToByteArray(BitmapSource bitmap)
    {
        byte[] buffer = null;

        if (bitmap != null)
        {
            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(bitmap));

            using (var stream = new MemoryStream())
            {
                encoder.Save(stream);
                buffer = stream.ToArray();
            }
        }

        return buffer;
    }
}

It would however be a lot simpler to directly assign the frame buffer from the image file to the view model property like

viewModel.InputImage = File.ReadAllBytes(op.FileName);

You would not need your converter at all, because due to built-in type conversion, the Image.Source property can directly be bound to source properties of type string, Uri and byte[] (besides ImageSource).

So you could simply write

<Image Source="{Binding InputImage}" ... />
  • Related