Build an App with Xamarin.Forms using Sitecore Mobile SDK for Xamarin – Website and Navigation : Part 2

StartHero

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

Previous posts in this series:
Build an App with Xamarin.Forms using Sitecore Mobile SDK for Xamarin – Getting started : Part 1

In this blogpost I want to talk about how the website is setup for the Mobile app and how the navigation works in the app.

I will be using a Sitecore Habitat website for this. Lets dive in 🙂

Website

I’ve decided to create a separate site for the “Mobile App” and it will be located in the Habitat folder.
HabitatAppInSitecore
The pages/items will not have any presentation details(for now).

We also need some new page types(templates) for the Mobile app.
HabitatAppPageTypeTemplates
The page types will inherit basic stuff like navigable and page content.

Start Page:
Will also have Navigation root and a multiselect field for teasers.
SitecoreStartPage

Carousel Parent Page:
Will present its children in a carousel
SitecoreCarouselParentPage

List Parent Page:
Will list its children in a list
SitecoreListParentpage

Simple Content Page:
As it says, a typical content page
SitecoreSimpleContentPage

Settings Page:
Act as a “placeholder” for the settings.
SitecoreSettingsPage

That was the website part. During the blogpost series this will probably change.

Navigation – Backend

In order to generate/create the navigation for the Habitat App we need to get the site structure from the website, we will of course use Sitecore Mobile SDK for this and fetch the start page and all its children. NavigationMenuService will do this, the service will return a collection of NavigationMenuItem’s(Poco class).

namespace HabitatApp.Models
{
	using Xamarin.Forms;

	public class NavigationMenuItem : ObservableModel
	{

		public string Title { get; set; }

		public string IconSource { get; set; }

		public string PageUrl { get; set; }

		public string ItemId { get; set; }

		private Color _rowColor;
		public Color RowColor {
			get {
				return _rowColor;
			}
			set { SetProperty (ref _rowColor, value); }

		}

		public PageData PageContext { get; set; }

		public bool ShowInMenu { get; set; }
	}
}

NavigationMenuItem on the GitHub
The poco class inherits from ObservableModel(Check it on the GitHub), it’s for the property RowColor. We will use it in order to set a color stripe when a menu item is selected
MenuSelected
The PageContext property contains the sitecore data – PageData, which will be used when we “create” the Xamarin.Forms pages.

namespace HabitatApp.Models
{
	using System;
	using System.Collections.Generic;
	using Sitecore.MobileSDK.API.Items;

	public class PageData
	{
		public string PageName { get; set; }

		public string PageType { get; set; }

		public string NavigationTitle { get; set; }

		public ScItemsResponse ItemContext { get; set; }

		public IList<ScItemsResponse> DataSourceFromField { get; set; }

		public ScItemsResponse DataSourceFromChildren { get; set; }

	}
}

PageData on the GitHub

If you guys notice the ItemContext property, that is the one that holds the response from the Sitecore Mobile SDK Api – ScItemsResponse(Check it on the GitHub), see it as a deserialized Sitecore Web Api response.

In order to generate a collection NavigationMenuItem’s from the NavigationMenuService, we need the settings:
Settings settings = await _settingsRepository.GetWithFallback ();
Settings data as navigationroot(item id), language version, database etc. Which was described in previous post, Build an App with Xamarin.Forms using Sitecore Mobile SDK for Xamarin – Getting started
In method GenerateMenuItems we will call the GeneratePageData in the SitecoreService. The parameter itemScopeTypes will decide what we will fetch from the NavigationRoot, in this case itself and all it's children.

new List<ScopeType> () {
ScopeType.Self,
ScopeType.Children
}

Constants.Sitecore.Fields.Teasers.TeaserSelector is the fieldname for the Multiselect field of teasers in the StartPage(in Sitecore).

namespace HabitatApp.Services
{

	using System.Collections.Generic;
	using System.Threading.Tasks;
	using System.Linq;
	using Xamarin.Forms;
	using Sitecore.MobileSDK.API.Request.Parameters;
	using Sitecore.MobileSDK.API.Items;
	using HabitatApp.Models;
	using HabitatApp.Repositories;
	using HabitatApp.Extensions;

	public class NavigationMenuService : ObservableModel, INavigationMenuService 
	{

		private readonly ISitecoreService _sitecoreService;
		private readonly ISettingsRepository _settingsRepository;

		public NavigationMenuService (ISitecoreService sitecoreService, ISettingsRepository settingsRepository)
		{
			_sitecoreService = sitecoreService;
			_settingsRepository = settingsRepository;
		}


		public async Task<IEnumerable<NavigationMenuItem>> GenerateMenuItems(){

			Settings settings = await _settingsRepository.GetWithFallback ();

			PageData rootPageData = await _sitecoreService.GeneratePageData (
				settings.SitecoreNavigationRootId, 
				PayloadType.Content, 
				new List<ScopeType> () {
					ScopeType.Self,
					ScopeType.Children
				}, 
				Constants.Sitecore.Fields.Teasers.TeaserSelector, 
				settings.SitecoreDefaultLanguage);

			if (rootPageData == null || rootPageData.ItemContext == null)
				return null;

			List<NavigationMenuItem> menuItems = new List<NavigationMenuItem> ();

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

				ISitecoreItem item = rootPageData.ItemContext [i];

				if (item == null)
					continue;

				NavigationMenuItem menuItem = new NavigationMenuItem {
					Title = item.GetValueFromField(Constants.Sitecore.Fields.Navigation.NavigationTitle),
					RowColor = Color.Transparent,
					PageContext = await GetPageData(item, rootPageData),
					ShowInMenu = item.GetCheckBoxValueFromField(Constants.Sitecore.Fields.Navigation.ShowInNavigation),
					IconSource = item.GetValueFromField(Constants.Sitecore.Fields.Navigation.Icon)
						
				};

				menuItems.Add(menuItem);

			}

			return menuItems;

		}

		private async Task<PageData> GetPageData(ISitecoreItem currentItem, PageData rootPageData){

			//If its root we don't need to fetch it again
			if (currentItem.Id == rootPageData.ItemContext.First ().Id)
				return rootPageData;

			return await _sitecoreService.GeneratePageData (currentItem.Id, PayloadType.Content, new List<ScopeType> (){ ScopeType.Self }, Constants.Sitecore.Fields.Teasers.TeaserSelector, currentItem.Source.Language);


		}

	}
}

NavigationMenuService on the GitHub

In GeneratePageData we do a Sitecore Mobile SDK call by calling method GetItemById and it will return the ScItemsResponse.

public async Task<PageData> GeneratePageData(string itemid, PayloadType itemLoadType, List<ScopeType> itemScopeTypes, string datasourceFieldName,  string itemLanguage = "en"){

	ScItemsResponse response = await GetItemById(itemid, itemLoadType, itemScopeTypes, itemLanguage);

	if (response == null)
		return null;

	ISitecoreItem item = response.First ();

	if (item == null)
		return null;

	PageData pageData = new PageData {
		PageName = item.DisplayName,
		ItemContext =  response,
		NavigationTitle = item.GetValueFromField(Constants.Sitecore.Fields.Navigation.NavigationTitle),
		PageType = item.GetTemplateName(),
		DataSourceFromChildren = await GetDatasourceFromChildren(item),
		DataSourceFromField = await GetDataSourceFromFieldName(item,datasourceFieldName) 

	};

	return pageData;
}

public async Task<ScItemsResponse> GetItemById(string itemId, PayloadType itemLoadType, List<ScopeType> itemScopeTypes, string itemLanguage = "en"){

	try {

		using (ISitecoreWebApiSession session = await SitecoreSession) {
			IReadItemsByIdRequest request = ItemWebApiRequestBuilder.ReadItemsRequestWithId (itemId)
				.Payload (itemLoadType)
				.AddScope (itemScopeTypes)
				.Language(itemLanguage)
				.Build ();


			return await session.ReadItemAsync(request);

		}

	} 
	catch(SitecoreMobileSdkException ex)
	{
		this._loggingService.Log ("Error in GetItemById,  id {0} . Error: {1}", itemId, ex.Message); 
		throw ex;
	}
	catch(Exception ex)
	{
		this._loggingService.Log ("Error in GetItemById,  id {0} . Error: {1}", itemId, ex.Message); 
		throw ex;
	}


}

