Build an App with Xamarin.Forms using Sitecore Mobile SDK for Xamarin – Putting it all together

StartHero

Xamarin is a true joy to work with, it’s easy and it’s all in.Net. To work with Xamarin is like being in heaven πŸ™‚
To combine it with Sitecore makes it even better, now you can keep the content from the app in Sitecore and manage it from there. Sitecore can also personalize the content for the app – how cool is that πŸ™‚

This is the third blogpost in a series on how to create an Xamarin Cross App using Sitecore Mobile SDK for Xamarin.

Here are the previous posts:
Build an App with Xamarin.Forms using Sitecore Mobile SDK for Xamarin – Getting started
Build an App with Xamarin.Forms using Sitecore Mobile SDK for Xamarin – Website and Navigation : Part 2

In this third and last post I will show you how the cross app was built.

Application – startup
Application is the startup class, her we will set the MainPage property which is where to set the initial page for the app.
It also exposes Lifecycle methods such as OnStart, OnSleep, and OnResume as well as modal navigation events. We will also setup the IOC here, I’ve choosen Autofac for this. πŸ™‚
And finally we will set the global styles here and it’s all in wonderful Xaml.

First we will have the HabitatAppModule class and this is where we will register services, viewmodels and pages. I will just show you a part of the class.

public class HabitatAppModule: Module
{
	protected override void Load (ContainerBuilder builder)
	{
		base.Load (builder);

		builder.RegisterType<SQLiteConnectionService> ()
			.As<ISQLiteConnectionService> ()
			.SingleInstance ();

		builder.RegisterType<SettingsRepository> ()
			.As<ISettingsRepository> ()
			.SingleInstance ();

        builder.RegisterType<SplashPageViewModel> ();
        builder.RegisterType<SplashPage> ();

		

HabitatAppModule on the GitHub

Next will be the Bootstrapper class, here we will call the HabitatAppModule and the container.
We will also set the MainPage by using a static property from the Application(startup class). We will start with a splash page, to make sure everything is loaded properly before we show the users the “actual” app. πŸ™‚

using Autofac;
using HabitatApp.Views.Pages;
using Xamarin.Forms;

public static class Bootstrapper
{
	public static void Run()
	{

		ContainerBuilder builder = new ContainerBuilder();
		builder.RegisterModule<HabitatAppModule>();
		HabitatApp.App.AppInstance.Container = builder.Build();

		HabitatApp.App.AppInstance.MainPage = HabitatApp.App.AppInstance.Container.Resolve<SplashPage> ();


	}
}

Bootstrapper on the GitHub

And the Application class itself, lets start with the codebehind. As you can see we call the Bootstrapper.Run to start it up.

using System;
using Xamarin.Forms;
using Autofac;

public partial class App : Application
{

	public IContainer Container;

	public static App AppInstance; 

	public App ()
	{
		AppInstance = this;

		InitializeComponent();

		Bootstrapper.Run();

	}

	protected override void OnStart ()
	{
		// Handle when your app starts
	}

	protected override void OnSleep ()
	{
		// Handle when your app sleeps
	}

	protected override void OnResume ()
	{
		// Handle when your app resumes
	}
}

App.xaml.cs on the GitHub

The xaml part of the Application. As I mentioned before this is where we will set all global styles like themes, fonts, colors etc. You can also set platform specific styles – how cool is that πŸ™‚
And finally we will have some ValueConverters.

<Application xmlns="http://xamarin.com/schemas/2014/forms" 
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	xmlns:conv="clr-namespace:HabitatApp.Converters;assembly=HabitatApp"  
	x:Class="HabitatApp.App">
	<Application.Resources>
		<ResourceDictionary>
			<OnPlatform x:TypeArguments="Font" Android="40" iOS="40" WinPhone="Large" x:Key="HeaderFontSize" />
			<OnPlatform x:TypeArguments="Color" Android="White" iOS="White" WinPhone="White" x:Key="PrimaryTextColor" />
			<OnPlatform x:TypeArguments="Color" Android="Black" iOS="Black" WinPhone="Black" x:Key="ArticleTextColor" />
			<OnPlatform x:TypeArguments="Color" Android="#2E384E" iOS="#2E384E" WinPhone="#2E384E" x:Key="HabitatBackgroundColor" />
			<OnPlatform x:TypeArguments="Color" Android="White" iOS="White" WinPhone="White" x:Key="HabitatMenuColor" />
			<OnPlatform x:TypeArguments="Color" Android="#c8244f" iOS="#c8244f" WinPhone="#c8244f" x:Key="HabitatRedColor" />

			<conv:CachedMediaToImageSourceConverter x:Key="imageConverter" />
			<conv:HtmlConverter x:Key="htmlConverter" />
			<conv:HtmlSourceConverter x:Key="htmlSourceConverter" />
			<conv:BooleanNegationConverter x:Key="boolNegationConverter" />

			<Style x:Key="HeaderStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource PrimaryTextColor}" />
				<Setter Property="FontSize" Value="40" />
				<Setter Property="FontFamily" Value="HelveticaNeue-CondensedBlack" />
			</Style>
			<Style x:Key="HabitatWhiteHeaderStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource PrimaryTextColor}" />
				<Setter Property="FontSize" Value="40" />
				<Setter Property="FontFamily" Value="HelveticaNeue-CondensedBlack" />
			</Style>
			<Style x:Key="HabitatRedHeaderStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource HabitatRedColor}" />
				<Setter Property="FontSize" Value="40" />
				<Setter Property="FontFamily" Value="HelveticaNeue-CondensedBlack" />
			</Style>
			<Style x:Key="HabitatWhiteSubtitleStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource PrimaryTextColor}" />
				<Setter Property="FontSize" Value="20" />
				<Setter Property="FontFamily" Value="HelveticaNeue" />
			</Style>
			<Style x:Key="HabitatRedSubtitleStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource HabitatRedColor}" />
				<Setter Property="FontSize" Value="20" />
				<Setter Property="FontFamily" Value="HelveticaNeue" />
			</Style>
			<Style x:Key="ArticleHeaderStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource ArticleTextColor}" />
				<Setter Property="FontSize" Value="30" />
				<Setter Property="FontFamily" Value="HelveticaNeue" />
			</Style>
			<Style x:Key="ArticleTextStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource HabitatBackgroundColor}" />
				<Setter Property="FontSize" Value="20" />
				<Setter Property="FontFamily" Value="HelveticaNeue" />
			</Style>
			<Style x:Key="ArticleLightTextStyle" TargetType="Label">
				<Setter Property="TextColor" Value="{StaticResource PrimaryTextColor}" />
				<Setter Property="FontSize" Value="15" />
				<Setter Property="FontFamily" Value="HelveticaNeue" />
			</Style>
			<Style x:Key="HabitatValidationTextStyle" TargetType="Label">
		        <Setter Property="HorizontalOptions" Value="Start" />
		        <Setter Property="VerticalOptions" Value="Center" />
		        <Setter Property="FontSize" Value="Micro" />
		        <Setter Property="FontAttributes" Value="Italic" />
      		</Style>
		
		</ResourceDictionary>
	</Application.Resources>
</Application>

App.xaml on the GitHub

So what is a ValueConverter class? See it as an helper class which is used in xaml pages if you want to databind two properties that have incompatible types. Then you need a piece of code in between that converts the value from source to target type and back. This piece of code is called ValueConverter.

For instance the CachedMediaToImageSourceConverter class, it is needed when to present images on the xaml pages. The images are stored as blobs in the local database(SQlite). A typical call to the value converter class, CachedMediaToImageSourceConverter, could look something like this:

<Image Source="{Binding Media, Converter={StaticResource imageConverter}}" Aspect="AspectFill"> 

Here is the CachedMediaToImageSourceConverter class. In the example above a viewmodel with the property “Media” which contains a byte array.
The value converter class will then convert the byte array to an image.

using Xamarin.Forms;
using System;
using System.Globalization;
using HabitatApp.Models;
using System.IO;
using Xamarin.Forms.Xaml;


public class CachedMediaToImageSourceConverter : IMarkupExtension, IValueConverter
{

	public  object Convert (object value, Type targetType, object parameter, CultureInfo culture)
	{
		CachedMedia cachedMedia = (CachedMedia)value;

		if (cachedMedia == null)
			return null;    

		if (cachedMedia.MediaData != null)
			return ImageFromBytes (cachedMedia);

		return ImageFromUrl (cachedMedia.Url);

	}

	private ImageSource ImageFromBytes (CachedMedia cachedMedia)
	{
		return ImageSource.FromStream (() => new MemoryStream (cachedMedia.MediaData));
	}

	private ImageSource ImageFromUrl (string url)
	{

		Uri outUri;

		if (Uri.TryCreate (url, UriKind.Absolute, out outUri))
			return ImageSource.FromUri (outUri);

		return ImageSource.FromResource (url);
	}


	public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		throw new NotImplementedException ();
	}


	public object ProvideValue (IServiceProvider serviceProvider)
	{
		return this;
	}

}

You can find the rest of the value converters here

Pages – The Xaml pages
The best of it all when it comes to Xamarin.Forms is of course the “forms” – the xaml pages.
How does it work in our app?
Instead of going through all pages/views in the app, let’s have a look at some of them. How about the ListParentPage.xaml?
SitecoreListParentpage
This page will list a “page item’s” children (Sitecore).

In codebehind on the xaml page the constructor will instantiate the viewmodel – ListParentPageViewModel.
Here we will also set the BindingContext property so we from the xaml page can bind to the viewmodel. But as you can see we have very little logic here, it’s all in the viewmodel. Everything is clean πŸ™‚

using Xamarin.Forms;
using Autofac;
using HabitatApp.ViewModels.Pages;
using Xamarin.Forms.Xaml;

[XamlCompilation (XamlCompilationOptions.Compile)]
public partial class ListParentPage : ContentPage
{
	private readonly ListParentPageViewModel _listParentPageViewModel;

