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 copyJason
@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 expensiveJason
@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.