DĂ©coupler la logique des Ă©tats et transitions dâun ViewModel en faisant gĂ©rer les Commandes par une Machine Ă Etats Finis apporte un nouveau niveau dâabstraction aussi important que lâest MVVM lui-mĂȘme. Ătes-vous prĂȘt Ă gĂ©rer correctement le workflow de vos applications et en amĂ©liorer lâergonomie ?
Nouvelle Version Xamarin.Forms
Jâai dĂ©jĂ dĂ©veloppĂ© ce sujet Ă propos de WPF il y a cinq ans⊠Le sujet est toujours aussi passionnant, toujours aussi rĂ©volutionnaire et toujours aussi peu populaire ! Beaucoup de raisons qui justifient de vous en prĂ©senter aujourdâhui une version rĂ©novĂ©e, utilisant les derniĂšres versions des outils de dĂ©veloppement et le tout sous Xamarin.Forms ! Bonne lecture, en espĂ©rant que cette fois-ci lâidĂ©e fasse encore un autre bout de chemin vers son adoptionâŠ
Le problĂšme des commandes
Avant dâentrer dans les dĂ©tails posons le problĂšme :
GĂ©rer des commandes en MVVM est trĂšs simple. Une commande nâest quâune propriĂ©tĂ© de type ICommand du ViewModel que lâon attache (binding) Ă une propriĂ©tĂ© de mĂȘme type dâun objet dâUI (type bouton par exemple). Lorsque lâutilisateur dĂ©clenche la commande via lâobjet dâUI la propriĂ©tĂ© ICommand du ViewModel dĂ©clenche sa mĂ©thode Execute().
Tout est simple et clair grĂące Ă XAML, son binding et le pattern MVVM. Les librairies annexes comme MVVM Light simplifient encore plus la tĂąche et proposent en gĂ©nĂ©ral un objet RelayCommand plus complet et plus pratique Ă utiliser que lâinterface brute ICommand. LâimplĂ©mentation dâorigine a Ă©tĂ© créée par Josh Smith et on la retrouve dans de nombreuses librairies MVVM dont Prism sous ce nom ou un autre. Xamarin.Forms vient avec sa propre implĂ©mentation appellĂ©e Command, supportant ICommand de la mĂȘme façon, il nây a donc ici aucune toolbox supplĂ©mentaire Ă utiliser.
Lâinterface ICommand est fort simple et se compose de peu de choses. Dont une mĂ©thode CanExecute(Object).
Mais tout cela ne change rien Ă la vĂ©ritable question quâon doit se poser pour crĂ©er un logiciel bien programmĂ© : comment gĂ©rer proprement le CanExecute ?
Je vois beaucoup de code dans lequel CanExecute nâest tout simplement pas gĂ©ré⊠Jâen conclue que beaucoup dâentre vous se diront âmais pourquoi se prend-t-il la tĂȘte avec ce âdĂ©tailâ ?â
Parce que ce nâest pas un dĂ©tail justement⊠CanExecute reflĂšte de façon assez fidĂšle le workflow du ViewModel, ce qui est possible de faire ou non Ă un moment donnĂ©, donc lâĂ©tat de lâautomate quâest un logicielâŠ
CanExecute est donc lâĂ©manation programmatique de lâessence mĂȘme de ce quâest lâinformatique : crĂ©er des automates, des machines de Turing. Comment cela pourrait-il ĂȘtre un simple âdĂ©tailâ ?
Plus important encore car câest la partie visible pour lâutilisateur, la bonne gestion de CanExecute permet Ă lâUI de mieux le guider et donc dâamĂ©liorer lâergonomie de vos applications.
Quand CanExecute nâest pas gĂ©rĂ©
Quand le dĂ©veloppeur ne gĂšre pas le CanExecute des commandes celles-ci peuvent donc sâexĂ©cuter Ă tout moment dans nâimporte quel ordre quel que soit lâĂ©tat de lâautomate porte ouverte Ă tous les bogues. Autant dire que câest le boxon pour ĂȘtre poli. A moins que le dĂ©veloppeur ne barde en entrĂ©e les mĂ©thodes exĂ©cutĂ©es de tests divers et variĂ©s pour vĂ©rifier si le code peut ou non ĂȘtre exĂ©cutĂ© justement. Ce nâest pas lâendroit oĂč le faire, le CanExecute sert Ă cela⊠à moins de travailler par Aspect mais câest un autre dĂ©bat. Pour lâutilisateur câest forcĂ©ment le chaos Ă lâarrivĂ©e aprĂšs un chemin semĂ© de doutes⊠Sans parler des incohĂ©rences, plantages, et autres dĂ©synchronisation code/UI.
Quand CanExecute est géré
Bien ! Vous gĂ©rez le CanExecute de vos commandes je vous en fĂ©licite ! Mais de ce que je constate le plus souvent, mĂȘme dans ce cas oĂč le dĂ©veloppeur Ă fait son job, câest quâhĂ©las les tests de CanExecute sont soit trop succincts soit se compliquent tellement quâon en perd le fil. Quant Ă comprendre ce qui est vraiment autorisĂ© ou non dans tel ou tel Ă©tat et comment maintenir ces tests ⊠il faudrait lire en un bloc tous les tests de tous les CanExecute et resynthĂ©tiser tout cela en un schĂ©ma. Dâailleurs quels sont les Ă©tats dâun ViewModel donnĂ© ? Il est rarissime quâun dĂ©veloppeur envisage les choses sous cet angle et des incohĂ©rences apparaissent vite entrainant une UX pĂ©nible pour lâutilisateur et des bogues difficiles Ă supprimer (sans parler de la dette technique qui augmente !). Le plus souvent on teste donc ce qui semble interdire une commande mais sans avoir fait lâeffort de lister tous les Ă©tats possibles du ViewModel⊠Bref mĂȘme quand le CanExecute est gĂ©rĂ© on est loin de la perfection. TrĂšs loin.
Il ne sâagit bien entendu pas dâacadĂ©misme. La recherche dâune perfection illusoire, dâune orthodoxie tatillonne. Non, il sâagit bien dâune prĂ©occupation majeure, un programme est une machine Ă Ă©tats et ne pas matĂ©rialiser clairement ces Ă©tats câest prendre de gros risques dont le plus grave de tous est de perdre lâutilisateur dans un dĂ©dale illogique. Tout comme MVVM et le dĂ©couplage fort permettent de minimiser les erreurs et les mĂ©langes entre UI et logique, entre services et utilisateurs de services, gĂ©rer les Ă©tats dâun ViewModel minimise un risque majeur, celui de pondre du code spaghetti au cĆur mĂȘme de la logique de lâapplication !
Comment gérer correctement CanExecute ?
La critique est facile, lâart est plus difficile, câest connu. Mais vous me connaissez, plus je critique plus ma rĂ©ponse est longue ! Donc comment gĂ©rer correctement CanExecute ? En gĂ©rant bien les Ă©tats de lâautomate quâest le ViewModel. Certes. Et comment ?
En utilisant un outil adapté à cette situation : une machine à états finis.
Il y a de nombreux avantages Ă utiliser cette stratĂ©gie, notamment celui de crĂ©er un dĂ©couplage entre dâune part la logique des Ă©tats possibles et dâautre part les transitions entre ceux-ci ainsi que le code du ViewModel. Cette abstraction supplĂ©mentaire simplifie le ViewModel, le rend plus maintenable tout en rendant lisible, cohĂ©rent et facilement modifiable le workflow. Le ViewModel devient lâemplacement du code des actions, la machine Ă Ă©tats finis le mode dâemploi des commandes. LâUI dĂ©jĂ dĂ©couplĂ©e par MVVM tire profit de ce nouveau niveau de dĂ©couplage de façon automatique grĂące Ă la machine Ă Ă©tats finis qui gĂšre automatiquement le CanExecute() qui guide lâutilisateur dans sa progression. DĂ©veloppeur et utilisateur sont gagnantsâŠ
Quâest-ce quâune machine Ă Ă©tats finis ?
Une Finite State Machine, ou Machine Ă Etats Finis modĂ©lise le comportement sĂ©quentiel dâun objet. Une telle machine possĂšde donc un nombre fini dâĂ©tats et ne peut avoir quâun seul Ă©tat Ă la fois. Les machines hiĂ©rarchiques autorisent la notion de sous-Ă©tats et sont trĂšs utilisĂ©es car plus conformes Ă la complexitĂ© des mĂ©canismes modĂ©lisĂ©s en gĂ©nĂ©ral (lâhĂ©ritage de la configuration de lâĂ©tat maitre par un sous-Ă©tat simplifie son paramĂ©trage et donc celui de toute la machine).
On notera quâon appelle Automate Fini ce type de machine mais jâutilise ici la traduction littĂ©rale de lâanglais âMachine Ă Ă©tats finisâ dans le sens dâun code spĂ©cifique (une librairie) gĂ©rant les Ă©tats dâune partie dâapplication afin de le diffĂ©rencier de lâAutomate Fini quâest lâapplication elle-mĂȘme prise comme un tout. Cette nuance est artificielle et nâa de sens que dans le contexte de cet article afin de sĂ©parer les deux choses.
Une telle machine se définie donc par :
- Des Ă©tats gĂ©nĂ©ralement nommĂ©s par un adjectif indiquant une action en cours (âen train deâ, le âingâ Ă la fin dâun verbe anglais), par exemple : Listening, Waiting, Editing, Printing, etc⊠(on peut le faire en français aussi bien que je conseille par cohĂ©rence avec le langage de rester toujours en anglais dans les noms de champs, mĂ©thodes etc, sinon on obtient un franglais abominable je trouve).
- Des Ă©vĂšnements qui sont des conditions extĂ©rieures comme une action utilisateur. Ces Ă©vĂšnements sont eux aussi nommĂ©s avec des conventions diverses mais indiquant une action comme Save, Refresh ou Print ou autre dans cet esprit. Ces Ă©vĂšnement peuvent dĂ©clencher une sortie de lâĂ©tat courant vers un nouvel Ă©tat dit final.
Au final lâassociation de lâĂ©tat courant, de lâĂ©tat final, de lâĂ©vĂšnement et de lâaction forme ce quâon appelle une transition.
On retrouve cette notion de machine Ă Ă©tats finis dans toute lâinformatique puisquâelle en est la base mĂȘme et câest sans surprise que le langage de modĂ©lisation UML propose un diagramme Ă©tats-transitions dans le groupe des diagrammes comportementaux par exemple. Mais sans aller chercher loin, tout le monde connait les logigrammes ou ordinogrammes qui servent Ă dĂ©crire un algorithme Ă lâaide de boites, de flĂšches et de losanges de prise de dĂ©cision. Sans ĂȘtre tout Ă fait identiques ces diagrammes âprimitifsâ reprĂ©sentent eux aussi en quelque sorte des Ă©tats et des transitions dâun automate.
Stateless State Machine
Stateless, âsans Ă©tatâ, est un code créé par Nicholas Blumhardt qui permet facilement de coder une machine Ă Ă©tats finis hiĂ©rarchique. Le nom est Ă©trange mais on peut certainement lâexpliquer par le fait que justement câest cette machine qui gĂšre les Ă©tats et non plus le code principal de lâapplication qui devient alors âsans Ă©tatsâ. Mais bon câest une supposition et cela reste tirĂ© par les cheveux ! Heureusement ce nâest pas le nom qui nous intĂ©resse mais ce que fait âStatelessâ.
Stateless se prĂ©sente sous la forme de paquet Nuget Ă installer dans la partie Xamarin.Forms, pas besoin de le placer dans les parties natives puisquâil sâagit de pur code C# destinĂ© Ă lâApp (pas de renderer par exemple). Les derniĂšres versions sont des 5.x. On trouve le package dans Nuget :

Code minimaliste dâune machine Ă Ă©tats finis
Pour mieux comprendre Stateless commençons par coder âĂ la mainâ (sans utiliser le paquet donc) une machine Ă Ă©tats finis vraiment minimale. Elle ne connait que 2 Ă©tats, On et Off, quâun seul dĂ©clencheur dans la mĂ©thode Transition, la commande âonâ (le reste Ă©tant compris comme la commande âoffâ) :
public class BasicStateMachine
{
public string State { get; private set; }
public void Transition(string state)
{
switch (state)
{
case "on":
State = "ON";
OnSwitchedOn();
break;
default:
State = "OFF";
OnSwitchedOff();
break;
}
}
private void OnSwitchedOn()
{
// Do something
}
private void OnSwitchedOff()
{
// Stop doing something
}
}Ce code est rĂ©ellement direct et dĂ©jĂ contient les germes de la problĂ©matique des commandes, sujet principal de cet article ne lâoublions pas mĂȘme si je suis obligĂ© de vous prĂ©senter tout cela avant dây arriver !
En effet, un choix a Ă©tĂ© fait ici dans lâimplĂ©mentation, le switch bascule vers On dans le cas oĂč la commande âonâ est envoyĂ©e et renvoie vers lâĂ©tat Off dans tous les autres cas. Câest un choix quâil faut assumer. La machine doit-elle sâĂ©teindre si je lui commande âtotoâ ou _uniquement_ si je lui commande âoffâ ? Un autre aspect est nĂ©gligĂ© : dans quel Ă©tat se trouve lâautomate au dĂ©part ?
Sans diagramme on se rend compte que mĂȘme dans un cas aussi simple les problĂšmes se posent. Alors imaginez un peu ce qui se passe dans le code rĂ©el dâun ViewModel un peu complexe oĂč cet aspect nâest pas mĂȘme gĂ©rĂ© !!!
Schématisons un peu le code ci-dessus :