SitecoreService on the GitHub

I have also created an extension class for the ISitecoreItem to make it easier to get the field values.

namespace HabitatApp.Extensions
{
	using System;
	using Sitecore.MobileSDK.API.Items;
	using System.Xml.Linq;
	using System.Linq;
	using System.Text;

	public static class SitecoreItemExtensions
	{

		public static string GetValueFromField(this ISitecoreItem item, string fieldName){

			if (item == null)
				return string.Empty;

			return item [fieldName].RawValue;

		}

		public static string GetTemplateName(this ISitecoreItem item){

			if (item == null)
				return string.Empty;

			return item.Template.Substring (item.Template.LastIndexOf ("/", System.StringComparison.Ordinal)+1);

		}

		public static bool GetCheckBoxValueFromField(this ISitecoreItem item, string fieldName){

			if (item == null)
				return false;

			return item [fieldName].RawValue == "1";

		}

		public static string GetImageUrlFromMediaField(this ISitecoreItem item, string mediafieldName, string websiteUrl = null, string defaultImageUrl = "http://myhabitat.dev/-/media/Habitat/Images/Wide/Habitat-004-wide.jpg"){


			XElement xmlElement = GetXElement (item, mediafieldName);

			if (xmlElement == null)
				return defaultImageUrl;

			XAttribute attribute = xmlElement.Attributes().FirstOrDefault(attr => attr.Name == "mediaid");

			string mediaId = attribute.Value;

			Guid id = Guid.Parse (mediaId);

			if (string.IsNullOrWhiteSpace (websiteUrl))
				return String.Format("-/media/{0}.ashx", id.ToString ("N"));

			return String.Format("{0}/-/media/{1}.ashx", websiteUrl, id.ToString ("N"));

		}

		public static string GetItemIdFromLinkField(this ISitecoreItem item, string linkFieldName){


			XElement xmlElement = GetXElement (item, linkFieldName);

			if (xmlElement == null)
				return string.Empty;


			XAttribute attribute = xmlElement.Attributes().FirstOrDefault(attr => attr.Name == "id");

			return attribute.Value;

		}

		public static string GetTextFromLinkField(this ISitecoreItem item,  string linkFieldName){


			XElement xmlElement = GetXElement (item, linkFieldName);

			if (xmlElement == null)
				return string.Empty;


			XAttribute attribute = xmlElement.Attributes().FirstOrDefault(attr => attr.Name == "text");

			return attribute.Value;

		}

		private static XElement GetXElement (this ISitecoreItem item, string fieldName)
		{
			if (item == null)
				return null;


			if (item.Fields.All (f => f.Name != fieldName))
				return null;

			string fieldValue = item [fieldName].RawValue;

			if (string.IsNullOrWhiteSpace (fieldValue))
				return null;

			return XElement.Parse (fieldValue);
		}
	}
}

SitecoreItemExtensions on the GitHub

Now let us put it together with the “frontend”

Navigation – Frontend

For the navigation in the Xamarin Cross App we will use Fly Out Navigation (Master-Detail) for the Android users and Tabbed Page Navigation for the IOS users.

Fly out Navigation – Android

Android.Nav

NavigationMasterPage.xaml is a MasterDetailPage where MasterDetailPage.Master will present the menu(in a listview) and MasterDetailPage.Detail will present the “navigated page”. In the xaml page we will bind to a viewmodel, in this case the NavigationMasterViewModel. You can see the bindings in the listview:

ItemsSource="{Binding MenuItems}"
SelectedItem="{Binding MenuItemSelected}"

