Alex Kay
Alex Kay

Reputation: 289

Binding image source in Xamarin when using async data

I am struggling to set the image source to the value I receive from the database. Here are relevant parts of my XAML, its code-behind and its view model.

XAML:

<Label Text="{Binding ViewModel_Fid}" />
<Image Source="{Binding ViewModel_ImageStream}" />

Code-behind:

protected override void OnAppearing() {
            base.OnAppearing();
            myViewModel = new myViewModel();

            myViewModel.PopulateFid();
            BindingContext = myViewModel;
        }

View Model:

public event PropertyChangedEventHandler PropertyChanged;
private string _fid;

public async void PopulateFid() {
            _fid = await getFid();
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(ViewModel_Fid)));
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(ViewModel_ImageStream)));

public MemoryStream ViewModel_ImageStream { 
            get {
                byte[] buffer = myGetBytes(_fid);
                return null == buffer ? null : new MemoryStream(buffer);    
            } 
        }

The problem is, ViewModel_ImageStream systematically executes BEFORE PopulateFid, which means that in ViewModel_ImageStream I always get _fid = null. I am pretty sure this is due to PopulateFid being async, but I need it to be this way, because getFid() is an external async function.

How can I enforce that PopulateFid executes before ViewModel_ImageStream is set?

Thanks!

PS. See my solution/answer below.

Upvotes: 1

Views: 242

Answers (1)

Alex Kay
Alex Kay

Reputation: 289

What ultimately worked for me was adding the Image element at runtime (i.e., not specifying it in XAML). I add it once I've retrieved the necessary data in View Model.

So in Code-behind:

protected override void OnAppearing() {
            base.OnAppearing();
            myViewModel = new myViewModel();

            myViewModel.PopulateFid();
            BindingContext = myViewModel;
            myViewModel.LoadImage = (obj) => {
                var img = new Image();
                img.Source = new StreamImageSource() {
                    Stream = (token) => getstream(token)
                };
                mainstack.Children.Add(img);
            };
        }

private async Task<Stream> getstream(object token) {
            return new MemoryStream(myViewModel.myGetBytes);
        }

in View Model:

private string _fid;

public async void PopulateFid() {
            _fid = await getFid();
            LoadImg?.Invoke(true);
}

Upvotes: 1

Related Questions