Still learning Xamarin. I have built a login page, validation, called a web api to login and stored credentials after the user has logged in. I now wanted to show a loading graphic whilst the web api is being hit.
Xamarin has a built in control called ActivityIndicator.
I defined a new property in my ViewModel to determine when to show the ActivityIndicator
bool isBusy = false; public bool IsBusy { get { return isBusy; } set { SetProperty(ref isBusy, value); } }
Without going too far off topic, this property was already included in my project by Visual Studio in a BaseViewModel. The call to SetProperty(ref isBusy, value); looks to be there to only raise OnPropertyChanged if the property has actually changed. So it could have been replaced with isBusy = value; OnPropertyChanged(nameof(IsBusy));
Then when we hit the API
IsBusy = true; var userId = await ApiLogin(); IsBusy = false;
We can now show the ActivityIndicator when this is true. But for the form, which is shown when the ActivityIndicator isn’t, setting a binding like {Binding !IsBusy} does not work, nor does {Binding IsBusy.Equals(false)}
To do this I used a ValueConverter
public class InverseBoolConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (!(value is bool)) { throw new InvalidOperationException("The target must be a boolean"); } return !(bool)value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return !(bool)value; } }
To use this in the view I had to add an xmlns – xmlns:converters=”clr-namespace:VCReportMobile.Converters”
And add a reference to the converter in the ResourceDictionary
<ContentPage.Resources> <ResourceDictionary> <converters:InverseBoolConverter x:Key="inverter"/> ... </ResourceDictionary> </ContentPage.Resources>
This is then referenced like IsVisible=”{Binding IsBusy, Converter={StaticResource inverter}}”
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="Fill"> <StackLayout Orientation="Horizontal" HorizontalOptions="Center" VerticalOptions="Center"> <ContentView Padding="0,30,0,25" VerticalOptions="FillAndExpand"> <Image Source="Logo.png" VerticalOptions="Center" HeightRequest="75" /> </ContentView> </StackLayout> </StackLayout> <ActivityIndicator Color="Black" IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" Grid.Row="1" /> <ScrollView Grid.Row="1" IsVisible="{Binding IsBusy, Converter={StaticResource inverter}}"> <!-- the login form, etc --> </ScrollView> </Grid>
The ActivityIndicator and the ScrollView both have Grid.Row=”1″, but since only one is visible at any time then it works.
Update
If you need to use the InverseBoolConverter on more than 1 page, then it can be placed in the App.xaml (along with it’s xmlns)