Loading Indicators and Automatic ViewModel Refresh in Xamarin Forms

This post continues the series on building a cross-platform app with Xamarin Forms. Although the focus is to demonstrate using Azure Mobile Services, there is a lot that we can do first to improve the user experience of the app. Today we will look at how easy it is to add a loading indicator as well as automatically refresh the app views when changes are made.

ActivityIndicator

Xamarin Forms includes a handy ActivityIndicator control which can be used to indicate that a long-running process is occurring. This is perfect for the Rebate Reminder app, since it has to fetch the data from Azure Mobile Services. Staying true to the MVVM pattern and DataBinding support of Xamarin Forms, I've simply added a new property to my base ViewModel so that it is available to all my views as needed:

private bool isLoading; public bool IsLoading { get { return isLoading; } set { isLoading = value; NotifyPropertyChanged(); } }

Using the control on the page is as simple as adding the XAML markup and binding it to the IsLoading property:

<ActivityIndicator IsRunning="{Binding IsLoading}" IsVisible="{Binding IsLoading}" Color="{x:Static common:ColorResources.ActivityIndicator}" />

Notice that here I'm using a global resource (see the previous post for more: Creating Global Resources for Xamarin Forms Apps) to define the Color property of the control. This is a required property, because although it is ignored by Android and Windows Phone (which instead use the system default color settings), it is required by iOS, and omitting this property will prevent the control from being visible on that platform. Now we simply need to toggle the value of the IsLoading property to show until after the operation is complete, which is a breeze thanks to Xamarin's support for async and await. Here's an example of the updated method that loads Stores from Azure:

private async Task ExecuteLoadItemsCommand() { if (IsLoading) return; IsLoading = true;

try { 
    var stores = await client.GetTable<Store>().ToListAsync(); 
    Stores.Clear(); 
    foreach (var store in stores) { Stores.Add(store); } 
} catch (Exception ex) { var page = new ContentPage(); var result = page.DisplayAlert("Error", "Error loading data. Please check connectivity and try again.", "OK", null); } 
IsLoading = false; 
}

We can now update each Azure call in this fashion to display the loading indicator any time a long-running process is executing. Launching the apps once again reveals the new loading indicator, custom-tailored to each device and giving the user a more complete, consistent experience. However, we still have one outstanding issue, which is the fact that newly added or updated items are not immediately and automatically reflected in the app. Let's look at how we can solve this problem with another helpful feature available in Xamarin Forms.

Xamarin Forms MessagingCenter

Xamarin Forms includes a simple Messaging Center Service which allows components to easily communicate with each other using a simple Message contract. This feature makes it easy to push notifications from one View to another, such as to notify the previous ViewModel that data has been updated so the data can be refreshed. The syntax for this is quite simple. In our case we only need to have our main ViewModel, which contains the data to be updated, to Subscribe to a specific message, which we'll call "Refresh". Here's a sample of the code in the Stores View that subscribes to the event, refreshing its data when it is triggered:

public StoresView() { InitializeComponent();

this.ViewModel = new StoresViewModel(App.client); 
//... 
MessagingCenter.Subscribe<Store>(this, "Refresh", (s) => { this.ViewModel.LoadItemsCommand.Execute(null); }); }

When the data is modified in the subsequent AddEditView, we can simply Push a message with the same "Refresh" name, so that it is intercepted by the StoreView. Here's the code from the StoreAddEditViewModel that sends the notification on completion of the Add or Edit action:

private async Task ExecuteAddEditCommand() { if (IsLoading) return; IsLoading = true;

try { 
    var stores = client.GetTable<Store>(); 
    if (CurrentStore.Id == Guid.Empty) 
        await stores.InsertAsync(CurrentStore); 
    else 
        await stores.UpdateAsync(CurrentStore); 
    MessagingCenter.Send(CurrentStore, "Refresh"); 
} catch (Exception ex) { 
    var page = new ContentPage(); page.DisplayAlert("Error", "Error saving data. Please check connectivity and try again.", "OK", null); 
} 
IsLoading = false; 
} 

Now any time a new Store is added to the list, the main Store List View will immediately be refreshed and up to date, and because of the IsLoading property we defined above, even shows the ActivityIndicator again while it refreshes.  Adding these methods throughout the app allows the different views to stay up to date whenever new items are created, updated, or deleted, completing the basic user experience for the app on all three platforms.

Summary and Next Steps

The ActivityIndicator and MessagingCenter are one of the many tools exposed by Xamarin that make it simple and intuitive to deliver a consistent user experience across any device. By leveraging these tools we were able to quickly close the gaps in our app from a single code base.

Get the CODE!

Now that we've got these basic (but important!) features in place, we can refocus our attention back to completing the integration of Rebate Reminder with Azure Mobile Services. In our next post we'll see how you can easily add authentication using any of the supported social providers (Facebook, Twitter, Google, and Microsoft). Until then, thanks for reading and as always, I hope this was helpful!

Enjoyed this post and/or found it useful?
Tagged with:
SelArom Dot Net Profile Image
SelAromDotNet

Josh loves all things Microsoft and Windows, and develops solutions for Web, Desktop and Mobile using the .NET Framework, Azure, UWP and everything else in the Microsoft Stack.

His other passion is music, and in his spare time Josh spins and produces electronic music under the name DJ SelArom.



Scroll to top