question

WhiteShavedChocolate-4717 avatar image
0 Votes"
WhiteShavedChocolate-4717 asked WhiteShavedChocolate-4717 commented

Unable to Display Image Inside CollectionView Using Local Storage (Not Embedded) in Xamarin.Forms

After searching for days and trying many things, I am hoping to get an answer from The Community. I am not an experienced coder, so please be gentle.

Using the LocalDataBaseTutorial as a starting point in VS2019, my app has an Editor field to input a person's name. It also has a Button that calls the Xamarin.Essentials Media Picker to choose a photo of the person from local storage and then display it next to the Editor field. There is one other Button that, when tapped, commits the name and image to an SQLite database and then displays the name and image inside a CollectionView.

Everything is working, except being able to display the image inside the CollectionView. It seems that Xamarin.Forms won't allow using a MemoryStream as an ImageSource, whenever a DataTemplate is being used. A string is required, instead: either a URL or a path name to local storage. I am using local storage.

Here is how things appear, as the app awaits the user's input:
100821-blank-name-and-image-small.png

How things look, once the person's name and image have been input:
100812-name-and-image-filled-small.png

And here is the appearance, once the "Add Text and Image to Playlist" button has been tapped:
100733-no-image-inside-collectionview-small.png

The person's image is supposed to show up to the right of their name, but as you can see, it is blank.

Below is my MainPage.xaml code:

 <?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="LocalDatabaseTutorial.MainPage">
     <ContentPage.Content>
         <StackLayout Orientation="Vertical">
             <StackLayout Orientation="Horizontal">
                 <Grid RowSpacing="0" ColumnSpacing="2">
                     <Grid.RowDefinitions>
                         <RowDefinition Height="64" />
                         <RowDefinition Height="64" />
                     </Grid.RowDefinitions>
                     <Grid.ColumnDefinitions>
                         <ColumnDefinition Width="*" />
                         <ColumnDefinition Width="*" />
                         <ColumnDefinition Width="*" />
                         <ColumnDefinition Width="2*" />
                     </Grid.ColumnDefinitions>
                     <Editor x:Name="nameEntry" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4"
                     Placeholder="Enter person's name" />
                     <Button Text="Add Image" Grid.Row="1" Grid.Column="0"
                     FontSize="12" Clicked="Button_Clicked" />
                     <Button Text="Add Text and Image to Playlist"  Grid.Row="1" Grid.Column="3" Grid.ColumnSpan="1"
                     VerticalOptions="CenterAndExpand" FontSize="12" Clicked="OnButtonClicked" />
                     <Image x:Name="ImageGIFentry" Grid.Row="1" Grid.Column="1" />
                 </Grid>
             </StackLayout>
         <CollectionView x:Name="collectionView">
             <CollectionView.ItemTemplate>
                 <DataTemplate>
                         <Grid RowSpacing="0" ColumnSpacing="2">
                         <Grid.RowDefinitions>
                             <RowDefinition Height="64" />
                         </Grid.RowDefinitions>
                         <Grid.ColumnDefinitions>
                             <ColumnDefinition Width="*" />
                             <ColumnDefinition Width="3.5*" />
                             <ColumnDefinition Width="1*" />
                             <ColumnDefinition Width="1*" />
                         </Grid.ColumnDefinitions>
                             <Button Text="{Binding ID}" Grid.Row="0" Grid.Column="0" HorizontalOptions="Center" VerticalOptions="Center" 
                                 TextColor="White" FontSize="12" BackgroundColor="#1D428A" FontAttributes="Bold"
                  WidthRequest="36" HeightRequest="36" CornerRadius="18"/>
                             <Editor Text="{Binding Name}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"  VerticalOptions="Center"
                                TextColor="Black" FontSize="Medium" />
                             <Image Source="{Binding ImageSource}" Grid.Row="0" Grid.Column="3" HeightRequest="64"></Image>
                         </Grid>
                 </DataTemplate>
             </CollectionView.ItemTemplate>
         </CollectionView>
         </StackLayout>
     </ContentPage.Content>
 </ContentPage>