LâĂ©tat initial nâest pas prĂ©cisĂ©, premiĂšre erreur qui devient Ă©vidente quand on reprĂ©sente le schĂ©ma. LâĂ©tat final aussi nâest pas prĂ©cisĂ©, la machine est donc Ă fonctionnement infini, il nây pas de sortie, ce qui nâest pas une erreur mais un choix.
Enfin on note les deux Ă©tats On et Off et les transitions. La premiĂšre va de On vers Off et nâest pas dĂ©finie, nâimporte quel Ă©vĂšnement fera donc basculer vers Off. La seconde va de Off Ă On et ne fonctionne que sur lâenvoi de la commande âonâ ce qui est trĂšs restrictif mais qui peut sâadmettre.
Bref, une fois le diagramme posĂ© on sâaperçoit des manques et incohĂ©rences.
On notera au passage que je parle de âcommandesâ pour passer dâun Ă©tat Ă lâautre ce nâest pas une terminologie tout Ă fait exacte (on parle dâĂ©vĂšnements ou de dĂ©clencheurs) mais cela est Ă mettre en rapport avec le sujet de lâarticle qui concerne les commandes qui sont bien Ă la source des Ă©vĂšnements qui causent les transitions. Pour approfondir toute la rigueur de la notation UML des diagrammes dâĂ©tats transitions je renvoie le lecteur intĂ©ressĂ© Ă la nombreuse littĂ©rature technique sur le sujet.
Si on modélise la machine de façon plus sensée (en y réfléchissant donc) on voit les états, les transitions, ce qui donne plutÎt cela :

