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}" ... />