?xml version="1.0" encoding="UTF-8"?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" 
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
	xmlns:local="clr-namespace:HabitatApp;assembly=HabitatApp"
	xmlns:control="clr-namespace:HabitatApp.Views.Controls;assembly=HabitatApp"  
	x:Class="HabitatApp.Views.Pages.NavigationMasterPage">
	<MasterDetailPage.Master>
		<ContentPage Padding="5, 25" 
			Icon="{Binding MenuIcon}" 
			Title=" " 
			BackgroundColor="{DynamicResource HabitatBackgroundColor}">
			<StackLayout VerticalOptions="FillAndExpand">
				<ListView x:Name="listView" 
					BackgroundColor="Transparent" 
					VerticalOptions="FillAndExpand" 
					SeparatorVisibility="None" 
					ItemsSource="{Binding MenuItems}" 
					SelectedItem="{Binding MenuItemSelected}">
					<ListView.ItemTemplate>
						<DataTemplate>
							<control:ViewCellNonSelectable>
								<Grid>
									<Grid.ColumnDefinitions>
										<ColumnDefinition Width="2">
										</ColumnDefinition>
										<ColumnDefinition Width="*">
										</ColumnDefinition>
									</Grid.ColumnDefinitions>
									<BoxView Color="{Binding RowColor}" Grid.Column="0" />
									<Label Grid.Column="1" 
										VerticalTextAlignment="Center" 
										HorizontalTextAlignment="Start" 
										Text="{Binding Title}" 
										Font="Large" 
										TextColor="White" 
										LineBreakMode="TailTruncation" />
								</Grid>
							</control:ViewCellNonSelectable>
						</DataTemplate>
					</ListView.ItemTemplate>
				</ListView>
			</StackLayout>
		</ContentPage>
	</MasterDetailPage.Master>
	<MasterDetailPage.Detail>
		<ContentPage 
		xmlns="http://xamarin.com/schemas/2014/forms" 
		xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
		BackgroundColor = "{DynamicResource HabitatBackgroundColor}"
		Title="Habitat">
			<StackLayout VerticalOptions="Center">
				<Image Aspect="AspectFit" 
					Source="{Binding HabitatLogo}" 
					VerticalOptions="Center" 
					HorizontalOptions="Center"/>
		   	</StackLayout>
		</ContentPage>
	</MasterDetailPage.Detail>
</MasterDetailPage>

NavigationMasterPage.xaml on the GitHub

The code behind for the NavigationMasterPage.xaml. The idea is NOT to have logic here, it will all happen in the viewmodel. What we do here is to connect the viewmodel to the xaml page:
BindingContext = _navigationMasterViewModel

namespace HabitatApp.Views.Pages
{
	using Xamarin.Forms;
	using Xamarin.Forms.Xaml;
	using HabitatApp.ViewModels.Pages;
	using Autofac;

	[XamlCompilation (XamlCompilationOptions.Compile)]
	public partial class NavigationMasterPage : MasterDetailPage
	{

		private NavigationMasterViewModel _navigationMasterViewModel;

		public NavigationMasterPage () : this(HabitatApp.App.AppInstance.Container.Resolve<NavigationMasterViewModel>())
		{

		}

		public NavigationMasterPage (NavigationMasterViewModel navigationMasterViewModel)
		{
			InitializeComponent ();

			_navigationMasterViewModel = navigationMasterViewModel;

			_navigationMasterViewModel.ConnectedToPage = this;

			BindingContext = _navigationMasterViewModel;
			//This is for Android, we dont want to have the row selected in the menu
			listView.ItemSelected += (s, e) => {
				listView.SelectedItem = null; 
			};


		}


		protected override async void OnAppearing ()
		{
			await _navigationMasterViewModel.LoadAsync();

			base.OnAppearing ();
		}



	}
}

When the page starts appearing we will call the LoadAsync method in NavigationMasterViewModel.

LoadAsync in NavigationMasterViewModel will retrieve the navigation data.

namespace HabitatApp.ViewModels.Pages
{

	using System;
	using Xamarin.Forms;
	using System.Collections.ObjectModel;
	using System.Threading.Tasks;
	using System.Collections.Generic;
	using System.Linq;
	using HabitatApp.Services;
	using HabitatApp.Models;
	using HabitatApp.Extensions;
	using HabitatApp.Views.Pages;




	public class NavigationMasterViewModel : ViewModelBase
	{
		private readonly INavigationMenuService _navigationMenuService;
		private readonly INavigationService _navigationService;

		public NavigationMasterViewModel (INavigationService navigationService, INavigationMenuService navigationMenuService)
		{
			_navigationMenuService = navigationMenuService;
			_navigationService = navigationService;
		}

		private ObservableCollection<NavigationMenuItem> _menuItems = new ObservableCollection<NavigationMenuItem> ();

