Xamarin – Display a loading graphic when performing an async operation

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

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

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

</p>
<p>IsBusy = true;<br />
var userId = await ApiLogin();<br />
IsBusy = false;</p>
<p>

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

<br />
    public class InverseBoolConverter : IValueConverter<br />
    {<br />
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)<br />
        {<br />
            if (!(value is bool))<br />
            {<br />
                throw new InvalidOperationException(&quot;The target must be a boolean&quot;);<br />
            }</p>
<p>            return !(bool)value;<br />
        }</p>
<p>        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)<br />
        {<br />
            return !(bool)value;<br />
        }<br />
    }<br />

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

<br />
 &lt;ContentPage.Resources&gt;<br />
        &lt;ResourceDictionary&gt;<br />
            &lt;converters:InverseBoolConverter x:Key=&quot;inverter&quot;/&gt;<br />
...<br />
        &lt;/ResourceDictionary&gt;<br />
&lt;/ContentPage.Resources&gt;<br />

This is then referenced like IsVisible=”{Binding IsBusy, Converter={StaticResource inverter}}”

<br />
&lt;Grid&gt;<br />
    &lt;Grid.RowDefinitions&gt;<br />
        &lt;RowDefinition Height=&quot;Auto&quot; /&gt;<br />
        &lt;RowDefinition Height=&quot;*&quot; /&gt;<br />
    &lt;/Grid.RowDefinitions&gt;<br />
    &lt;StackLayout VerticalOptions=&quot;FillAndExpand&quot; HorizontalOptions=&quot;Fill&quot;&gt;<br />
        &lt;StackLayout Orientation=&quot;Horizontal&quot; HorizontalOptions=&quot;Center&quot; VerticalOptions=&quot;Center&quot;&gt;<br />
            &lt;ContentView Padding=&quot;0,30,0,25&quot; VerticalOptions=&quot;FillAndExpand&quot;&gt;<br />
                &lt;Image Source=&quot;Logo.png&quot; VerticalOptions=&quot;Center&quot; HeightRequest=&quot;75&quot; /&gt;<br />
            &lt;/ContentView&gt;<br />
        &lt;/StackLayout&gt;<br />
    &lt;/StackLayout&gt;</p>
<p>    &lt;ActivityIndicator Color=&quot;Black&quot; IsRunning=&quot;{Binding IsBusy}&quot; IsVisible=&quot;{Binding IsBusy}&quot; VerticalOptions=&quot;CenterAndExpand&quot; HorizontalOptions=&quot;CenterAndExpand&quot; Grid.Row=&quot;1&quot; /&gt;</p>
<p>       &lt;ScrollView Grid.Row=&quot;1&quot; IsVisible=&quot;{Binding IsBusy, Converter={StaticResource inverter}}&quot;&gt;<br />
&lt;!-- the login form, etc --&gt;<br />
       &lt;/ScrollView&gt;<br />
&lt;/Grid&gt;<br />

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)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.