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:
How things look, once the person's name and image have been input:
And here is the appearance, once the "Add Text and Image to Playlist" button has been tapped:
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!