	public ListParentPage() : this(App.AppInstance.Container.Resolve<ListParentPageViewModel>())
	{

	}

	public ListParentPage (ListParentPageViewModel listParentPageViewModel)
	{
		InitializeComponent ();

		_listParentPageViewModel = listParentPageViewModel;

		_listParentPageViewModel.ConnectedToPage = this;

		BindingContext = _listParentPageViewModel;

		//We need it for Android
		MediaItemsListView.ItemSelected += (s, e) => {
			MediaItemsListView.SelectedItem = null; 
		};

	}


	protected override async void OnDisappearing ()
	{

		base.OnDisappearing ();
	}
} 

Lets have a quick look at the viewmodel – ListParentPageViewModel.
It contains a number of observable properties for the XAML page and it’s all about binding πŸ™‚

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Sitecore.MobileSDK.API.Items;
using System.Windows.Input;
using Xamarin.Forms;
using HabitatApp.Services;
using HabitatApp.Models;
using HabitatApp.Extensions;
using HabitatApp.Repositories;

public class ListParentPageViewModel : ViewModelBase
{
	private readonly IListItemService _listItemService;
	private readonly INavigationService _navigationService;
	private readonly ICachedMediaRepository _cachedMediaRepository;


	public ListParentPageViewModel (IListItemService listItemService, INavigationService navigationService, ICachedMediaRepository cachedMediaRepository)
	{
		_listItemService = listItemService;
		_navigationService = navigationService;
		_cachedMediaRepository = cachedMediaRepository;

	}

