Bootstrap Series – Xamarin.Forms in Prism.Forms

Update March 13: Prism 7.0 is now live! While many of the concepts in this post still apply, please review the full set of changes on Brian Lagunas’ Blog. I’ve prepared a follow up post on how to upgrade your existing apps you can view here


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 7.1.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&quot; xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot; x:Class="PrismForms.App" xmlns:prism="clr-namespace:Prism.DryIoc;assembly=Prism.DryIoc.Forms">
<prism:PrismApplication.Resources>
<!– Application resource dictionary –>
<ResourceDictionary>
</ResourceDictionary>
</prism:PrismApplication.Resources>
</prism:PrismApplication>

view raw

App.xaml

hosted with ❤ by GitHub

… and App inherits PrismApplication as well:


using DryIoc;
using Prism;
using Prism.DryIoc;
using Prism.Ioc;
using Prism.Logging;
using Xamarin.Forms;
namespace PrismForms
{
public partial class App : PrismApplication
{
/// <summary>
/// Initializes a new instance of the <see cref="T:PrismForms.App"/> class.
/// </summary>
/// <remarks>
/// Used when no additional parmeters are needed
/// </remarks>
public App()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:PrismForms.App"/> class.
/// </summary>
/// <param name="platformInitializer">Platform initializer.</param>
/// <remarks>
/// Used when platfrom specific initializer is required – this will be used if you are using
/// very platform-focused libraries like ARKit that you want to resolve using DI
/// </remarks>
public App(IPlatformInitializer platformInitializer) : base(platformInitializer)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:PrismForms.App"/> class.
/// </summary>
/// <param name="platformInitializer">Platform initializer.</param>
/// <param name="setFormsDependencyResolver">If set to <c>true</c> set forms dependency resolver.</param>
public App(IPlatformInitializer platformInitializer, bool setFormsDependencyResolver) : base(platformInitializer, setFormsDependencyResolver)
{
}
public static new App Current => Application.Current as App;
/* Omitted */
}

view raw

App.xaml.cs

hosted with ❤ by GitHub

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 this.Logger; } // Prism 7
}
/// <summary>
/// With Prism, we navigate using a URI format to preserve the stack. We can also
/// reset or rearrage 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()
{
InitializeComponent();
NavigationService.NavigateAsync($"myapp:///Root/Navigation/{nameof(Views.HomePage)}");
}
/// <summary>
/// Registers the types. Notice that we can set the name explicity by providing the
/// name parameter, or just use the nameof property for the page
/// </summary>
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// Register Navigation page
containerRegistry.RegisterForNavigation<Views.AppShellNavigationPage>("Navigation");
// Register Views
containerRegistry.RegisterForNavigation<Views.AppShell>("Root");
containerRegistry.RegisterForNavigation<Views.HomePage>();
containerRegistry.RegisterForNavigation<Views.SamplePage>();
containerRegistry.RegisterForNavigation<Views.SettingsPage>();
}

view raw

App.xaml.cs

hosted with ❤ by GitHub

Unless you have a single page application, be sure to include a NavigationPage in your initial stack. Here’s what this looks like in our case, where we’re utilizing the Master Detail layout:

NavigationService.NavigateAsync($”myapp:///Root/Navigation/{nameof(Views.HomePage)});

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&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot;
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">
<ListView Grid.Row="1" ItemsSource="{Binding MenuItems}" HasUnevenRows="true" SeparatorVisibility="None" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" BackgroundColor="Transparent">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10,0" Spacing="0">
<StackLayout Orientation="Horizontal">
<Image HeightRequest="50" WidthRequest="50" Source="{Binding Image}" />
<Label HeightRequest="50" Text="{Binding Title}" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" />
</StackLayout>
<BoxView Style="{StaticResource DividerBoxView}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Master>
</MasterDetailPage>

view raw

AppShell.xaml

hosted with ❤ by GitHub

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:


using System;
using System.Collections.Generic;
using Prism.Navigation;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace PrismForms.Views
{
public partial class AppShell : MasterDetailPage, IMasterDetailPageOptions
{
public AppShell()
{
InitializeComponent();
}
/// <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 class AppShellNavigationPage : Xamarin.Forms.NavigationPage, INavigationPageOptions, IDestructible
{
public AppShellNavigationPage()
{
BarTextColor = (Color)App.Current.Resources["White"];
BarBackgroundColor = (Color) App.Current.Resources["BrandColor"];
}
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.


<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms&quot; xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot; x:Class="PrismForms.Views.HomePage" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" prism:ViewModelLocator.AutowireViewModel="True" Title="{Binding Title}">
<ContentPage.Content>
<StackLayout Padding="10,20">
<ListView SelectedItem="{Binding SelectedItem, Mode=TwoWay}" ItemsSource="{Binding CopyItems}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Title}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Show Error Dialog" Command="{Binding ShowAlertCommand}" />
<Button Text="Show Action Sheet" Command="{Binding ShowActionSheetCommand}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

view raw

HomePage.xaml

hosted with ❤ by GitHub

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());
            SelectItemCommand = new DelegateCommand<CopyItem>((param) => Navigate(param));

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)
        {
            this.Title = “Home”;
            _dialogService = dialogService;

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:

this._navigationService.NavigateAsync($”Navigation/{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}");
// Or, we can declare them explicity using a strongly typed object, which is ideal for passing
// a more complex model that the next page won't lookup on its own
/*
var payload = new NavigationParameters();
payload.Add("content", parameter);
await _navigationService.NavigateAsync($"Navigation/{nameof(Views.SamplePage)}", payload);
*/
}

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(INavigationParameters 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:


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}");
// Or, we can declare them explicity using a strongly typed object, which is ideal for passing
// a more complex model that the next page won't lookup on its own
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:

await _navigationService.NavigateAsync($”/NewRoot/”);

Also note that we can replace the previous page (or series of pages, using this syntax:

            await _navigationService.NavigateAsync($”../PageToReplacePrevious”); // replace previous
            await _navigationService.NavigateAsync($”../../PageToReplacePrevious”); // replace two previous

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 ShowAlert()
{
var response = await _dialogService.DisplayAlertAsync("Yikes!", "This is a fake error to demonstrate the alert", "Retry", "Cancel");
if (response)
{
// TODO: If response is true, the user wants to "retry" the action.
// Do something here to attempt the action again
}
else
{
// TODO: If response is false, the user has canceled the attempted action
// So perform an action here that makes sense; we may want to naviagte to
// a previous page or just sit idle
}
}

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!

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

Leave a comment