		public ObservableCollection<NavigationMenuItem> MenuItems {
			get {
				return _menuItems;
			}
			set { SetProperty (ref _menuItems, value); }
		}


		private NavigationMenuItem _menuItemSelected;

		public NavigationMenuItem MenuItemSelected {
			get {
				return _menuItemSelected;
			}
			set {
				SetProperty (ref _menuItemSelected, value);

				if (_menuItemSelected != null) {

					HandleRowStyleForSelectedMenuItem (_menuItemSelected);

					HandleNavigationForSelectedMenuItem (_menuItemSelected);
				}

			}

		}


		private string _menuIcon;

		public String MenuIcon {
			get {
				return _menuIcon;
			}
			set { SetProperty (ref _menuIcon, value); }

		}


		/// <summary>
		/// Loads the async.
		/// </summary>
		public async override Task LoadAsync()
		{


			await SetData ();

		}

		/// <summary>
		/// Sets the data.
		/// </summary>
		/// <returns>The data.</returns>
		private async Task SetData(){
		

			IEnumerable<NavigationMenuItem> menuItems = await _navigationMenuService.GenerateMenuItems ();

			if (menuItems == null)
				return;

			MenuItems = menuItems.Where(item => item.ShowInMenu).ToObservableCollection();

			if(MenuItems.Any())
				MenuItemSelected = MenuItems.ElementAt (0);

			MenuIcon = "HamburgerIcon.png";

		} 



		/// <summary>
		/// Sets the menu.
		/// </summary>
		/// <returns>The menu.</returns>
		private async Task SetMenu ()
		{
			IEnumerable<NavigationMenuItem> menuItems = await _navigationMenuService.GenerateMenuItems ();
			if (menuItems == null)
				return;

			MenuItems = menuItems.Where(item => item.ShowInMenu).ToObservableCollection();
		}


		/// <summary>
		/// Handles the navigation for selected menu item.
		/// </summary>
		/// <param name="navigationMenuItem">Navigation menu item.</param>
		private async void HandleNavigationForSelectedMenuItem (NavigationMenuItem navigationMenuItem)
		{
			//This is ugly need to clean it up
			NavigationPage nav = new NavigationPage();
			nav.BarBackgroundColor = Color.FromRgb(46, 56, 78);
			nav.BarTextColor = Color.White; 

			await _navigationService.NavigateToPageByPageData (nav, navigationMenuItem.PageContext);

			((NavigationMasterPage)ConnectedToPage).IsPresented = false;
			((NavigationMasterPage)ConnectedToPage).Detail = nav;


		}

		/// <summary>
		/// Handles the row style for selected menu item.
		/// </summary>
		/// <param name="navigationMenuItem">Navigation menu item.</param>
		private void HandleRowStyleForSelectedMenuItem (NavigationMenuItem navigationMenuItem)
		{
			NavigationMenuItem previousSelectedItem =  MenuItems.FirstOrDefault(m => m.RowColor == Color.Red);

			if(previousSelectedItem!=null)
				previousSelectedItem.RowColor = Color.Transparent;

			NavigationMenuItem currentSelectedItem =  MenuItems.FirstOrDefault(m => m.Title == MenuItemSelected.Title);
			currentSelectedItem.RowColor = Color.Red;

		}



	}
}

NavigationMasterViewModel on the GitHub
If everything goes well we will set the SelectedItem with the first element from the MenuItems collections and because SelectedItem is an Observable object(that means it listens to changes using INotifyPropertyChanged), we can take action on it.

We will call the HandleRowStyleForSelectedMenuItem for setting the selected style on the menu and call HandleNavigationForSelectedMenuItem for navigating to selected page

private NavigationMenuItem _menuItemSelected;

public NavigationMenuItem MenuItemSelected {
	get {
		return _menuItemSelected;
	}
	set {
		SetProperty (ref _menuItemSelected, value);

		if (_menuItemSelected != null) {

			HandleRowStyleForSelectedMenuItem (_menuItemSelected);

			HandleNavigationForSelectedMenuItem (_menuItemSelected);
		}

	}

}