	private CachedMedia _media;

	public CachedMedia ContentMedia {
		get {
			return _media;
		}
		set { SetProperty (ref _media, value); }

	}

	private string _contentHeader = string.Empty;

	public string ContentHeader {
		get {
			return _contentHeader;
		}
		set { SetProperty (ref _contentHeader, value); }
	}

	private string _contentSummary = string.Empty;

	public string ContentSummary {
		get {
			return _contentSummary;
		}
		set { SetProperty (ref _contentSummary, value); }
	}

	private ObservableCollection<Tuple<ListItem,ListItem>> _listItems = new ObservableCollection<Tuple<ListItem,ListItem>> ();

	public ObservableCollection<Tuple<ListItem,ListItem>> ListItems {
		get {
			return _listItems;
		}
		set { 
			SetProperty (ref _listItems, value); 
		}
	}

	private ObservableCollection<ListItem> _list = new ObservableCollection<ListItem> ();

	public ObservableCollection<ListItem> ListOfItems {
		get {
			return _list;
		}
		set { 
			SetProperty (ref _list, value); 
		}
	}

	private Command _linkSelectedCommand;
	public ICommand LinkSelectedCommand
	{
		get
		{
			if (_linkSelectedCommand == null)
			{
				_linkSelectedCommand = new Command (async (parameter) =>  {
					string itemId = (string)parameter;

					if(string.IsNullOrWhiteSpace(itemId))
						return;

					await _navigationService.NavigateToPageByItemId(ConnectedToPage, itemId);

				});
			}

			return _linkSelectedCommand;
		}
	}

	/// <summary>
	/// Entering page
	/// </summary>
	/// <returns>The async.</returns>
	public async override Task LoadAsync ()
	{

		SetBusy ("Loading");

		PageData pageData = base.PageContext;

		await SetData (pageData);

		ClearBusy ();


	}

