Utiliser Razor dans vos applications Xamarin

Parmi les fonctionnalités étonnantes dans Xamarin, on trouve le support de Razor.

Razor est un moteur de templating HTML disponible dans ASP.NET MVC.
Ayant été conçu de manière indépendante à MVC, Xamarin a décidé de le proposer dans ses produits.

Pourquoi Razor dans Xamarin ?

Un projet sur lequel je travaille actuellement nécessite d’afficher des articles. Le contenu des ces articles doit être en HTML et remonte depuis un serveur sous forme de JSON. Ce contenu est sauvegardé dans une base SQLite locale au téléphone. Il n’y a donc aucune URL disponible pour afficher une webview de cet article. Pour résumer, je dois utiliser une webview pour afficher du contenu HTML local.

Dans mon application j’ai donc créé la classe suivante :

	public class Article
	{
		public int TemplateId {
			get;
			set;
		}
		public string Title {
			get;
			set;
		}
		public string Subtitle {
			get;
			set;
		}
		public string Summary {
			get;
			set;
		}
		public string Body {
			get;
			set;
		}
		public byte[] Image {
			get;
			set;
		}
		public DateTime PublicationDate {
			get;
			set;
		}
	}

On remarque plusieurs propriétés classiques dont deux qui peuvent nous poser problème :

  • Body (contenu HTML)
  • Image (Image sous forme de tableau d’octets).

Nous allons voir comment Razor peut nous aider à afficher cet article.

Mise en place de Razor

Utiliser Razor dans Xamarin est assez simple. Pour cela, il faut ouvrir votre projet avec Xamarin Studio car le template de fichier Xamarin n’est pas encore disponible dans Visual Studio. Dans mon projet, j’ai crée un dossier nommé ArticleTemplates dans lequel tous mes templates Razor seront stockés. Il me suffit ensuite de faire un clic droit et d’ajouter un nouveau fichier.

Xamarin Studio, ajout nouveau fichier

Xamarin Studio, ajout nouveau fichier.

Dans l’écran qui s’ouvre, on choisit ensuite un fichier de type template Razor.

Xamarin Studio, Ajout template Razor

Xamarin Studio, ajout template Razor

Dans mon cas, j’ai appelé ce fichier SimpleArticleTemplate.

Xamarin Studio, SimpleArticleTemplate Razor

Xamarin Studio, template Razor

Pour utiliser ce template, j’ai créé, dans mon projet Xamarin Forms, une nouvelle page Xaml contenant une webview.

<?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="RazorArticle.MainPage"
	Title="{Binding Title}">

	<StackLayout VerticalOptions="FillAndExpand">
		<Label Text="{Binding Title}">
			<Label.IsVisible>
				<OnPlatform x:TypeArguments="x:Boolean" WinPhone="True" Android="False" iOS="False" />
			</Label.IsVisible>
		</Label>
		<WebView x:Name="webView" VerticalOptions="FillAndExpand" />
	</StackLayout>
</ContentPage>

 

Dans la méthode OnAppearing de son code-behind, j’ai crée un article de test et je l’ai affecté à la webview.

protected override void OnAppearing()
{
	base.OnAppearing();

	var article = new Article {
		Title = "Partenariat",
		Subtitle = "Votre projet Xamarin avec Cellenza !",
		Summary = "Cellenza, votre partenaire dans la réalisation de vos projets Xamarin.",
		Body = "<p><em>Cellenza est votre partenaire</em></p>" +
			"<p>Grâce à son expertise, Cellenza peut vous accompagner pour chaque étape de vos projets Xamarin</p>" +
			"<ul>" +
			"<li><strong>Conseil</strong></li>" +
			"<li><strong>Formation</strong></li>" +
			"<li><strong>Réalisation</strong></li>" +
			"<li><strong>Industrialisation</strong></li>" +
			"<li><strong>Qualité</strong></li>" +
			"</ul>" +
			"<p>Contactez-nous pour plus d'informations !</p>",
		Image = Convert.FromBase64String("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")
	};

	BindingContext = article;
	var htmlString = new SimpleArticleTemplate { Model = article }.GenerateString();
	webView.Source = new HtmlWebViewSource { Html = htmlString };
}

Nous remarquerons ici que l’image est une chaîne de caractères convertie en Base64. Cela permet de tester simplement le rendu du template. Si vous souhaitez tester avec votre propre image rendez-vous sur http://base64image.org pour convertir votre image en chaîne de caractères.

Pour l’instant, je n’ai pas changé le template Razor pour modifier l’affichage de la page. Dans l’état actuel des choses, il est impossible d’afficher le contenu HTML du Body et l’image dans Razor. En effet, ce dernier ne prend pas en charge l’injection de HTML ni l’affichage d’image à partir d’un tableau d’octets.

Installation de PortableRazor

Pour résoudre le soucis, il faut installer le paquet Nuget Portable Razor. Ce dernier va nous permettre de créer nos propres extensions Razor et donc d’ajouter les deux fonctionnalités qui manquent.

Une fois le paquet installé, nous allons créer la classe d’extension suivante.

using System;
using PortableRazor.Web;
using PortableRazor.Web.Mvc;

namespace RazorArticle
{
	public static class HtmlExtensions
	{
		public static IHtmlString Raw(this HtmlHelper helper, string raw)
		{
			return new HtmlString(raw);
		}

		public static IHtmlString ImageBase64Source(this HtmlHelper helper, byte[] image)
		{
			if (image != null)
				return new HtmlString("data:image/jpeg;base64," + Convert.ToBase64String(image));
			return new HtmlString(string.Empty);
		}
	}
}

La première méthode est assez explicite et permet d’injecter du HTML pur. La seconde méthode va convertir l’image en Base64 dans un format spécifique pour l’intégrer dans le corps du HTML. Cela fonctionne sur tous les navigateurs mobiles que j’ai pu tester et est un format assez ancien et connu.

Il ne reste maintenant plus qu’à modifier le template Razor.

@using RazorArticle
@inherits PortableRazor.ViewBase
@model RazorArticle.Article

<html>
<body>
<h1>@Model.Subtitle</h1>
<p><strong>@Model.Summary</strong></p>

<img src="@Html.ImageBase64Source(@Model.Image)" />

<div>@Html.Raw(@Model.Body)</div>
</body>
</html>

Le rendu de l’article sera le suivant sur iOS.

Rendu du template Razor

Rendu du template Razor

Conclusion

Au travers de cet article, on a vu quelle peut être l’utilité de Razor dans Xamarin et à quel point il est facile à utiliser. Dans mon cas, cette fonctionnalité m’a vraiment fait économiser de nombreuses journées de développement !

N’hésitez pas à nous contacter en cas de questions !

2 Commentaires Laisser un commentaire

Je viens de retomber sur cet article 🙂
C’est en production depuis maintenant plus d’un an et ca fonctionne toujours aussi bien, les messieurs du métier et de la com publient des articles avec parfois des mises en forme avancées.
Merci encore John !

Répondre

Merci Fred ! Content de voir que tout fonctionne bien !

Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *