Photo par pxhere.com

Un aperçu complet de MvvmCodeGenerator

Gagnez du temps en écrivant vos ViewModels!

Posted by Damien Aicheh on July 31, 2019 · 20 mins

Un générateur de code MVVM pour .NET

J’ai créé cet outil pour éviter d’écrire du code redondant pour mes ViewModels. Cet outil est construit à partir du compiler Rosyln pour .NET.

MvvmCodeGenerator

Pourquoi utiliser ce NuGet ?

Lorsque vous développez votre projet, vous définissez toujours un grand nombre de properties et de commands dans vos ViewModels. Cela donne des dizaines et des dizaines de lignes qui ne permettent pas une lecture rapide de votre fichier.

L’idée de ce NuGet est simple: vous définissez simplement vos commands et properties avec une seule ligne dans un fichier XML spécifique, vous rebuildez votre projet et vous êtes prêt à démarrer !

Ce NuGet a différents avantages:

  • Évitez d’écrire le code redondant
  • Vos propriétés et commandes sont déclarées en utilisant les mêmes conventions
  • Réduisez la quantité de code dans vos ViewModels
  • Les ViewModels ne contiennent que le code le plus important
  • Migrer d’un Framework MVVM à un autre facilement
  • Il supporte différents Frameworks MVVM

MvvmCodeGenerator prend désormais en charge les Frameworks MVVM:

Installation

Tout d’abord, installez le NuGet sur votre projet .NetStandard par exemple. Créez ensuite un nouveau fichier appelé MvvmCodeGenMapper.xml à la racine de votre projet. Ce fichier contiendra la définition de vos ViewModels.

Nous devons maintenant choisir le Framework MVVM dont nous avons besoin pour générer nos ViewModels. Comme je l’ai dit précédemment, MvvmCodeGenerator supporte ces options:

  • mvvmlightlibs
  • mvvmcross
  • mvvmicro
  • freshmvvm
  • prismcore

Lorsque votre choix est terminé, signalons-le au générateur à l’intérieur du fichier MvvmCodeGenMapper.xml comme ceci:

<?xml version="1.0" encoding="UTF-8" ?>
<Resources>
    <Generator Value="mvvmlightlibs" />
</Resources>    

Pour ce tutoriel, j’ai choisi MvvmLightLibs. N’oubliez pas d’installer le NuGet du Framework MVVM que vous avez choisi dans votre projet.

Définissez vos ViewModels

Créer les dossiers ViewModels

Il est temps de définir vos ViewModels. Pour ce tutoriel, imaginons que nous avons deux cas:

  • Nous avons besoin d’un dossier avec la liste de nos ViewModels associés à nos Vues
  • Nous avons besoin d’un deuxième dossier pour définir les ItemViewModels dont nous avons besoin pour les éléments d’une ListView.

Donc, pour créer cet arbre à l’intérieur des balises Resources, faisons ceci:

<?xml version="1.0" encoding="UTF-8" ?>
<Resources>
    <Generator Value="mvvmlightlibs" />
    <ViewModels Namespace="Xamarin.Sample" DestinationFolder="ViewModel">
    </ViewModels>

    <ViewModels Namespace="Xamarin.Sample.Items" DestinationFolder="ViewModel/Items">
    </ViewModels>
</Resources>    

Comme vous pouvez le voir ci-dessus, vous définissez namespace associé à vos ViewModels et le dossier de destination. Vous devez savoir que MvvmCodeGenerator créera tous les dossiers de destination s’ils n’existent pas.

Créer les ViewModels

Chaque balise ViewModel a 2 propriétés:

  • Key : Le préfixe du nom du ViewModel
  • Base : Le préfixe du nom du ViewModel de la classe parente

Dans beaucoup de projets, nous avons un ViewModel racine qui est une classe qui regroupe toutes les propriétés répétitives dont nous avons besoin dans chaque ViewModel. Une propriété de base est le IsLoading. Donc, pour éviter de déclarer cette propriété pour chaque ViewModel, vous pouvez simplement la déclarer une fois dans un RootViewModel et tous les autres ViewModels en hériteront.