	private async Task SetData(PageData pageData){
		
		ISitecoreItem item = pageData.ItemContext.FirstOrDefault ();

		base.Title = item.GetValueFromField (Constants.Sitecore.Fields.PageContent.Title);

		ContentHeader = item.GetValueFromField(Constants.Sitecore.Fields.PageContent.Title);
		ContentSummary = item.GetValueFromField(Constants.Sitecore.Fields.PageContent.Summary);
		ContentMedia =  await _cachedMediaRepository.GetCache(item.GetImageUrlFromMediaField (Constants.Sitecore.Fields.PageContent.Image));

		IEnumerable<ListItem> listItems = await _listItemService.GenerateListItemsFromChildren(pageData.DataSourceFromChildren);

		ListItems = listItems.ToList().AsPairsSafe ().ToObservableCollection ();


	}


}

If you noticed in the screendump, the page lists a number of images – side by side. For that we will have a list of TUPLE items:

ObservableCollection<Tuple<ListItem,ListItem>>

To get the list of items we will call the ListItemService
Here is the method we are using:

public async Task<IEnumerable<ListItem>> GenerateListItemsFromChildren(ScItemsResponse itemsResponse)
{

	List<ListItem> list = new List<ListItem> ();


	for (int i = 0; i < itemsResponse.ResultCount; i++) {

		ISitecoreItem item = itemsResponse[i];

		if (item == null)
			continue;

		ListItem listlItem = new ListItem{ 
			Header = item.GetValueFromField (Constants.Sitecore.Fields.PageContent.Title),
			Text = item.GetValueFromField (Constants.Sitecore.Fields.PageContent.Summary),
			NavigationItem = item.Id,
			NavigationText = item.GetValueFromField (Constants.Sitecore.Fields.PageContent.Title),
			SitecoreItem = item,
			Media =  await _cachedMediaRepository.GetCache(item.GetImageUrlFromMediaField(Constants.Sitecore.Fields.PageContent.Image))	

		};

		list.Add (listlItem);

	}

	return list;
}

Then we will use an extension method to convert the list to a “tuple” list:

ListItems = listItems.ToList().AsPairsSafe ().ToObservableCollection ();

Here is the method in IEnumerableExtensions

public static IEnumerable<Tuple<T, T>> AsPairsSafe<T>(this List<T> list)
{
	int index = 0;

	while (index < list.Count())
	{
		if (index + 1 >= list.Count())
			yield break;

		yield return new Tuple<T,T>(list[index++],  list[index++]);
	}

}

Now finally, let us all have a look at the XAML page – ListParentPage.xaml. Xaml is great and working with bindings is a bliss πŸ™‚ (I really miss that in ASP.Net MVC).
If you look at the code where the backgroundcolor is set – BackgroundColor=”{DynamicResource HabitatBackgroundColor}”. Here we use the global styles from the Application.xaml I showed you earlier.

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
	x:Class="HabitatApp.Views.Pages.ListParentPage" 
	xmlns:control="clr-namespace:HabitatApp.Views.Controls;assembly=HabitatApp" 
	Title="{Binding Title}" 
	Icon="{Binding Icon}"
	x:Name="rootPage" 
	xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms" 
	BackgroundColor="{DynamicResource HabitatBackgroundColor}">
	<ListView x:Name="MediaItemsListView" 
		BackgroundColor="{DynamicResource HabitatBackgroundColor}" 
		ItemsSource="{Binding ListItems}" 
		SeparatorVisibility="None" 
		HorizontalOptions="FillAndExpand" 
		Header="{Binding}" 
		RowHeight="200" 
		CachingStrategy="RecycleElement">
		<ListView.HeaderTemplate>
			<DataTemplate>
				<Grid>
					<Grid.RowDefinitions>
						<RowDefinition Height="200" />
						<RowDefinition Height="*" />
					</Grid.RowDefinitions>
					<Grid Grid.Row="0" Padding="10,0,10,0">
						<Image Aspect="AspectFill" Source="{Binding ContentMedia, Converter={StaticResource imageConverter}}" />
						<StackLayout Padding="10,10,10,10">
							<StackLayout Opacity="0.7" BackgroundColor="{DynamicResource HabitatBackgroundColor}" Padding="10" HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand" Orientation="Vertical">
								<Label x:Name="headerLabel" VerticalOptions="EndAndExpand" Text="{Binding ContentHeader}" Style="{DynamicResource HabitatWhiteHeaderStyle}" />
							</StackLayout>
						</StackLayout>
					</Grid>
					<StackLayout Padding="10,0,10,10" Grid.Row="1" HorizontalOptions="Start" VerticalOptions="EndAndExpand" Orientation="Horizontal">
						<Label x:Name="subtitleLabel" VerticalOptions="StartAndExpand" Style="{DynamicResource ArticleLightTextStyle}" HorizontalOptions="StartAndExpand" Text="{Binding ContentSummary, Converter={StaticResource htmlConverter}}" />
					</StackLayout>
				</Grid>
			</DataTemplate>
		</ListView.HeaderTemplate>
		<ListView.ItemTemplate>
			<DataTemplate>
				<control:ViewCellNonSelectable>
					<Grid Padding="10,0,10,0">
						<Grid.RowDefinitions>
							<RowDefinition Height="200" />
						</Grid.RowDefinitions>
						<Grid.ColumnDefinitions>
							<ColumnDefinition Width="*" />
							<ColumnDefinition Width="*" />
						</Grid.ColumnDefinitions>
						<Grid Grid.Row="0" Grid.Column="0" Padding="0,0,0,5">
							<ffimageloading:CachedImage Opacity="0.9"
								Aspect="AspectFill" 
								DownsampleWidth="100" 
								DownsampleHeight="100" 
								Source="{Binding Item1.Media, Converter={StaticResource imageConverter}}">
							</ffimageloading:CachedImage>
							<StackLayout Padding="10">
								<StackLayout.GestureRecognizers>
										<TapGestureRecognizer Command="{Binding Source={x:Reference rootPage}, Path=BindingContext.LinkSelectedCommand}" CommandParameter="{Binding Item1.NavigationItem}" />
								</StackLayout.GestureRecognizers>
								<Label VerticalOptions="EndAndExpand" Text="{Binding Item1.Header}" Style="{DynamicResource ArticleLightTextStyle}" />
							</StackLayout>
						</Grid>
						<Grid Grid.Row="0" Grid.Column="1" Padding="0,0,0,5">
							<ffimageloading:CachedImage Opacity="0.9"
								Aspect="AspectFill" 
								DownsampleWidth="100" 
								DownsampleHeight="100" 
								Source="{Binding Item2.Media, Converter={StaticResource imageConverter}}">
							</ffimageloading:CachedImage>
							<StackLayout Padding="10">
								<StackLayout.GestureRecognizers>
										<TapGestureRecognizer Command="{Binding Source={x:Reference rootPage}, Path=BindingContext.LinkSelectedCommand}" CommandParameter="{Binding Item2.NavigationItem}" />
								</StackLayout.GestureRecognizers>
								<Label VerticalOptions="EndAndExpand" Text="{Binding Item2.Header}" Style="{DynamicResource ArticleLightTextStyle}" />
							</StackLayout>

