StackLayout isVisible parameter never gets updated from Binding in Xamarin.Forms

Problem

My StackLayout is always visible. The isBusy seems to work because my ActivityIndicator is not running.

Version

I use Xamarin.Forms in xamarin.forms.4.6.0.494-pre2. I need to run the pre release version due to the Surface Duo SDK which requires this kind of alpha Xamarin.Forms Version.

Code Base ViewModel

bool isBusy = false;
public bool IsBusy
{
    get { return isBusy; }
    set { SetProperty(ref isBusy, value); }
}

Code Page View Model

public void LoadData()
{
    IsBusy = true;
    LoadAsync();
    IsBusy = false;
}

Code XAML

<StackLayout IsVisible="{Binding IsBusy}">
    <!-- Animated spinner -->
    <ActivityIndicator Color="{StaticResource PrimaryReduced}" IsRunning="{Binding IsBusy}" />
    <!-- Text -->
    <Label Text="Loading ..." />
</StackLayout>

Update: Other things I tried

I read a lot that this was a malfunction behavior in prior versions of Xamarin.Forms. That's why I tried to workaround this behavior by setting the opacity value to 0 of my StackLayout.

Opacity="{Binding Path=IsBusy, Converter={StaticResource BoolToLoadingViewOpacityValueConverter}}"

But this custom converter never gets called.

Update 2: Source code

2 answers

  • answered 2020-03-31 09:14 Sven-Michael Stübe

    Your IsBusy is set to false immediately, because you do not await your loading methods (fire and forget).

    Actions needed:

    • make LoadData() async
    • change return of your loading methods from void to Task
    • await the loading methods in your LoadData()
    • read some articles about "why to avoid async void"

    See:

    public class ItemsViewModel : BaseViewModel
    {
        public async Task LoadData()
        {
            IsBusy = true;
    
            await LoadTweetsAsync();
            await LoadArticlesAsync();
            await LoadVideosAsync();
    
            // or Task.WaitAll(LoadTweetsAsync(), LoadArticlesAsync(), LoadVideosAsync()); if it should be done im parallel
    
            IsBusy = false;
        }
    
        private async Task LoadTweetsAsync()
        {
        }
    
        private async Task LoadArticlesAsync()
        {
        }
    }
    

  • answered 2020-03-31 12:52 Tobonaut

    Thanks all of you. It was a mix of what @sven-michael-stübe and @benl said.

    • I forgot that the overall page has no view model. I moved the loading view to the list view and not the parent container.

    • My source did not await the finishing of the tasks due to a wrong usage / understanding of the await operator.