Implémentez vos menus Hamburger avec AppShell !

Bonjour à tous!

Dans le cadre du développement d’une application universelle, je voulais une fonctionnalité « bête » mais qui peut vite paraître compliqué si on part from scratch : un menu comme on a sur les applications Microsoft, avec leur fameux hamburgers.

wp_ss_20160615_0002

Exemple sur mon téléphone sous WM10 avec l’application Actualités

Cela peut être assez lourd à mettre en place, surtout si on est amené à faire plusieurs applications (passage par un UserControl ?), et après plusieurs recherches je suis tombé par hasard sur un composant qui fait tout le travail pour nous, magnifique n’est-ce pas?

Aperçu et introduction

Avant de parler plus en détail de ce plugin, voyons le rendu sur un projet UWP avec émulation Windows Mobile 10 :

Hamburger1Hamburger2

Ne prêtez pas attention à mes données, c’est juste un mock sur un projet qui vient à peine de démarrer pour montrer l’implémentation du composant 😉

Ce projet a été créé par TommasoScalici sur GitHub, vous pouvez le retrouver sur ce lien. Il est sous licence MIT, et est encore maintenu (dernier commit il y a 26 jours à l’heure où j’écris ces lignes).

Il se base sur un principe simple : le menu hamburger est en fait une page conteneur, qui va contenir notre page principale. C’est cette page principale qui va se charger de tout le reste.

Documentation

L’avantage avec ce composant, c’est que la page d’accueil sur GitHub résume bien les possibilités et l’utilisation du composant, ce qu’on ne retrouve pas forcément chez tous les composants. Donc je ne vais pas m’étendre sur l’implémentation en XAML du composant avec l’architecture du composant, car cela reste assez simple avec la documentation (MenuListItem, Header, Footer, etc.).

Je vais juste revenir sur le point Conteneur/Contenu, au cas où vous êtes amenés à changer vos noms de page/namespaces.

La page conteneur, qui va contenir notre composant AppShell, doit être définie dans le fichier App.xaml.cs, dans la méthode OnLaunched.


if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// Quand la pile de navigation n'est pas restaurée, accédez à la première page,
// puis configurez la nouvelle page en transmettant les informations requises en tant que
// paramètre
rootFrame.Navigate(typeof(MenuPage), e.Arguments);
}
// Vérifiez que la fenêtre actuelle est active
Window.Current.Activate();
}

Ensuite, dans cette page (dans mon cas MenuPage), vous pouvez passer votre page fille dans le constructeur :


///
/// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
///
public sealed partial class MenuPage : Page
{
public MenuPage()
{
this.InitializeComponent();
TommasoScalici.AppShell.AppShell.Current.AppFrame.Navigate(typeof(MainPage));
}
}

Et le tour est joué!

Pour résumer, les avantages que j’ai trouvé à ce plugin est :

  • l’implémentation rapide et assez logique
  • le respect des thèmes (dark/light)
  • la disponibilité sur NuGet
  • la documentation ni trop conséquence ni trop succinte
  • le projet toujours actif
  • la licence, non contraignante

En espérant vous avoir aidé et fait connaître un plugin assez sympathique, bon développement!

 

 

Créer ses Data Annotation Validator personnalisées (ASP.NET MVC)

En ASP.NET MVC, quand vous mettez en place une validation de formulaire, il est très utile d’utiliser les Data Annotation Validator sur le modèle pour vous épargner toute la logique de test de code. Le Framework .NET en comprend nativement plusieurs :

  • Range : permet la validation si la valeur de la propriété testée est dans une certaine tranche
  • RegularExpression : permet la validation si la valeur de la propriété testée respecte la RegEx fournie
  • Required : Comme son nom l’indique, permet d’indiquer une propriété comme requise
  • StringLength : Dans le cadre d’une propriété de type String, spécifie la longueur maximum de chaîne.
  • DataType : permet la validation si la valeur de la propriété testée repecte le type spécifiée (DataType.Date par exemple)
  • Validation : classe de base pour tous les attributs de validation.

Pour plus de détails sur le fonctionnement des Data Annotation Validators, vous pouvez consulter ce lien : ASP.NET

Ce qui nous intéresse ici n’est pas de revoir toutes ces bases, mais d’envisager une chose : si on doit faire une validation qui n’est pas prévue par le Framework .NET, comme par exemple une validation qui dépend d’une autre propriété? On devra passer par nos propres Data Annotation Validators, ce qui peut devenir très intéressant.

Présentation avec un cas concret : Date maximum

Tout le monde a connu au moins une fois dans sa vie de développeur ce scénario : lors d’une saisie de fourchette de date, la date maximum ne peut pas être inférieure ou égale à la date minimum. Ce sera la base de mon premier Custom Validator.


///
/// Spécifie qu'une valeur de champ correspondant à une date ne peut pas dépasser une autre valeur de champ correspondant à une date
///
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class MaxValueDateTimeAttribute : ValidationAttribute
{
}

Via ce petit bout de code, vous pouvez remarquez que j’ai créé une classe de type MaxValueDateTimeAttribute. Prenez l’habitude de mettre Attribute en suffixe : lors de l’utilisation du Validator dans les ViewModels, vous verrez que le suffixe Attribute est supprimée.

Deux autres choses sont à noter :

  • L’utilisation d’AttributeUsage. Ici, on spécifie que ce Validator sera valable sur les propriétés, les champs et les paramètres. AllowMultiple permet de dire si on peut spécifier plusieurs fois le même attribut sur un unique champ, ce qui ne sera pas notre cas ici.
  • L’implémentation de ValidationAttribute : tous vos Validators doivent implémenter cette classe (pour information, c’est justement le Validation cité précédemment!)

Pour l’usage de notre Validator, nous avons besoin de faire référence à la date minimum. Pas de souci, on ajoute une propriété qu’on va initialiser via le constructeur !


private String _minDateTimeProperty;


CheckMinValueDateTimeAttribute(String minDateTimeProperty)
{
_minDateTimeProperty = minDateTimeProperty;
}

En implémentant le Validator dans le ViewModel, on voit que le constructeur fait bien son boulot :
 CustomValidator
Et voici l’implémentation côté ViewModel :


[Display(Name = "Date minimum")]
public DateTime DateMin { get; set; }


[CheckMinValueDateTime("DateMin", ErrorMessage = "La date maximum est inférieure à la date minimum")]
[Display(Name = "Date maximum")]
public DateTime DateMax { get; set; }

Maintenant, on a notre contrôle lié aux deux dates, et implémentée dans la date correspondante. Il est temps d’implémenter la logique permettant de dire si la règle est valide ou non. Dans mon cas, elle est valide si la date max est supérieure ou égale à la date min. Il est temps de surcharger la méthode IsValid !


protected override ValidationResult IsValid(Object value, ValidationContext validationContext)
{
PropertyInfo propertyInfo = validationContext.ObjectType.GetProperty(this._minDateTimeProperty);
Object minValuePropertyValue = propertyInfo.GetGetMethod().Invoke(validationContext.ObjectInstance, null);
Boolean isMinValueEmpty = (minValuePropertyValue == null);
if (!isMinValueEmpty)
{
if (!(minValuePropertyValue is DateTime))
throw new NotSupportedException(" La propriété de comparaison doit être de type DateTime");
DateTime minValue = (DateTime)minValuePropertyValue;
DateTime currentValue = (DateTime)value;
if (currentValue < minValue)
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}

Rien de bien compliqué je pense : les deux premières lignes me permettent de récupérer la propriété DateMin à partir du nom et de récupérer la valeur via sa méthode publique. Ensuite je fais ma comparaison, et si ça échoue, je retourne un objet ValidationResult correspondant à l’erreur, sinon je retourne ValidationResult.Success.
Cependant, si vous analysez bien, vous avez dû remarquer quelque chose…
Si vous placez un point d’arrêt sur votre code, vous verrez que votre éditeur y passera après un aller/retour serveur, et donc avant les autres propriétés natives. C’est parce qu’il s’agit de l’Unobtrusive Javascript, et qu’on ne l’a pas mis en place dans notre classe. Je vais sûrement en faire un tutoriel plus tard ou compléter celui ci, en attendant vous pouvez lire ce ticket de blog très intéressant : Blog de Brad Wilson.
En résumé dans ce ticket vous avez appris à créer un Validator, notamment celui concernant les dates, bien que perfectible : on peut gérer un booléen pour dire si la date max doit être supérieure ou supérieure ou égale, la validation des types peut se faire bien avant via d’autres Validators… Les possibilités sont immenses.
Bon développement!