MainPage.cs

 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Xamarin.Forms;
 using Xamarin.Essentials;
    
 namespace LocalDatabaseTutorial
 {
     public partial class MainPage : ContentPage
     {
         string bmpFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "file001.bmp");
         MemoryStream destination = new MemoryStream();  //define a new MemoryStream named destination
    
         public ImageSource ImageSource { get; private set; }
    
         public MainPage()
         {
             InitializeComponent();
         }
         protected override async void OnAppearing()
         {
             base.OnAppearing();
             collectionView.ItemsSource = await App.Database.GetPeopleAsync();
         }
    
         async public void OnButtonClicked(object sender, EventArgs e)
         {
             byte[] data = destination.ToArray();    //converts stream data for the file into a byte array
             destination.Position = 0;               //resets destination stream position index to "0"
             if (!string.IsNullOrWhiteSpace(nameEntry.Text))
             {
                 File.WriteAllBytes(bmpFileName, data);  //writes image file to local storage
                 await App.Database.SavePersonAsync(new Person
                 {
                     Name = nameEntry.Text,      //commits Name to SQLite database
                     BmpImg = bmpFileName        //commits picked image to SQLite database
                 });
                 if (File.Exists(bmpFileName))
                 {
                     byte[] ReadBMP = File.ReadAllBytes(bmpFileName);    //Open and read image file into array as debug test
                 }
                 ImageSource = ImageSource.FromFile(bmpFileName);
                 nameEntry.Text = string.Empty;
                 collectionView.ItemsSource = await App.Database.GetPeopleAsync();
             }
         }
    
         async public void Button_Clicked(object sender, EventArgs e)
         {
             var result = await MediaPicker.PickPhotoAsync(new MediaPickerOptions{});    //result is a FileResult
             var stream = await result.OpenReadAsync();  //Opens result and reads the file contents into a stream
             stream.CopyTo(destination); //Copies the stream to a new stream, destination
             stream.Position = 0;    //Resets the stream position index to 0
             ImageGIFentry.Source = ImageSource.FromStream(() => stream); // Sets the source for ImageGIFentry to stream;
             byte[] data = destination.ToArray();    //converts stream data for the file into a byte array
         }
     }
 }

And my SQLite code:
Person.cs

 namespace LocalDatabaseTutorial
 {
     public class Person
     {
         [PrimaryKey, AutoIncrement]
         public int ID { get; set; }
         public string Name { get; set; }
 //        public int Age { get; set; }
         public string BmpImg { get; set; }
     }
 }

Database.cs

 namespace LocalDatabaseTutorial
 {
     public class Database
     {
         readonly SQLiteAsyncConnection _database;
    
         public Database(string dbPath)
         {
             _database = new SQLiteAsyncConnection(dbPath);
             _database.CreateTableAsync<Person>().Wait();
         }
    
         public Task<List<Person>> GetPeopleAsync()
         {
             return _database.Table<Person>().ToListAsync();
         }
    
         public Task<int> SavePersonAsync(Person person)
         {
             return _database.InsertAsync(person);
         }
     }
 }

This is probably something incredibly simple that I am missing, but I just can't figure it out. Your help would be greatly appreciated!


dotnet-xamarin
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

LeonLu-MSFT avatar image
1 Vote"
LeonLu-MSFT answered WhiteShavedChocolate-4717 commented

Hello,​

Welcome to our Microsoft Q&A platform!

You just have one mistakes in your code.

Your binding ImageSource with this code <Image Source="{Binding ImageSource}" Grid.Row="0" Grid.Column="3" HeightRequest="64"></Image>, but your Model(Person.cs), this image binding name should be BmpImg not ImageSource. So please change binding Image source from ImageSource to BmpImg in CollectionView like following code.

<?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="MySqliteDB.MainPage">

    <ContentPage.Content>
        <StackLayout Orientation="Vertical">
            <StackLayout Orientation="Horizontal">
                <Grid RowSpacing="0" ColumnSpacing="2">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="64" />
                        <RowDefinition Height="64" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="2*" />
                    </Grid.ColumnDefinitions>
                    <Editor x:Name="nameEntry" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4"
                     Placeholder="Enter person's name" />
                    <Button Text="Add Image" Grid.Row="1" Grid.Column="0"
                     FontSize="12" Clicked="Button_Clicked" />
                    <Button Text="Add Text and Image to Playlist"  Grid.Row="1" Grid.Column="3" Grid.ColumnSpan="1"
                     VerticalOptions="CenterAndExpand" FontSize="12" Clicked="OnButtonClicked" />
                    <Image x:Name="ImageGIFentry" Grid.Row="1" Grid.Column="1" />
                </Grid>
            </StackLayout>
            <CollectionView x:Name="collectionView">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <Grid RowSpacing="0" ColumnSpacing="2">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="64" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="3.5*" />
                                <ColumnDefinition Width="1*" />
                                <ColumnDefinition Width="1*" />
                            </Grid.ColumnDefinitions>
                            <Button Text="{Binding ID}" Grid.Row="0" Grid.Column="0" HorizontalOptions="Center" VerticalOptions="Center" 
                                 TextColor="White" FontSize="12" BackgroundColor="#1D428A" FontAttributes="Bold"
                  WidthRequest="36" HeightRequest="36" CornerRadius="18"/>
                            <Editor Text="{Binding Name}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"  VerticalOptions="Center"
                                TextColor="Black" FontSize="Medium" />

                            <Image Source="{Binding BmpImg}" Grid.Row="0" Grid.Column="3" HeightRequest="64"></Image>

                        </Grid>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </StackLayout>
    </ContentPage.Content>

</ContentPage>

Here is running screenshot.

100817-image.png

Best Regards,

Leon Lu



If the response is helpful, please click "Accept Answer" and upvote it.

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.



image.png (47.4 KiB)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Leon, thank you so much! Yes, this now works perfectly! This is a great case for making a tutorial, "Including Local Storage Images in a CollectionView".

0 Votes 0 ·