1
votes

I am trying to create an image based on the Xamarin forms Image which can be bound with an byte array.

I don't want to use the ImageSource directly in my ModelView because the ModelViews are also used for an WPF UI.

So I only want to convert the byte array(created from an base64 string) to an image which can be bound by the UI's. On the WPF end there is no problem, because it can take an image in bytearray format. Now I want to create an Custom-ImageControll for Xamarin Forms which can take/bind an byte array and format it to an ImageSource.

Also I don't want to create an Renderer for each platform. I want to use standard renderer from Xamarin Image.

As you can see below I created an custom Image(MVVMImage) and using the basic Image provided by Xamarin.

The problem is every time the MVVMImage is loading I get:

System.Reflection.TargetInvocationException:

Custom Image:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Xamarin.Forms;

   namespace Testape
   {
      public class MVVMImage : Image
      {

          public MVVMImage() : base()
          {
            //Attempt to avoid of create renderer on every device. 
          }

          public static readonly BindableProperty ArraySourceProperty =
          BindableProperty.Create(nameof(ArraySource), typeof(byte[]), typeof(MVVMImage), "");

          public byte[] ArraySource
          {
              set
              {
                  this.Source = ImageSource.FromStream(() => new   MemoryStream(value));
              }
          }
      }
  }

MainPage XAML

<?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:Testape;assembly=Testape"
             x:Class="Testape.MainPage">

    <local:MVVMImage ArraySource="{Binding Img}"
           VerticalOptions="Center"
           HorizontalOptions="Center"/>
</ContentPage>

MainPage Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Testape
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            byte[] bt = Convert.FromBase64String("iVBORw0KGgoAAA< And so on >AAAAAElFTkSuQmCC");
            BindingContext = new ViewModelMainw(bt);
        }
    }
}

#Model View

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Testape
{
    public class ViewModelMainw
    {
        byte[] bffapt;
        public ViewModelMainw(byte[] bff)
        {
            bffapt = bff;
        }
        public byte[] Img
        {
            get { return bffapt; }
        }
    }
}
1

1 Answers

1
votes

One issue is the way you are handling your bindable property change.

It should be handled in property changed not the setter.

It should be something like this:

public static readonly BindableProperty ArraySourceProperty =
    BindableProperty.Create(nameof(ArraySource), typeof(byte[]), typeof(MVVMImage), null,
    BindingMode.OneWay, null, OnArraySourcePropertyChanged);

public byte[] ArraySource
{
    get { return (byte[])GetValue(ArraySourceProperty); }
    set { SetValue(ArraySourceProperty, value); }
}

private static void OnArraySourcePropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
{
    var control = (MVVMImage) bindable;
    if (control != null)
    {
        control.Source = ImageSource.FromStream(() => new  MemoryStream((byte[])newvalue));
    }
}