2
votes

I am very new to Xamarin, MVVM and cross-platform app developing (Android, iOS, UWP). I am using Realm to store/show images in Base64 format. I have successfully stored the image base64 string, but I am having difficulties in showing images back in ListView.

ImageModel.cs

namespace RealmTest.Models
{
    public class ImageModel : RealmObject
    {
        [PrimaryKey]
        public string ImageId { get; set; } = Guid.NewGuid().ToString();

        public string ImageName { get; set; }

        public string ImageBase64 { get; set; }

        public string ImageCaseHistory { get; set; }

    }
}

BaseViewModel.cs

namespace RealmTest.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

RealmImageViewModel.cs

namespace RealmTest.ViewModels
{
    public class RealmImageViewModel : BaseViewModel
    {
        private ObservableCollection<ImageModel> imagesList;
        public ObservableCollection<ImageModel> ImagesList
        {
            get
            {
                return imagesList;
            }
            set
            {
                imagesList = value;
            }
        }

        public RealmImageViewModel()
        {
            Realm context = Realm.GetInstance();
            ImagesList = new ObservableCollection<ImageModel>(context.All<ImageModel>());

            Debug.WriteLine($"ImagesList ==>" + ImagesList.Count());
        }
    }
}

HomePage.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"
             x:Class="RealmTest.Views.HomePage"
             Title="Home">
    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Add"
                     Icon="ic_add_black.png"/>
    </ContentPage.ToolbarItems>

    <ContentPage.Content>

        <ListView ItemsSource="{Binding ImagesList}"
                  HasUnevenRows="True"
                  BackgroundColor="#f5f5f5">

            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>

                        <Grid BackgroundColor="White"
                              Margin="4">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>


                            <Label Grid.Row="0"
                                   Text="{Binding ImageName}"
                                   FontSize="Medium"
                                   Margin="4"
                                   FontAttributes="Bold" />

                            <Image Source="{Binding ImageBase64}" 
                                   HeightRequest="150"
                                   Grid.Row="1"
                                   />

                            <StackLayout Orientation="Horizontal"
                                         Grid.Row="2"
                                         Margin="4"
                                         Padding="2">

                                <Label Text="{Binding ImageCaseHistory}"
                                       FontSize="Small" />

                                <Label Text="{Binding ImageName}"
                                       FontSize="Small" />

                            </StackLayout>
                        </Grid>

                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

    </ContentPage.Content>

</ContentPage>

HomePage.cs

namespace RealmTest.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class HomePage : ContentPage
    {
        public HomePage()
        {
            InitializeComponent();
        }

        protected override void OnAppearing()
        {
            BindingContext = new RealmImageViewModel();

            /**
            var image = Xamarin.Forms.ImageSource.FromStream(
                () => new MemoryStream(Convert.FromBase64String(base64)));
            **/
        }
    }
}

So far I have learnt two things regarding my issue/query -

  1. I can display base64 string into image format like -

    var image = Xamarin.Forms.ImageSource.FromStream( () => new MemoryStream(Convert.FromBase64String(base64)));

  2. I have read this answer on SO and learnt that some ConverterBase64ImageSource can be created, but I am not able to understand it, how to make and implement it.

I am still unable to figure out a way to display all images (which are in Base64 string format in my Realm database) inside a ListView.

1
it would probably be a lot more efficient to save your images locally when you get them from Realm, and then display the local copy - Jason
@Jason - storing image locally was the original idea, when I began, but somehow I felt that it was getting more difficult to sync/upload images and relevant info to online database. Using base64 I can upload all info using single function and can sync data over multiple devices. Would you still recommend to use storing images locally? - Dr. Atul Tiwari
I'm saying when you retrieve a base64 image from Realm, you should ALSO cache it locally as an image, rather than attempting to decode a stream on the fly, which seems expensive - Jason
@Jason - My bad. Sorry. I understood your point, after I posted my comment. Yes, that appears to be better approach. I will try to work through it. Thanks for the suggestion. - Dr. Atul Tiwari
you can't directly set base64 string in image source you need to use converter and convert base64 to image source. - Ziyad Godil

1 Answers

0
votes

I have found a little different way to do this, I save in my localDB (SQLite) a base64 file.

This is in the model:

//Here we convert the base64 to a ImageSource

     string foto;
    [JsonProperty(PropertyName = "Foto")]
    public string Foto
    {
        get { return foto; }
        set
        {
            foto = value;
            OnPropertyChanged("Foto");

            FOTO = Xamarin.Forms.ImageSource.FromStream(
                () => new MemoryStream(Convert.FromBase64String(foto)));
        }
    }
    private Xamarin.Forms.ImageSource _foto;
    [Ignore]//The localdb don't support the ImageSource
    public Xamarin.Forms.ImageSource FOTO
    {
        get { return _foto; }
        set
        {
            _foto = value;
            OnPropertyChanged("FOTO");
        }
    }

In my case I used this class like a helper to convert the file:

public class FilesHelper
    {
        public static byte[] ReadFully(Stream input)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                input.CopyTo(ms);
                return ms.ToArray();
            }
        }
    }

Here we convert the image into a base64 file to save in the localDB:

 byte[] imageArray = null;
            String image = "";
            if (this.file != null)
            {
                imageArray = FilesHelper.ReadFully(this.file.GetStream());

                image = Convert.ToBase64String(imageArray);
            }

I hope this help.