Bootstrap Series – Xamarin.Forms in Prism.Forms

Welcome! This is the second entry in a series where I explore MVVM frameworks that can be used with Xamarin.Forms. As a by-product of my investigation, I’ll have a handy bootstrapped project of each framework to help you get started using the framework discussed!

tl;dr – source code

Over the course of the series, I’ll be examining the pros and cons of the following MVVM Frameworks:

  1. MVVM Light (source)
  2. Prism.Forms (source) <– This post!
  3. Xamvvm (source)

In this post, we’ll dive in to a framework that evolved from Microsoft Patterns & Practices. Prism 6 is now fully open source and has formed a very robust following which has led to a lot of great tutorials and presentations, like this one by Brian Lagunas from Evolve 2016. Its a great watch, so I won’t judge if you’d rather follow along with a vid. That said, if you prefer reading or forgot your headphones at home, I’ll dive into some of the neat MVVM features that comes with Prism.Forms in this blog post and help you create your first project using Prism.

Getting Started

It always starts with a NuGet package. Using the NuGet package explorer, a quick search for “Prism.Forms” is likely to yield the library you need. Don’t forget! You’ll need to add the package and any dependencies to each project; your PCL, and every platform you are supporting. Of course, you can always get it from the command line package manager, if you prefer, like so:

Install-Package Prism.Forms -Version 6.3.0

Prism also provides a template pack, which makes setting up your Views and ViewModels. On Visual Studio for Mac. Check out the documentation on GitHub to get it set up for Visual Studio on Mac or Windows – I highly recommend the template pack, but for the sake of rigor this post will assume the extension hasn’t been installed.

Like in the previous, MVVMLight sample, let’s configure our folder structure to be more MVVM-like once the  needed packages are done installing. The final result will look something like this:

Screen Shot 2017-09-29 at 5.08.24 PM.png

For the sake of consistency, we will try to keep the structure and content as similar to the previous entry in this series as possible. As with the MvvmLight demonstration, let’s delete the default HomePage.xaml and create a new one within our Views folder, as well as a few other pages. Notice right away that we no longer need the startup folder or the contents within: IoCConfig and ViewModelLocator. Thats because the functions handled by those classes are now part of the base class PrismApplication, which our main App.cs needs to inherit from. So, we make a quick change to the App.xaml.cs and App.xaml (if we have it), such that the xaml looks like so:

<?xml version="1.0" encoding="utf-8"?>
<prism:PrismApplication xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="PrismForms.App" xmlns:prism="clr-namespace:Prism.DryIoc;assembly=Prism.DryIoc.Forms">
    <prism:PrismApplication.Resources>
        <!-- Application resource dictionary -->
        <ResourceDictionary>

… and App inherits PrismApplication as well:

using DryIoc;
using Prism.DryIoc;
using Prism.Logging;
using Xamarin.Forms;

namespace PrismForms
{
    public partial class App : PrismApplication
    {
	public static new App Current => Application.Current as App;

        public App()
        {
            InitializeComponent();
        }

Dependency Injection

There are some abstract methods we need to implement to make the compiler happy, so let’s go ahead and build those out with some things we know will be needed down the line, such as registering our Views and setting a root page.

        /// <summary>
        /// Logging provided by <see cref="Prism.Logging"/>
        /// </summary>
        /// <value>The logger</value>
		public new ILoggerFacade Logger
		{
			get { return base.Logger; }
		}

        /// <summary>
        /// With Prism, we navigate using a URI format to preserve the stack. We can also
        /// reset or rearrange the stack by manipulating the URI, or perform "deep linking"
        /// when the app is launched with parameters (i.e - email link, push notification, etc)
        /// </summary>

		protected override void OnInitialized()
		{
            NavigationService.NavigateAsync($"Root/Navigation/{nameof(Views.HomePage)}", animated: false);
		}

        /// <summary>
        /// Registers the types. Notice that we can set the name explicitly by providing the
        /// name parameter, or just use the nameof property for the page
        /// </summary>