						</Grid>
					</Grid>
				</control:ViewCellNonSelectable>
			</DataTemplate>
		</ListView.ItemTemplate>
	</ListView>
</ContentPage>  

To list the images we use a listview.
As datasource(ItemsSource) to the listview we will BIND the ListItems property(from the viewmodel).
We also have a header in the listview – ListView.HeaderTemplate.

In the ListView.ItemTemplate element we will render the images, side by side. To show the image we will use a ValueConverter(convert blob to image)

 
Source="{Binding Item1.Media, Converter={StaticResource imageConverter}}"> 

In order to make the images to load faster and the listview to scroll smothly, I used a very nice component called FFImageLoading

And when the users touch/tap an image we will navigate them to next page. In order to make it work we will use TapGestureRecognizer.

<StackLayout.GestureRecognizers>
	<TapGestureRecognizer Command="{Binding Source={x:Reference rootPage}, Path=BindingContext.LinkSelectedCommand}" CommandParameter="{Binding Item1.NavigationItem}" />
</StackLayout.GestureRecognizers>

TapGestureRecognizer will then call the LinkSelectedCommand in the viewmodel.

public ICommand LinkSelectedCommand
{
	get
	{
		if (_linkSelectedCommand == null)
		{
			_linkSelectedCommand = new Command (async (parameter) =>  {
				string itemId = (string)parameter;

				if(string.IsNullOrWhiteSpace(itemId))
					return;

				await _navigationService.NavigateToPageByItemId(ConnectedToPage, itemId);

			});
		}

		return _linkSelectedCommand;
	}
}

And here we call the NavigationService to navigate to “selected” page.

There are more stuff I would like to go through with you guys but the post will be to long and we all know what happens if a post is to long – no one will read it 😦

I will have to end here, I think there will be a fourth post in this series. I need to show you how cool it is to work with Behaviors in Xamarin.

That’s all for now folks πŸ™‚


Leave a comment

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