/// <summary>
/// Handles the navigation for selected menu item.
/// </summary>
/// <param name="navigationMenuItem">Navigation menu item.</param>
private async void HandleNavigationForSelectedMenuItem (NavigationMenuItem navigationMenuItem)
{
	//This is ugly need to clean it up
	NavigationPage nav = new NavigationPage();
	nav.BarBackgroundColor = Color.FromRgb(46, 56, 78);
	nav.BarTextColor = Color.White; 

	await _navigationService.NavigateToPageByPageData (nav, navigationMenuItem.PageContext);

	((NavigationMasterPage)ConnectedToPage).IsPresented = false;
	((NavigationMasterPage)ConnectedToPage).Detail = nav;


}

/// <summary>
/// Handles the row style for selected menu item.
/// </summary>
/// <param name="navigationMenuItem">Navigation menu item.</param>
private void HandleRowStyleForSelectedMenuItem (NavigationMenuItem navigationMenuItem)
{
	NavigationMenuItem previousSelectedItem =  MenuItems.FirstOrDefault(m => m.RowColor == Color.Red);

	if(previousSelectedItem!=null)
		previousSelectedItem.RowColor = Color.Transparent;

	NavigationMenuItem currentSelectedItem =  MenuItems.FirstOrDefault(m => m.Title == MenuItemSelected.Title);
	currentSelectedItem.RowColor = Color.Red;

}

Tabbed Navigation – IOS

IOS.Start

NavigationTabbedPage.xaml is a TabbedPage. I had to create a custom TabbedPage,BindableTabbedPage, in order for the page to bind from viewmodel NavigationTabbedPageViewModel.cs

<?xml version="1.0" encoding="UTF-8"?>
<page:BindableTabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
	x:Class="HabitatApp.Views.Pages.NavigationTabbedPage"
	xmlns:page="clr-namespace:HabitatApp.Views.Pages;assembly=HabitatApp"  
	ChildrenList="{Binding TabbedPages}" 
	BackgroundColor="{DynamicResource HabitatBackgroundColor}">

			<ContentPage 
			xmlns="http://xamarin.com/schemas/2014/forms" 
			xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
			BackgroundColor = "{DynamicResource HabitatBackgroundColor}"
			Title="">
				<StackLayout VerticalOptions="Center">
					<Image Aspect="AspectFit" 
						Source="{Binding HabitatLogo}" 
						VerticalOptions="Center" 
						HorizontalOptions="Center"/>
					
			   	</StackLayout>
			</ContentPage>

</page:BindableTabbedPage>

NavigationTabbedPage.xaml on the GitHub

I wanted to bind the collection TabbedPages(from the NavigationTabbedPageViewMdel) to the TabbedPage, in order to do so I’ve created page BindableTabbedPage. When we do a bind to the new property ChildrenListProperty, it will call UpdateList and add the TabbedPages as children to the BindableTabbedPage.

namespace HabitatApp.Views.Pages
{

	using HabitatApp.ViewModels;
	using Xamarin.Forms;
	using System.Collections;
	using System.Collections.Generic;

	public class BindableTabbedPage : TabbedPage
	{
		public BindableTabbedPage()
		{

		}


		public static BindableProperty ChildrenListProperty = BindableProperty.Create ("ChildrenList", 
			typeof(IList), typeof(BindableTabbedPage), 
			null, 
			BindingMode.Default, 
			null, 
			new BindableProperty.BindingPropertyChangedDelegate(UpdateList));

		public IList<Page> ChildrenList
		{
			get { return (IList<Page>)GetValue(ChildrenListProperty); }
			set { 
				SetValue(ChildrenListProperty, value); 
			}
		}

		private static void UpdateList(BindableObject bindable, object oldvalue, object newvalue)
		{

			BindableTabbedPage bindableTabbedPage = bindable as BindableTabbedPage;
			if (bindableTabbedPage == null) {
				return;
			}

			bindableTabbedPage.Children.Clear ();

			IList<Page> pageList = (IList<Page>)newvalue;

			if (pageList == null || pageList.Count == 0) {
				return;
			}

			foreach (Page page in pageList) {

				IViewModel model = (IViewModel)page.BindingContext;
				model.LoadAsync ().GetAwaiter();

				NavigationPage navPage = new NavigationPage (page);
				navPage.BarBackgroundColor = Color.FromRgb (46, 56, 78);
				navPage.BarTextColor = Color.White;
				navPage.Icon =  page.Icon;
				navPage.Title = page.Title;
				bindableTabbedPage.Children.Add (navPage);

			}
	
		}		

	}
}

BindableTabbedPage on the GitHub

In order to set the backgroundcolor on the tabs, I had to do a custom rendering in the IOS project.
The interesting part is this:
[assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedPageRenderer))]
It allows us to “override” a control/view/page in Xamarin.Forms project. In this case we do it with TabbedPage.

using Xamarin.Forms;
using HabitatApp.iOS.Renderers;

[assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedPageRenderer))]
namespace HabitatApp.iOS.Renderers
{
	using System;
	using Xamarin.Forms.Platform.iOS;
	using UIKit;

	public class TabbedPageRenderer: TabbedRenderer 
	{
		public TabbedPageRenderer ()
		{

			TabBar.BarTintColor = UIColor.FromRGB(46, 56, 78);
			TabBar.BackgroundColor = UIColor.FromRGB(46, 56, 78);
			TabBar.SelectedImageTintColor = UIColor.White;

		}
	}
}

TabbedPageRenderer on the GitHub

Here is the viewmodel – NavigationTabbedPageViewModel

namespace HabitatApp.ViewModels.Pages
{

	using System;
	using Xamarin.Forms;
	using System.Collections.ObjectModel;
	using System.Threading.Tasks;
	using System.Collections.Generic;
	using System.Linq;
	using HabitatApp.Services;
	using HabitatApp.Models;
	using HabitatApp.Extensions;
	using HabitatApp.Repositories;


	public class NavigationTabbedPageViewModel : ViewModelBase
	{
		private readonly INavigationMenuService _navigationMenuService;
		private readonly IPageService _pageService;
	
		public NavigationTabbedPageViewModel (IPageService pageService, INavigationMenuService navigationMenuService)
		{
			_pageService = pageService;
			_navigationMenuService = navigationMenuService;
		}


		private ObservableCollection<Page> _tabbedPages = new ObservableCollection<Page> ();

		public ObservableCollection<Page> TabbedPages {
			get {
				return _tabbedPages;
			}
			set { SetProperty (ref _tabbedPages, value); }
		}

	
		private string _habitatLogo;

		public String HabitatLogo {
			get {
				return _habitatLogo;
			}
			set { SetProperty (ref _habitatLogo, value); }

		}

		/// <summary>
		/// Loads the async.
		/// </summary>
		/// <returns>The async.</returns>
		public async override Task LoadAsync()
		{
			HabitatLogo = "HabitatSmallTransparent.png"; 

			await SetData ();

		}

		private async Task SetData(){

			IEnumerable<NavigationMenuItem> menuItems = await _navigationMenuService.GenerateMenuItems ();

			if (menuItems == null)
				return;

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

			foreach (NavigationMenuItem menuItem in menuItems.Where(item => item.ShowInMenu)) {
				Page page = await _pageService.LoadPageByPageData (menuItem.PageContext);
				page.Icon = menuItem.IconSource;
				list.Add (page);
			}

			TabbedPages = list.ToObservableCollection ();

		} 
	}
}

NavigationTabbedPageViewModel on the GitHub

I will stop here now, it is too much already 🙂

There will be a third blogpost in this series where I will show you how the Habitat app was done.

I have it all on the Github, feel free to take a look at it: Xamarin.Habitat

That’s all for now folks 🙂


9 thoughts on “Build an App with Xamarin.Forms using Sitecore Mobile SDK for Xamarin – Website and Navigation : Part 2

    1. Thank you for reading it 🙂
      Sure I can add the sitecore package (containing templates and items) to the github project. You can give me your email address, so I can ping you when I’m done

      Like

      1. Hi Goran – Thanks for sharing this information and this is great!!

        i was trying to import the content and template tree via Install package, it only imported the content tree, non of the template fields got imported. looks like the fields are missing in the package. Is it possible to get the package with template fields as well. Please let me know. Thanks!

        Liked by 1 person

Leave a comment

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