		protected override void RegisterTypes()
		{
            // Register Navigation page
            Container.RegisterTypeForNavigation<Views.AppShellNavigationPage>("Navigation");

            // Register Views
            Container.RegisterTypeForNavigation<Views.AppShell>("Root");
			Container.RegisterTypeForNavigation<Views.HomePage>();
            Container.RegisterTypeForNavigation<Views.SamplePage>();
			Container.RegisterTypeForNavigation<Views.SettingsPage>();
		}

Keep in mind that this won’t run until we finish setting up our Views and ViewModels, but let’s take a look at what’s going on here before moving on. Notice that within RegisterTypes, we add all of our pages, but also give a few views special names; namely, our AppShell (which, like before will be the universe within which our app lives, navigates, and behaves) and our main navigation page. We then reference these in OnInitialized to set our starting view. But, why does it look so weird?

NavigationService.NavigateAsync($"Root/Navigation/{nameof(Views.HomePage)}", animated: false);

The navigation service for Prism (which comes fully built out of the box, a huge leg up over MvvmLight IMO), treats navigation and the navigation stack . much like a web URL. As pages are added and we traverse deeper into the stack, the “URL” that describes the stack gets longer. What we are doing in the OnInitialized() method takes advantage of this fact to specify the root view (a Master/Detail page we named “Root”), set the Navigation Page on the Detail view and set the Detail View page (our Home Page from before). This is especially useful for “deep linking” – your starting view is entirely dependent on what string URL the NavigationService is passed, we can be driven by push notifications, web links, or any other avenue of entry to the app that may require a specific landing point. For this simple example however, once we’ve finished configuring our Views and ViewModels, the end result will be nearly indistinguishable from the previous example.

AppShell

Let’s start with AppShell.xaml and tell our View about the ViewModel that backs it:

<?xml version="1.0" encoding="UTF-8"?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PrismForms.Views.AppShell"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True">
    <MasterDetailPage.Master>
        <!-- Be mindful of the Title and Icon properties; they are required -->
        <NavigationPage Title="Required" Icon="slideout.png" BarTextColor="White" BarBackgroundColor="#87A3BA">
            <x:Arguments>
                <ContentPage Title="{Binding Title}" BackgroundColor="#F4F4F4">

Assuming you have the Prism Template Pack, the highlighted lines will be added for you. But, there is no harm in adding them by hand as I have done. Notice that we are relying on Prism to “Auto Wire” the View to it’s ViewModel. Because of this, we need to make sure that our Views and ViewModels follow very specific naming conventions. For example, if we create a page called HomePage.xaml, it’s ViewModel must be named HomePageViewModel. You can define your own mappings manually, but I prefer to rely on the Prism’s auto wire function, as it happens to conform to my usual MVVM naming habits. Since we are using a Master/Detail view, we need to add some stuff in the code behind of App.xaml.cs:

{
    public partial class AppShell : MasterDetailPage, IMasterDetailPageOptions
    {
        /// <summary>
        /// Gets a value indicating whether this <see cref="T:PrismForms.Views.AppShell"/> is presented after navigation.
        /// </summary>
        /// <remarks>
        /// Required by <see cref="T:Prism.Navigation.IMasterDetailPageOptions"/>
        /// </remarks>
        /// <value><c>true</c> if is presented after navigation; otherwise, <c>false</c>.</value>
        public bool IsPresentedAfterNavigation => Device.Idiom != TargetIdiom.Phone;

        public AppShell()
        {
            InitializeComponent();
        }
    }

    public class AppShellNavigationPage : NavigationPage, INavigationPageOptions, IDestructible
    {
        public AppShellNavigationPage()
        {
            BarTextColor = Color.White;
            BarBackgroundColor = Color.FromHex("#394A76");
        }

        public bool ClearNavigationStackOnNavigation
        {
            get { return false; }
        }

        public void Destroy()
        {

        }
    }
}

Here we implement a few Prism interfaces to expose Master/Detail functionality; namely, closing the menu on navigation which we achieved previously using Messaging. With that in mind, we can follow suit and add the View/ViewModel relationship to our remaining pages.

Commanding

From the perspective of the View, commanding in Prism is nearly identical to our previous example in the series. In fact, beyond the initial establishment of data context, you can essentially copy/paste the previous example directly into this new project file.

            <Button Text="Show Error Dialog" Command="{Binding ShowAlertCommand}" />
            <Button Text="Show Action Sheet" Command="{Binding ShowActionSheetCommand}" />

That said, things do look a little different from the ViewModel, but a similar pattern follows. We establish our command like so:

            ShowAlertCommand = new DelegateCommand(async () => await ShowAlert());
            ShowActionSheetCommand = new DelegateCommand(async () => await ShowActionSheet());

Unlike before, we are using DelegateCommands rather than RelayCommands, but the end result is the same – we have successfully bound ViewModel logic to the view that the user can interact with.

Navigating

Oct-02-2017 08-58-32

We already touched on Prism’s cool URL-like navigation schema, but let’s look a little more at that in practice. Like before, we’ll need to inject our navigation and dialog services into the constructor of our ViewModel. Unlike before, however, both services already have implementations we can utilize right from Prism.Forms:


        public HomePageViewModel(IPageDialogService dialogService, INavigationService navigationService) : base(navigationService)
        {

Then, we can start navigating from our ViewModel; the simplest means is to simple provide the name of the View we wish to navigate to, like so:

_navigationService.NavigateAsync($"{nameof(Views.SettingsPage}");

If, for example, we want to pass parameters, we can achieve that by providing query string “properties” to the navigation URI, which we then retrieve on the result page. Thus, from our calling page we would form out navigation URI like so:

        private async void Navigate(CopyItem parameter)
        {
            // Pass the parameters as part of the navigation string. A typical example would be to pass only the ID of an object, which
            // the resulting page would lookup/resolve via a service
            await _navigationService.NavigateAsync($"{nameof(Views.SamplePage)}?subject={parameter.Title}&content={parameter.Body}");

And use the following code block on the next page by overriding the OnNavigatedTo method that is part of the INavigationAware interface

        public override void OnNavigatedTo(NavigationParameters parameters)
        {
            this.Copy = parameters.GetValue<string>("content");
            this.Subject = parameters.GetValue<string>("subject");
        }

For a more complex objects, we could also initialize our Navigation Parameters using a strongly-typed object, like so:

		var payload = new NavigationParameters();
		payload.Add("content", parameter);

            await _navigationService.NavigateAsync($"Navigation/{nameof(Views.SamplePage)}", payload);

Using the URI scheme provided by Prism, we can also easily reset the root page of our app by providing an absolute URI. This is especially useful in scenarios like when a user is signing in or out of our app:

_navigationService.NavigateAsync($"/NewRoot/");

Show Alerts & Dialogs

Like navigating, getting user interaction using MVVM can be a bit confusing at first. In many cases, we may want to present the user with an alert dialog (such as when an error occurs, or when the app needs user confirmation), or an action sheet (such as when the app should present the user with many options).


Using Prism.Forms, the dialog service is already implemented for us; we just need to get a reference to IPageDialogService, which we injected earlier in our constructor. Once we have that reference, we can start showing dialogs! Prism.Forms provides two flavors of alert dialogs we can use – one with “accept only” buttons, and one with “confirm or deny” buttons and you can control the text displayed in either case. Let’s see what using the latter looks like:

        private async Task ShowActionSheet()
        {
            var response = await _dialogService.DisplayActionSheetAsync("Actions", "Cancel", "Destroy", new string[] { "Action1", "Action2" });

            // TODO: Like with the Error Dialog, we want to perform different actions based on the user selection
            switch (response)
            {
                case ("Action1"):
                case ("Action2"):
                default:
                    await _dialogService.DisplayAlertAsync("I did it!", $"Selected {response}", "Ok");
                    break;
            }
        }

We also have Action Sheets taken care of for us:

        private async Task ShowActionSheet()
        {
            var response = await _dialogService.DisplayActionSheetAsync("Actions", "Cancel", "Destroy", new string[] { "Action1", "Action2" });

            // TODO: Like with the Error Dialog, we want to perform different actions based on the user selection
            switch (response)
            {
                case ("Action1"):
                case ("Action2"):
                default:
                    await _dialogService.DisplayAlertAsync("I did it!", $"Selected {response}", "Ok");
                    break;
            }
        }

Conclusion

We did it! That concludes our tour of Prism in Xamarin.Forms. If you’re still craving Prism.Forms goodies, the GitHub repo has a bunch of spectacular samples specifically for Xamarin.Forms, and the documentation is top notch. For the example used in this post, feel free to browse the complete source code, or leave a comment if you have questions!

2 thoughts on “Bootstrap Series – Xamarin.Forms in Prism.Forms

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s