Comme on visualise le diagramme on sâaperçoit quâil faut bien un Ă©tat initial, et ici il est Ă âOffâ. Ensuite sâil faut une commande âonâ pour passer Ă âOnâ, il faut aussi pour ĂȘtre rigoureux une commande âoffâ pour passer Ă âOffâ. Ce nâest pas innocent comme choixâŠ
Si nâimporte quoi fait basculer Ă Off comme dans le code de dĂ©part, cela peut ĂȘtre voulu, une machine outil par exemple est dangereuse, dans la prĂ©cipitation dâun accident lâopĂ©rateur peut par affolement appuyer nâimporte oĂč et il est prĂ©fĂ©rable dans ce contexte que cela passe Ă âOffâ, mieux vaut arrĂȘter la machine pour rien quâarracher un bras... Si maintenant câest un systĂšme de circulation extracorporelle utilisĂ© durant les pontages coronariens (qui remplace le cĆur pendant quâon le âdĂ©monteâ), il est prĂ©fĂ©rable que la commande âOffâ soit au contraire hyper verrouillĂ©e avec mĂȘme une clĂ© Ă tourner, un code Ă saisir, etc.
il est vraiment essentiel de comprendre que toutes ces petites choses apparemment de lâordre du dĂ©tail ont en rĂ©alitĂ© un impact gigantesque sur lâapplication et sa raison dâĂȘtre, sa finalitĂ©, ses contraintes. Et que tout cela OBLIGE a se poser des questions qui ne viendrait jamais en tĂȘte sans cet effort de formalisation !
Il nây a pas dâĂ©tat final dans lâexemple corrigĂ©, câest une machine Ă Ă©tats finis mais Ă fonctionnement infini, pourquoi pas. On pourrait en revanche pousser plus loin le diagramme en indiquant un Ă©tat âErrorâ si une commande diffĂ©rente de âonâ ou âoffâ est envoyĂ©e ou bien reprĂ©senter une boucle sur les Ă©tats âOnâ et âOffâ qui indiquerait que toute commande diffĂ©rente de celle attendue fait rester la machine dans son Ă©tat courant.
LĂ encore rien nâest de lâordre du dĂ©tail, les questions soulevĂ©es par ces deux boites, ce point et ces deux flĂšches vont creuser trĂšs loin dans lâanalyse fonctionnelle du logiciel, bien plus loin quâon ne lâimagine au dĂ©part. Les diagrammes en gĂ©nĂ©ral, mĂȘme de SGBD sont des outils essentiels en cela que la prĂ©sence ou non dâune flĂšche, dâune orientation, dâune cardinalitĂ©, aussi insignifiant que cela paraisse soulĂšve des questions dâune grande profondeur dans la comprĂ©hension du mĂ©canisme modĂ©lisĂ©, questions qui sont rarement posĂ©es autrement.
Nous nâirons pas trop loin dans la modĂ©lisation de notre exemple et regardons simplement lâimpact du diagramme corrigĂ© succinctement sur le code :
public class BasicStateMachine
{
const string offState = "OFF";
const string onState = "ON";
private string state = offState;
public string State {
get { return state;}
private set { state=value;}
}
public void Transition(string state)
{
switch (state.ToUpper())
{
case "ON":
State = onState;
OnSwitchedOn();
break;
case "OFF":
State = offState;
OnSwitchedOff();
break;
}
}
private void OnSwitchedOn()
{
// Do something
}
private void OnSwitchedOff()
{
// Stop doing something
}
}La machine ainsi codĂ©e possĂšde un Ă©tat initial, âOffâ, et ne bascule de lâun Ă lâautre que si les commandes âonâ et âoffâ, peu importe la casse, sont envoyĂ©es. Tout autre commande laisse la machine dans son Ă©tat courant.
Finalement nous avons un code plus rigoureux mais sâil fallait reprĂ©senter une vĂ©ritable machine Ă Ă©tats finis et la faire Ă©voluer il faut avouer que cela deviendrait vite fastidieux avec une telle programmation âmanuelleâ.
Stateless pour coder la machine à états
Avec Stateless la mĂȘme machine se codera de la façon suivante :
public class StatelessStateMachine
{
private readonly StateMachine stateMachine;
public enum Trigger
{
TurnOn,
TurnOff
}
public enum State
{
On,
Off
}
public State Current => stateMachine.State;
public StatelessStateMachine()
{
stateMachine = new StateMachine(State.Off);
stateMachine.Configure(State.Off)
.Permit(Trigger.TurnOn, State.On)
.OnEntry(OnSwitchedOff);
stateMachine.Configure(State.On)
.Permit(Trigger.TurnOff, State.Off)
.OnEntry(OnSwitchedOn);
}
public bool Transition(Trigger trigger)
{
if (!stateMachine.CanFire(trigger))
return false;
stateMachine.Fire(trigger);
return true;
} private void OnSwitchedOn()
{
// Do something clever
}
private void OnSwitchedOff()
{
// Stop doing something clever
}
}Câest un peu long pour si peu vous direz-vous et câest normal, notre machine est tellement bĂȘte et simple que le code pour lâexprimer est forcĂ©ment long par rapport Ă son utilitĂ© rĂ©elle, ce nâest quâune sorte de Hello Word pour StatelessâŠ
Mais si vous lisez attentivement ce code vous vous apercevrez rapidement quâil est bien plus clair et bien plus structurĂ©. Et surtout quâil est bien plus souple, chaque Ă©tat est dĂ©fini, chaque Ă©vĂšnement aussi, et lâAPI de type âfluentâ qui permet de dĂ©finir tout cela est aussi simple que puissante.
Modifier un Ă©tat, une transition, ajouter des actions dâentrĂ©es et de sorties Ă une transition, etc, tout cela devient limpide.
Stateless utilise des méthodes génériques et le type des états ou des triggers est totalement libre. Ici on utilise des énumérations mais des objets plus complexes sont utilisables si cela le demande.
Et le rapport avec les commandes en MVVM ?
Câest en fait tout le sujet de lâarticle.
Maintenant vous avez une idĂ©e de ce quâest une machine Ă Ă©tats finis et de ce quâest la librairie Stateless et comment elle sâutilise (au moins vous en avez une idĂ©e).
CâĂ©tait un prĂ©ambule nĂ©cessaire. Le âvĂ©ritableâ article commence ici.
âLa continuitĂ© câest maintenant !â (enfin un slogan politique qui ne ment pas !).
Retour à la problématique des commandes
Revenons sur le problĂšme que pose les commandes. On a bien compris quâelles se fondent sur lâinterface ICommand (mĂȘme si on utilise un RelayCommand ou une Command) et que les mĂ©thodes principales sont Execute() qui exĂ©cute lâaction, CanExecute() qui teste si la commande peut sâexĂ©cuter et CanExecuteChanged() un Ă©vĂšnement qui permet Ă lâUI de savoir si CanExecute() Ă changĂ© de valeur.
LâintĂ©rĂȘt dâune classe comme RelayCommand ou Command est de proposer non pas une interface quâil faut implĂ©menter Ă chaque fois mais une classe dont on peut crĂ©er des instances immĂ©diatement ce qui est bien plus pratique. Autre apport de RelayCommand / Command, la mĂ©thode RaiseCanExecuteChanged() qui dĂ©clenche lâĂ©vĂšnement CanExecuteChanged(). En effet, si les possibilitĂ©s dâexĂ©cution changent et bien quâil existe un Ă©vĂšnement ad hoc auquel sâabonner dans ICommand rien ne permet dans cette interface de forcer cet Ă©vĂšnement et donc de prĂ©venir lâUI que âlâexĂ©cutabilitĂ©â de la commande vient de changer.
Je nâirai pas trop loin sur ce point bien quâessentiel car jâai dĂ©jĂ Ă©crit de trĂšs nombreux articles et livres sur MVVM et notamment sur la façon de gĂ©rer les commandes. Le lecteur intĂ©ressĂ© saura retrouver tout cela sur Dot.Blog et dans la collection âAll Dot.Blogâ jâen suis certain.
Comme toujours lâenfer se loge dans les fameux dĂ©tails. Le vrai chalenge nâest pas dans lâexĂ©cution de la commande, le code derriĂšre Execute(), mais dans le fait de sâassurer quâil sâexĂ©cute quand le ViewModel est dans un Ă©tat valide pour lâaction donnĂ©e.
Typiquement le code du CanExecute(), quand il est gĂ©rĂ©, est implĂ©mentĂ© sous la forme dâexpressions conditionnelles qui utilisent des variables locales de type âisLoadingâ âisPrintingâ âCanDoxxxâ âobjectMachin!=nullâ etcâŠ
Chaque nouvel Ă©tat nĂ©cessite de coder des conditions supplĂ©mentaires qui nĂ©cessitent une Ă©valuation, de nouvelles variables locales, etc, ce qui trĂšs rapidement devient dâune complexitĂ© impossible Ă maitriser.
Sous XAML on dispose dâun outil qui permet de gĂ©rer les Ă©tats visuels, le Visual State Manager, câest une aide prĂ©cieuse pour clarifier les choses. Mais si lâUI est dĂ©couplĂ©e du code, ses Ă©tats reposent sur ceux du code. Il ne peut y avoir une reprĂ©sentation de lâĂ©tat âattenteâ dans le visuel XAML sâil nây a pas un Ă©tat correspondant dans le ViewModel. Tout tient donc sur la bonne gestion des Ă©tats de la machine Ă Ă©tats finis qui permet de construire un code solide et cohĂ©rent aussi bien en C# que cĂŽtĂ© UI en XAML. Une raison de plus dâadopter lâapproche que je vous propose dans le prĂ©sent article !
Le rÎle de la machine à états finis
La machine Ă Ă©tats finis va permettre de modĂ©liser tous les Ă©tats du ViewModel, donc ce qui est autorisĂ© ou non de faire selon lâĂ©tat en cours.
En construisant des commandes Ă partir du mĂ©canisme de la machine et en laissant cette derniĂšre dĂ©cider des actions et de la gestion du CanExecute il sera mĂȘme possible dâautomatiser les rĂ©actions de lâUI puisque les objets supportant ICommand savent rĂ©agir au CanExecute en modifiant leur aspect visuel (qui peut de plus ĂȘtre retravaillĂ© grĂące Ă la grande souplesse de XAML, Xamarin.Forms nâimplĂ©mentant quelque chose que pour le Button Ă lâheure actuelle).
On créé ainsi un nouveau dĂ©coupage intĂ©ressant : lâUI dâune part dont le rĂŽle ne change pas, le ViewModel qui devient un magasin de code (les actions) et dâautre part la machine Ă Ă©tats finis qui joue le chef dâorchestre afin de garantir la cohĂ©rence de lâĂ©tat du ViewModel et de lâUI.
Un exemple simple
Pour tenter dâĂȘtre concret nous allons partir dâun exemple simple. Une pseudo gestion du personnel qui permet de faire des recherches, dâafficher la liste des personnes filtrĂ©es et bien entendu la modification dâune fiche sĂ©lectionnĂ©e. Pour rendre tout cela encore plus Ă©vident et Ă©viter dâavoir trop de code parasite le mode de recherche se limitera Ă faire un refresh de la liste et simulera un temps dâattente (pour voir lâĂ©tat visuel de lâapplication dans son mode âbusyâ).
Afin de comprendre les changements dâĂ©tats et bien que cela sera absent dâune application rĂ©elle, nous allons adjoindre lâaffichage de lâĂ©tat courant ainsi quâune liste qui va se remplir au fur et Ă mesure de tous les Ă©tats qui se succĂšdent.
Il serait un peu stupide de recoder un nouvel exemple puisque les principes sont identiques, je renvoie ainsi le lecteur Ă lâexemple WPF original.
Téléchargez directement le Projet WPF VS 2013 du Code Démo (pointe le répertoire partagé des exemples Dot.Blog, répertoire Dropbox, chercher le fichier DemoMachineEtats.7z).
Un bon exercice sera de le transformer en Xamarin.Forms, mais lâutilisation de la Datagrid qui nâexiste pas de base ni Ă lâidentique, le ruban supĂ©rieur quâil faudra simuler, la mise en page forcĂ©ment pas adaptĂ©e Ă un smartphone tout cela fait quâil y aura du travail ! Le mieux serait de penser une nouvelle dĂ©mo adaptĂ©e Ă un smartphone. Si cela pourrait me prendre plus de temps que d'Ă©crire l'article, en revanche cela vous ferait un bon entraĂźnement, que vous pourriez Ă©ventuellement partager avec nous ! Un courageux ou une courageuse pour se dĂ©vouer ?
Pour ceux qui ne veulent pas télécharger le code WPF et le faire tourner, voici un gif animé qui en fait une démonstration assez complÚte :

Conclusion
Il faut bien terminer un jour⊠Jâai passĂ© trois jours sur lâarticle original et malgrĂ© la similitude je viens de passer tout un dimanche aprĂšs-midi Ă crĂ©er cette version Xamarin.Forms, conclure prend donc ici toute sa signification !
JâespĂšre que vous avez compris le principe et surtout les intĂ©rĂȘts nombreux quâil y a Ă suivre ce pattern de lâAutomate Fini en lâappliquant Ă MVVM et XAML.
Non seulement les Ă©tats du ViewModel deviennent clairs et maintenables, cohĂ©rents et inviolables, mais cette approche force Ă penser le workflow, les Ă©tats et transitions du ViewModel trĂšs tĂŽt dans le dĂ©veloppement de celui-ci. Câest un gain Ă©norme qui permet de lever des loups avant mĂȘme de coder, de sâapercevoir que le cahier des charges ne prĂ©voit rien dans telle ou telle situation par exemple. Car noyĂ©e dans une prose rĂ©barbative une analyse cache souvent beaucoup de choses !
Le code exemple complet est tĂ©lĂ©chargeable en fin dâarticle, amusez-vous bien ! (car dĂ©velopper correctement avec les bonnes mĂ©thodes est un plaisir).
Le sujet est vaste alors voici quelques références intéressantes :
Tout dâabord Tarquin Vaughan-Scott Ă qui jâai empruntĂ© originellement une partie de lâarticle et du code de dĂ©mo quâil avait publiĂ© dans un article en 2014 dans MSDN qui mâavait sĂ©duit et dont je voulais absolument vous parler. Mieux vaut tard que jamais ! Et jâai rĂ©ussi Ă faire mieux que de vous en parler, en tout cas je lâespĂšre, en Ă©crivant ce long article qui enrichit beaucoup lâoriginal et le rend plus accessible Ă ceux dâentre-vous qui ne sont pas des fans de lâanglaisâŠ
Cet article de Bob Nystrom (en anglais) qui présente assez bien le concept de machines à états.
Tom Anderson et sa rapide introduction Ă âStatelessâ dont est issu le premier exemple de lâarticle.
On pourrait certainement encore allonger cette liste car le sujet est vaste dĂšs quâon parle de MVVM, de son systĂšme de commande et lorsquâon y ajoute les automates finis ! Mais jâajouterais forcĂ©ment la collection All.Dot.Blog dont certains tomes sont particuliĂšrement orientĂ©s XAML ou MVVM.
Bref, il y a de quoi lire. Et surtout de quoi rĂ©flĂ©chirâŠ
Stay Tuned !