Voyons comment dans notre fichier XML:

<?xml version="1.0" encoding="UTF-8" ?>
<Resources>
    <Generator Value="mvvmlightlibs" />
    <ViewModels Namespace="Xamarin.Sample" DestinationFolder="ViewModel">
        <ViewModel Key="Root">
        </ViewModel>
        
        <ViewModel Key="Home" Base="Root">
          
        </ViewModel>
    </ViewModels>

    <ViewModels Namespace="Xamarin.Sample.Items" DestinationFolder="ViewModel/Items">
        <ItemViewModel Key="Switch">
        </ItemViewModel>
    </ViewModels>
</Resources>    

Notre HomeViewModel hérite maintenant du RootViewModel.

Définir les propriétés

Propriétés

Vous pouvez spécifier les Properties pour chaque ViewModels:

Pour les Properties, vous avez la possibilité de le définir en utilisant la balise Property qui a ces valeurs:

  • Name : Le nom de la property
  • Description : Le commentaire associé à la property
  • Type : Le type de la property

Vous avez différentes options pour spécifier le Type de votre property

Ici vous avez la liste des types de base que vous pouvez utiliser:

  • string
  • int
  • long
  • bool
  • float
  • object
  • double
  • DateTime
  • DateTimeOffset
  • TimeSpan

Si nous revenons à notre exemple pour définir une propriété IsLoading dans notre RootViewModel, il nous suffit d’écrire:

<Property Name="IsLoading" Type="bool" Description="Gets or sets the is loading property" />

Types personnalisés

Si le type dont vous avez besoin ne figure pas dans la liste des types que j’ai décris précédemment, ne vous inquiétez pas, vous pouvez simplement le spécifier en utilisant l’espace de noms complet, par exemple:

<Property Name="House" Type="Xamarin.Sample.Models.House" Description="Gets or sets the house." />

Ici, je définis une property en utilisant un modèle House que j’ai crée dans mon projet.

Liste de propriétés

Vous pouvez également déclarer une liste d’éléments en utilisant le mot clé list avant votre type, comme ceci:

<Property Name="names" Type="list string" Description="Gets or sets all names." />
<Property Name="AllHouses" Type="list Xamarin.Sample.Models.House" Description="Gets or sets all houses." />

Cela générera une IList du type de vos besoins.

Pour résumer ici, voici un exemple plus complet:

<?xml version="1.0" encoding="UTF-8" ?>
<Resources>
    <Generator Value="mvvmlightlibs" />
    <ViewModels Namespace="Xamarin.Sample" DestinationFolder="ViewModel">
        <ViewModel Key="Root">
            <Property Name="IsLoading" Type="bool" Description="Gets or sets the is loading property" />
        </ViewModel>
        
        <ViewModel Key="Home" Base="Root">
          <Property Name="Title" Type="string" Description="Gets or sets the title." />
          <Property Name="Number" Type="int" Description="Gets or sets the total number." />
          <Property Name="Degrees" Type="float" Description="Gets or sets the degrees." />
          <Property Name="MyTimeOffset" Type="DateTimeOffset" Description="Gets or sets the date and hour offset." />
          <Property Name="MyTimeSpan" Type="TimeSpan" Description="Gets or sets the time span." />
          <Property Name="Messages" Type="list string" Description="Gets or sets all messages." />
          <Property Name="MyHouse" Type="Xamarin.Sample.Models.House" Description="Gets or sets my houses." />
          <Property Name="AllHouses" Type="list Xamarin.Sample.Models.House" Description="Gets or sets all houses." />
        </ViewModel>
    </ViewModels>

    <ViewModels Namespace="Xamarin.Sample.Items" DestinationFolder="ViewModel/Items">
        <ItemViewModel Key="Switch">
          <Property Name="UnKnown" Type="object" Description="Gets or sets the unknown object." />
          <Property Name="MyDate" Type="DateTime" Description="Gets or sets the date and hour." />
        </ItemViewModel>
    </ViewModels>
</Resources>

Définir les commands

Dans vos ViewModels, vous devez toujours spécifier certaines commandes. Faisons-le avec MvvmCodeGenerator!

