Setup:
XAML:
<Image x:Name="MyImage"
HeightRequest="160"
Aspect="AspectFit"/>
Code-behind:
protected override void OnAppearing()
{
base.OnAppearing();
var filePath = Path.Combine(FileSystem.AppDataDirectory,
"test.jpg");
if (File.Exists(filePath))
{
MyImage.Source = ImageSource.FromFile(filePath);
}
}
It seems the above only works on Windows. It does NOT work on Android.
The following permissions are set in AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
I do NOT want to use FilePicker or anything like that.
The question is plain and simple: how to load image file from storage to .NET MAUI Image?
This GitHub issue seems related and still open since 2022! Anyone knows a workaround or third-party component that sorts this out?
I've been trying to do something similar, and have been seeing the same kinds of wonky behaviors.
So, you asked if:
Anyone knows a workaround [...]?
That's what this is actually, a workaround not a canonical answer. But I've been testing it a lot and it seems really stable on these physical devices:
It mainly consists of this Refresh()
extension that I wrote for the purpose of forcing a reload of image content into the ImageSource
that is otherwise aggressively cached and difficult to modify:
static partial class Extensions
{
public static ImageSource? Refresh(this string path)
{
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
return ImageSource.FromStream(() => new MemoryStream(File.ReadAllBytes(path)));
}
else return null;
}
}
Usage (Code-Behind)
This version of the test app came first, because I wanted to eliminate any variables having to do with the binding of the view model. In other words, in code-behind, using a reference to the Image
control, I set its Source
property directly. There is no view model at all in this version. To test under "worst" conditions I use a singular file path FixedPathForTest
located (from your original post) at Path.Combine(FileSystem.CacheDirectory,"kqeah1iq.yih")
. To apply the latest file update, the syntax is:
imagePhoto.Source = FixedPathForTest.Refresh();
Usage (ViewModel Binding)
This version of the test app (the extension doesn't change) came next, to go back to the idea of binding Source
in the VM.
<Image
Source="{Binding Source}"
HeightRequest="185"
Aspect="AspectFit"/>
It's important that the type of Source
is ImageSource
because even if you think you are setting a string to it, the "real" ImageSource
is being implicitly casted from it and the result of the implicit cast is a FileImageSource
where the File
property is set to the string.
class MainPageViewModel : INotifyPropertyChanged
{
/// <summary>
/// This needs to be ImageSource and not String.
/// </summary>
public ImageSource? Source
{
get => _imageSource;
set
{
ImageSource? preview = value;
if (preview is FileImageSource fileImageSource &&
fileImageSource.File is string path)
{
var fi = new FileInfo(path);
if(!Equals(fi.LastWriteTime, _fileWriteTime))
{
_imageSource = fileImageSource.File.Refresh();
OnPropertyChanged();
}
}
else
{
if (!Equals(_imageSource, value))
{
_imageSource = value;
OnPropertyChanged();
}
}
}
}
ImageSource? _imageSource = default;
DateTime _fileWriteTime = default;
}