En utilisant MvvmCodeGenerator, vous avez la possibilité de définir:

  • Une commande synchrone utilisant la balise Command
  • Une commande asynchrone utilisant la balise AsyncCommand

Chaque balise Command a des propriétés différentes:

  • Name : Le nom de la command
  • Parameter : Le type du parametre de la command
  • CanExecute : Un booléen pour savoir si la command doit avoir une méthode can execute ou non
  • Description : Le commentaire associé à la command

Voici un exemple cd Command:

<Command Name="Buy"  Parameter="bool" CanExecute="true" Description="Gets or sets the command to buy a boat" />

Et voici un exemple d’AsyncCommand:

<AsyncCommand Name="Consultation" Parameter="int" CanExecute="true" Description="Gets or sets the command for the consultation" />

Facile à définir, n’est-ce pas?

Les commandes asynchrone de MvvmLightLibs et FreshMvvm n’ayant pas de propriété IsRunning, MvvmCodeGenerator les génèrent automatiquement pour vous. MvvmCross et Mvvmicro disposent déjà de ce type de mécanisme.

Maintenant, si nous revenons à un exemple complet, nous pouvons avoir quelque chose comme ceci:

<?xml version="1.0" encoding="UTF-8" ?>
<Resources>
    <Generator Value="mvvmlightlibs" />
    <ViewModels Namespace="Xamarin.Sample" DestinationFolder="ViewModel">
        <ViewModel Key="Root">
            <Property Name="IsLoading" Type="bool" Description="Gets or sets the is loading property" />
            <AsyncCommand Name="InitData" Description="Gets or sets the command to init the data" />
        </ViewModel>
        
        <ViewModel Key="Home" Base="Root">
          <Property Name="Title" Type="string" Description="Gets or sets the title." />
          <Property Name="Number" Type="int" Description="Gets or sets the total number." />
          <Property Name="Degrees" Type="float" Description="Gets or sets the degrees." />
          <Property Name="MyTimeOffset" Type="DateTimeOffset" Description="Gets or sets the date and hour offset." />
          <Property Name="MyTimeSpan" Type="TimeSpan" Description="Gets or sets the time span." />
          <Property Name="Messages" Type="list string" Description="Gets or sets all messages." />
          <Property Name="MyHouse" Type="Xamarin.Sample.Models.House" Description="Gets or sets my houses." />
          <Property Name="AllHouses" Type="list Xamarin.Sample.Models.House" Description="Gets or sets all houses." />
          <Command Name="JobActivation" Description="Gets or sets the command to manager job actions such as day (de)activation and breaks" />
          <Command Name="ConsultControlFiles" Parameter="int" Description="Gets or sets the command to consult control files" />
          <Command Name="Files" Parameter="bool" CanExecute="true" Description="Gets or sets the files"/>
          <AsyncCommand Name="ConsultFiles" Description="Gets or sets the command to consult control files" />
          <AsyncCommand Name="Consult" Parameter="string" Description="Gets or sets the command to consult control files" />
          <AsyncCommand Name="Consultation" Parameter="string" CanExecute="true" Description="Gets or sets the command for the consultation" />
        </ViewModel>
    </ViewModels>

    <ViewModels Namespace="Xamarin.Sample.Items" DestinationFolder="ViewModel/Items">
        <ItemViewModel Key="Switch">
          <Property Name="UnKnown" Type="object" Description="Gets or sets the unknown object." />
          <Property Name="MyDate" Type="DateTime" Description="Gets or sets the date and hour." />
          <AsyncCommand Name="Toggle" Description="Gets or sets the toggle command" />
        </ItemViewModel>
    </ViewModels>
</Resources>

Ce XML générera 3 fichiers pour chaque ViewModel que vous définissez, voici le résultat pour le SwitchItemViewModel:

Le SwitchItemViewModel.cs:

namespace Xamarin.Sample.Items
{
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using GalaSoft.MvvmLight.Command;

    public partial class SwitchItemViewModel
    {
    }
}

Le SwitchItemViewModel.interface.g.cs:

//------------------------------------------------------------------------------ 
// <auto-generated> 
// This code was generated by MvvmCodeGenerator.
// Runtime Version:4.0.30319.42000
// 
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated> 
//------------------------------------------------------------------------------
namespace Xamarin.Sample.Items
{
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using GalaSoft.MvvmLight.Command;

    public interface ISwitchItemViewModel : System.ComponentModel.INotifyPropertyChanged
    {
        System.Object UnKnown
        {
            get;
        }

        System.DateTime MyDate
        {
            get;
        }

        System.Boolean IsToggleCommandRunning
        {
            get;
        }

        System.Windows.Input.ICommand ToggleCommand
        {
            get;
        }
    }
}

Le SwitchItemViewModel.part.g.cs:

//------------------------------------------------------------------------------ 
// <auto-generated> 
// This code was generated by MvvmCodeGenerator.
// Runtime Version:4.0.30319.42000
// 
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated> 
//------------------------------------------------------------------------------
namespace Xamarin.Sample.Items
{
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using GalaSoft.MvvmLight.Command;

    public partial class SwitchItemViewModel : GalaSoft.MvvmLight.ViewModelBase, Xamarin.Sample.Items.ISwitchItemViewModel
    {
        private System.Object unKnown;
        private System.DateTime myDate;
        private System.Boolean isToggleCommandRunning;
        private GalaSoft.MvvmLight.Command.RelayCommand toggleCommand;
        /// <summary>
        // Gets or sets the unknown object.
        /// </summary>
        public System.Object UnKnown
        {
            get => this.unKnown;
            set => this.Set(ref this.unKnown, value);
        }

        /// <summary>
        // Gets or sets the date and hour.
        /// </summary>
        public System.DateTime MyDate
        {
            get => this.myDate;
            set => this.Set(ref this.myDate, value);
        }

        /// <summary>
        // Gets or sets the value to know if the associated async command is running.
        /// </summary>
        public System.Boolean IsToggleCommandRunning
        {
            get => this.isToggleCommandRunning;
            set => this.Set(ref this.isToggleCommandRunning, value);
        }

        /// <summary>
        // Gets or sets the toggle command
        /// </summary>
        public System.Windows.Input.ICommand ToggleCommand
        {
            get => this.toggleCommand ?? (this.toggleCommand = new GalaSoft.MvvmLight.Command.RelayCommand(async () =>
            {
                try
                {
                    this.IsToggleCommandRunning = true;
                    await ExecuteToggleCommandAsync();
                }
                catch (System.Exception ex)
                {
                    OnExecuteToggleCommandAsyncError(ex);
                }
                finally
                {
                    this.IsToggleCommandRunning = false;
                }
            }

            )); // You must implement the following method(s): ExecuteToggleCommandAsync and OnExecuteToggleCommandAsyncError
        }
    }
}

Il ne vous reste plus qu’à implémenter vos commandes dans le fichier SwitchItemViewModel.cs et le tour est joué !

Affichage Visual Studio

Lorsque vous reconstruisez votre projet, vous devriez voir les ViewModels, les dossiers et un fichier MvvmCodeGenMapper.g.targets générés. Ce dernier permet à Visual Studio de regrouper vos fichiers correctement.

Si tout fonctionne correctement, vous devriez voir quelque chose comme ceci:

Visual Studio File Viewer

Si vos fichiers ne sont pas correctement groupés comme ci-dessus, veuillez vérifier si le fichier MvvmCodeGenMapper.g.targets a été importé correctement dans votre .csproj associé à votre projet. Si ce n’est pas le cas, vous pouvez le faire manuellement en ajoutant cette ligne:

<Import Project="MvvmCodeGenMapper.g.targets" />

Rechargez votre projet et vous serez prêt à partir.

Touche finale

Vous trouverez un sample dans le répertoire Github MvvmCodeGenerator pour le tester vous-même.

Happy coding !

Vous avez aimé ce tutoriel ? Laissez une étoile sur le répertoire Github associé !

N'hésitez pas à me suivre sur pour ne pas rater mon prochain tutoriel !