Partager via


Modèle Model-View-ViewModel

Remarque

Ce livre électronique a été publié au printemps 2017 et n’a pas été mis à jour depuis. Il y a beaucoup dans le livre qui reste précieux, mais certains de la matière est obsolète.

L’expérience Xamarin.Forms du développeur implique généralement la création d’une interface utilisateur en XAML, puis l’ajout de code-behind qui fonctionne sur l’interface utilisateur. À mesure que les applications sont modifiées et augmentent en taille et en étendue, des problèmes de maintenance complexes peuvent survenir. Ces problèmes incluent le couplage étroit entre les contrôles d’IU et la logique métier, ce qui augmente le coût des modifications de l’IU ainsi que la difficulté d’effectuer des tests unitaires sur ce code.

Le modèle Model-View-ViewModel (MVVM) permet de séparer correctement la logique métier et de présentation d’une application de son interface utilisateur. La maintenance d’une séparation propre entre la logique d’application et l’interface utilisateur permet de résoudre de nombreux problèmes de développement et peut faciliter le test, la maintenance et l’évolution d’une application. Il peut également améliorer considérablement les opportunités de réutilisation du code et permet aux développeurs et aux concepteurs d’interface utilisateur de collaborer plus facilement lors du développement de leurs parties respectives d’une application.

Modèle MVVM

Il existe trois composants principaux dans le modèle MVVM : le modèle, la vue et le modèle de vue. Chacun sert un objectif distinct. La figure 2-1 montre les relations entre les trois composants.

Le modèle MVVM

Figure 2-1 : Modèle MVVM

En plus de comprendre les responsabilités de chaque composant, il est également important de comprendre comment ils interagissent les uns avec les autres. À un niveau supérieur, la vue « connaît » le modèle de vue, et le modèle de vue « connaît » le modèle, mais le modèle ignore l’existence du modèle de vue et le modèle de vue ignore l’existence de la vue. Ainsi, le modèle de vue isole la vue du modèle et permet au modèle d’évoluer indépendamment de la vue.

Les avantages liés à l’utilisation du modèle MVVM sont les suivants :

  • S’il existe une implémentation de modèle existante qui encapsule une logique métier existante, il peut être difficile ou risqué de le modifier. Dans ce scénario, le modèle d’affichage agit en tant qu’adaptateur pour les classes de modèle et vous permet d’éviter d’apporter des modifications majeures au code du modèle.
  • Les développeurs peuvent créer des tests unitaires pour le modèle de vue et le modèle, sans utiliser la vue. Les tests unitaires du modèle de vue peuvent offrir exactement les mêmes fonctionnalités que celles utilisées par la vue.
  • L’interface utilisateur de l’application peut être repensée sans toucher au code, à condition que la vue soit entièrement implémentée en XAML. Une nouvelle version de la vue doit donc fonctionner avec le modèle de vue existant.
  • Les concepteurs et les développeurs peuvent travailler indépendamment et simultanément sur leurs composants pendant le processus de développement. Les concepteurs peuvent se concentrer sur la vue, tandis que les développeurs peuvent travailler sur le modèle de vue et les composants du modèle.

La clé de l’utilisation de MVVM consiste à comprendre comment factoriser le code d’application dans les classes correctes et comprendre comment les classes interagissent. Les sections suivantes décrivent les responsabilités de chacune des classes du modèle MVVM.

Affichage

La vue est chargée de définir la structure, la disposition et l’apparence de ce que l’utilisateur voit à l’écran. Dans l’idéal, chaque vue est définie en XAML, avec un code-behind limité qui ne contient pas de logique métier. Toutefois, dans certains cas, le code-behind peut contenir une logique d’IU qui implémente un comportement visuel difficile à exprimer en XAML, par exemple les animations.

Dans une Xamarin.Forms application, une vue est généralement une Pageclasse dérivée ou ContentViewdérivée. Toutefois, les vues peuvent également être représentées par un modèle de données, qui spécifie les éléments d’IU à utiliser pour représenter visuellement un objet quand il est affiché. Un modèle de données en tant que vue n’a pas de code-behind et est conçu pour être lié à un type de modèle de vue spécifique.

Conseil

Évitez d’activer et de désactiver les éléments d’IU dans le code-behind. Assurez-vous que les modèles d’affichage sont responsables de la définition des modifications d’état logique qui affectent certains aspects de l’affichage de la vue, par exemple si une commande est disponible ou une indication qu’une opération est en attente. Vous devez donc activer et désactiver les éléments d’IU en établissant une liaison aux propriétés du modèle de vue, au lieu de les activer et de les désactiver dans le code-behind.

Il existe plusieurs options pour exécuter du code sur le modèle de vue en réponse aux interactions avec la vue, par exemple un clic sur un bouton ou la sélection d’un élément. Si un contrôle prend en charge les commandes, la propriété du Command contrôle peut être liée aux données d’une ICommand propriété sur le modèle d’affichage. Quand la commande du contrôle est appelée, le code du modèle de vue est exécuté. En plus des commandes, les comportements peuvent être attachés à un objet dans la vue et peuvent écouter une commande à appeler ou un événement à lever. En réponse, le comportement peut ensuite appeler un modèle d’affichage ICommand ou une méthode sur le modèle d’affichage.

Vue modèle

Le modèle de vue implémente des propriétés et des commandes avec lesquelles la vue peut effectuer une liaison aux données. Il notifie la vue de tout changement d’état via des événements de notification de changement. Les propriétés et les commandes fournies par le modèle de vue définissent la fonctionnalité à offrir par l’IU, mais la vue détermine la façon dont cette fonctionnalité doit être affichée.

Conseil

Gardez l’IU réactive avec les opérations asynchrones. Les applications mobiles doivent conserver le thread d’interface utilisateur débloqué pour améliorer la perception des performances de l’utilisateur. Ainsi, dans le modèle de vue, utilisez des méthodes asynchrones pour les opérations d’E/S, et déclenchez des événements pour envoyer aux vues de manière asynchrone des notifications sur les changements de propriétés.

Le modèle de vue est également responsable de la coordination des interactions de la vue avec les classes de modèle nécessaires. Il existe généralement une relation un-à-plusieurs entre le modèle de vue et les classes de modèle. Le modèle de vue peut exposer les classes de modèle directement à la vue pour que les contrôles de la vue puissent effectuer une liaison aux données directement avec celles-ci. Dans ce cas, les classes de modèle doivent être conçues pour prendre en charge les événements de liaison de données et de notification de changement.

Chaque modèle de vue fournit les données d’un modèle sous une forme que la vue peut facilement consommer. Pour ce faire, le modèle de vue effectue parfois une conversion de données. Il est judicieux de placer cette conversion de données dans le modèle de vue, car elle fournit des propriétés auxquelles la vue peut se lier. Par exemple, le modèle d’affichage peut combiner les valeurs de deux propriétés pour faciliter l’affichage par l’affichage.

Conseil

Centralisez les conversions de données dans une couche de conversion. Il est également possible d’utiliser des convertisseurs en tant que couche de conversion de données distincte située entre le modèle de vue et la vue. Cela peut être nécessaire, par exemple, quand les données nécessitent une mise en forme spéciale que le modèle de vue ne fournit pas.

Pour que le modèle de vue participe à la liaison de données bidirectionnelle avec la vue, ses propriétés doivent déclencher l’événement PropertyChanged. Les modèles de vue répondent à cet impératif en implémentant l’interface INotifyPropertyChanged et en déclenchant l’événement PropertyChanged en cas de changement d’une propriété.

Pour les collections, le ObservableCollection<T> adapté aux vues est fourni. Cette collection implémente la notification des changements apportés aux collections, ce qui évite au développeur d’implémenter l’interface INotifyCollectionChanged sur les collections.

Modèle

Les classes de modèle sont des classes non visuelles qui encapsulent les données de l’application. Ainsi, le modèle peut être considéré comme une représentation du modèle de domaine de l’application, qui comprend généralement un modèle de données avec une logique métier et une logique de validation. Parmi les exemples d’objets de modèle, citons les objets de transfert de données (DTO), les objets CLR traditionnels (POCO) ainsi que les objets d’entité et de proxy générés.

Les classes de modèle sont généralement utilisées conjointement avec des services ou des dépôts qui encapsulent l’accès aux données et la mise en cache.

Connexion de modèles d’affichage à des vues

Les modèles d’affichage peuvent être connectés aux vues à l’aide des fonctionnalités de liaison de données de Xamarin.Forms. Il existe de nombreuses approches qui permettent de construire des vues et des modèles de vue, et de les associer au moment de l’exécution. Ces approches se divisent en deux catégories, appelées composition de vue en premier et composition de modèle de vue en premier. Le choix entre la composition de vue en premier et la composition de modèle de vue en premier est une question de préférence et de complexité. Toutefois, toutes les approches partagent le même objectif : pour la vue, un modèle de vue doit être affecté à sa propriété BindingContext.

Avec la composition de vue en premier, l’application est composée conceptuellement de vues qui se connectent aux modèles de vue dont elles dépendent. Le principal avantage de cette approche est qu’elle facilite la construction d’applications faiblement couplées et pouvant faire l’objet de tests unitaires, car les modèles de vue ne dépendent pas des vues elles-mêmes. Il est également facile de comprendre la structure de l’application en suivant sa structure visuelle, au lieu d’avoir à suivre l’exécution du code pour comprendre la façon dont les classes sont créées et associées. En outre, la première construction d’affichage s’aligne sur le Xamarin.Forms système de navigation chargé de construire des pages lorsque la navigation se produit, ce qui rend un modèle d’affichage complexe et mal aligné sur la plateforme.

Avec la composition du modèle d’affichage, l’application est composée conceptuellement de modèles d’affichage, avec un service chargé de localiser l’affichage d’un modèle d’affichage. La composition de modèle de vue en premier semble plus naturelle à certains développeurs, car la création de la vue peut être mise de côté, ce qui leur permet de se concentrer sur la structure logique de l’application hors IU. De plus, elle permet la création de modèles de vue par d’autres modèles de vue. Toutefois, cette approche est souvent complexe et peut devenir difficile à comprendre comment les différentes parties de l’application sont créées et associées.

Conseil

Gardez une indépendance entre les modèles de vue et les vues. La liaison des vues à une propriété dans une source de données doit correspondre à la dépendance principale de la vue par rapport au modèle de vue correspondant. Plus précisément, ne référencez pas les types d’affichage, tels que Button et ListView, à partir de modèles d’affichage. En suivant les principes décrits ici, les modèles de vue peuvent être testés isolément, ce qui réduit la probabilité de défauts logiciels en limitant l’étendue.

Les sections suivantes décrivent les principales approches de connexion des modèles de vue aux vues.

Création d’un modèle d’affichage de manière déclarative

L’approche la plus simple consiste pour la vue à instancier de manière déclarative son modèle de vue correspondant en XAML. Quand la vue est construite, l’objet de modèle de vue correspondant est également construit. Cette approche est illustrée dans l’exemple de code suivant :

<ContentPage ... xmlns:local="clr-namespace:eShop">  
    <ContentPage.BindingContext>  
        <local:LoginViewModel />  
    </ContentPage.BindingContext>  
    ...  
</ContentPage>

Quand ContentPage est créé, une instance de LoginViewModel est automatiquement construite et définie en tant que BindingContext de la vue.

La construction et l’affectation déclaratives du modèle de vue par la vue présentent l’avantage d’être simples, mais présentent l’inconvénient de nécessiter un constructeur par défaut (sans paramètre) dans le modèle de vue.

Création d’un modèle d’affichage par programmation

Une vue peut avoir du code dans le fichier code-behind qui entraîne l’affectation du modèle d’affichage à sa BindingContext propriété. Cela s’effectue souvent dans le constructeur de la vue, comme le montre l’exemple de code suivant :

public LoginView()  
{  
    InitializeComponent();  
    BindingContext = new LoginViewModel(navigationService);  
}

La construction et l’affectation programmatiques du modèle de vue dans le code-behind de la vue présentent l’avantage d’être simples. Toutefois, le principal inconvénient de cette approche est que la vue doit fournir au modèle de vue toutes les dépendances nécessaires. L’utilisation d’un conteneur d’injection de dépendances peut contribuer à maintenir un couplage faible entre la vue et le modèle de vue. Pour plus d’informations, consultez l’injection de dépendances.

Création d’une vue définie en tant que modèle de données

Une vue peut être définie en tant que modèle de données et associée à un type de modèle d’affichage. Les modèles de données peuvent être définis en tant que ressources, ou ils peuvent être définis inline dans le contrôle qui affiche le modèle d’affichage. Le contenu du contrôle est l’instance de modèle d’affichage et le modèle de données est utilisé pour le représenter visuellement. Cette technique est un exemple de situation dans laquelle le modèle d’affichage est instancié en premier, suivi de la création de la vue.

Création automatique d’un modèle d’affichage avec un localisateur de modèle d’affichage

Un localisateur de modèles d’affichage est une classe personnalisée qui gère l’instanciation des modèles d’affichage et leur association aux vues. Dans l’application mobile eShopOnContainers, la ViewModelLocator classe a une propriété jointe, AutoWireViewModelutilisée pour associer des modèles d’affichage à des vues. Dans le code XAML de la vue, cette propriété jointe a la valeur true pour indiquer que le modèle d’affichage doit être automatiquement connecté à la vue, comme illustré dans l’exemple de code suivant :

viewModelBase:ViewModelLocator.AutoWireViewModel="true"

La AutoWireViewModel propriété est une propriété pouvant être liée qui est initialisée à false, et lorsque sa valeur change le OnAutoWireViewModelChanged gestionnaire d’événements est appelée. Cette méthode résout le modèle d’affichage de la vue. L’exemple de code suivant montre comment procéder :

private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)  
{  
    var view = bindable as Element;  
    if (view == null)  
    {  
        return;  
    }  

    var viewType = view.GetType();  
    var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");  
    var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;  
    var viewModelName = string.Format(  
        CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName);  

    var viewModelType = Type.GetType(viewModelName);  
    if (viewModelType == null)  
    {  
        return;  
    }  
    var viewModel = _container.Resolve(viewModelType);  
    view.BindingContext = viewModel;  
}

La OnAutoWireViewModelChanged méthode tente de résoudre le modèle d’affichage à l’aide d’une approche basée sur une convention. Cette convention suppose que :

  • Les modèles d’affichage se trouvent dans le même assembly que les types d’affichage.
  • Les vues sont dans un . Affiche l’espace de noms enfant.
  • Les modèles d’affichage sont dans un . Espace de noms enfant ViewModels.
  • Les noms des modèles d’affichage correspondent aux noms d’affichage et se terminent par « ViewModel ».

Enfin, la OnAutoWireViewModelChanged méthode définit le BindingContext type de vue sur le type de modèle de vue résolu. Pour plus d’informations sur la résolution du type de modèle d’affichage, consultez Résolution.

Cette approche présente l’avantage qu’une application possède une classe unique responsable de l’instanciation des modèles d’affichage et de leur connexion aux vues.

Conseil

Utilisez un localisateur de modèle d’affichage pour faciliter la substitution. Un localisateur de modèle d’affichage peut également être utilisé comme point de substitution pour d’autres implémentations de dépendances, telles que pour les données de test unitaire ou de temps de conception.

Mise à jour des vues en réponse aux modifications dans le modèle ou le modèle d’affichage sous-jacent

Toutes les classes de modèle de vue et de modèle accessibles à une vue doivent mettre en œuvre l'interface INotifyPropertyChanged. L’implémentation de cette interface dans une classe de modèle de vue ou de modèle permet à la classe de fournir des notifications de changement à tous les contrôles liés aux données dans la vue en cas de changement de la valeur de propriété sous-jacente.

Les applications doivent être conçues pour l’utilisation correcte de la notification de modification de propriété, en répondant aux exigences suivantes :

  • Déclenchez toujours un événement PropertyChanged en cas de changement de la valeur d’une propriété publique. Ne partez pas du principe que le déclenchement de l’événement PropertyChanged peut être ignoré dans la mesure où vous savez comment la liaison XAML se produit.
  • Déclenchez toujours un événement PropertyChanged pour les propriétés calculées dont les valeurs sont utilisées par d’autres propriétés dans le modèle de vue ou le modèle.
  • Déclenchez toujours l’événement PropertyChanged à la fin de la méthode qui effectue un changement de propriété, ou quand l’objet est dans un état sécurisé. Le déclenchement de l’événement interrompt l’opération en appelant les gestionnaires d’événements de manière synchrone. Si cela se produit au milieu d’une opération, l’objet peut être exposé à des fonctions de rappel alors qu’il se trouve dans un état non sécurisé et partiellement mis à jour. De plus, il est possible que des changements en cascade soient déclenchés par les événements PropertyChanged. Pour pouvoir être effectués de manière sécurisée, les changements en cascade nécessitent généralement au préalable l’exécution des mises à jour.
  • Ne déclenchez jamais d’événement PropertyChanged si la propriété ne change pas. Cela signifie que vous devez comparer les anciennes et les nouvelles valeurs avant de déclencher l’événement PropertyChanged.
  • Ne déclenchez jamais l’événement PropertyChanged durant l’exécution du constructeur d’un modèle de vue si vous initialisez une propriété. Les contrôles liés aux données dans la vue ne se sont pas abonnés à la réception des notifications de changement à ce stade.
  • Ne déclenchez jamais plusieurs événements PropertyChanged avec le même argument de nom de propriété dans un seul appel synchrone d’une méthode publique d’une classe. Par exemple, pour une propriété NumberOfItems dont le magasin de stockage est le champ _numberOfItems, si une méthode incrémente _numberOfItems 50 fois durant l’exécution d’une boucle, elle ne doit déclencher qu’une seule fois la notification de changement de propriété sur la propriété NumberOfItems, une fois le travail effectué. Avec les méthodes asynchrones, déclenchez l’événement PropertyChanged pour un nom de propriété donné dans chaque segment synchrone d’une chaîne de continuation asynchrone.

L’application mobile eShopOnContainers utilise la ExtendedBindableObject classe pour fournir des notifications de modification, qui s’affiche dans l’exemple de code suivant :

public abstract class ExtendedBindableObject : BindableObject  
{  
    public void RaisePropertyChanged<T>(Expression<Func<T>> property)  
    {  
        var name = GetMemberInfo(property).Name;  
        OnPropertyChanged(name);  
    }  

    private MemberInfo GetMemberInfo(Expression expression)  
    {  
        ...  
    }  
}

La classe Xamarin.Form implémente l’interface BindableObject INotifyPropertyChanged et fournit une OnPropertyChanged méthode. La classe ExtendedBindableObject fournit la méthode RaisePropertyChanged pour appeler la notification de changement de propriété. Ainsi, elle utilise les fonctionnalités fournies par la classe BindableObject.

Chaque classe de modèle d’affichage dans l’application mobile eShopOnContainers dérive de la ViewModelBase classe, qui dérive à son tour de la ExtendedBindableObject classe. Ainsi, chaque classe de modèle de vue utilise la méthode RaisePropertyChanged de la classe ExtendedBindableObject pour fournir une notification de changement de propriété. L’exemple de code suivant montre comment l’application mobile eShopOnContainers appelle la notification de modification de propriété à l’aide d’une expression lambda :

public bool IsLogin  
{  
    get  
    {  
        return _isLogin;  
    }  
    set  
    {  
        _isLogin = value;  
        RaisePropertyChanged(() => IsLogin);  
    }  
}

Notez que l’utilisation d’une expression lambda de cette façon implique un coût de performances réduit, car l’expression lambda doit être évaluée pour chaque appel. Bien que le coût des performances soit faible et n’affecte pas normalement une application, les coûts peuvent s’accumuler lorsqu’il existe de nombreuses notifications de modification. Toutefois, cette approche présente l’avantage d’offrir une cohérence des types au moment de la compilation et une prise en charge de la refactorisation au moment du renommage des propriétés.

Interaction de l’interface utilisateur à l’aide de commandes et de comportements

Dans les applications mobiles, les actions sont généralement appelées en réponse à une action utilisateur, par exemple un clic de bouton, qui peut être implémentée en créant un gestionnaire d’événements dans le fichier code-behind. Toutefois, dans le modèle MVVM, la responsabilité de l’implémentation de l’action incombe au modèle de vue. De plus, le placement de code dans le code-behind doit être évité.

Les commandes offrent un moyen pratique de représenter des actions qui peuvent être liées à des contrôles dans l’IU. Ils encapsulent le code qui implémente l’action et aident à le dissocier de sa représentation visuelle dans la vue. Xamarin.Forms inclut des contrôles qui peuvent être connectés de manière déclarative à une commande, et ces contrôles appellent la commande lorsque l’utilisateur interagit avec le contrôle.

Les comportements permettent également aux contrôles d’être connectés de manière déclarative à une commande. Toutefois, les comportements peuvent être utilisés pour appeler une action associée à une plage d’événements déclenchée par un contrôle. Ainsi, les comportements répondent à un grand nombre des scénarios propres aux contrôles basés sur des commandes, tout en offrant un plus grand degré de flexibilité et de contrôle. De plus, les comportements peuvent également être utilisés pour associer des objets de commande ou des méthodes à des contrôles qui n’ont pas été spécifiquement conçus pour interagir avec des commandes.

Implémentation de commandes

Les modèles d’affichage exposent généralement des propriétés de commande, pour la liaison à partir de la vue, qui sont des instances d’objet qui implémentent l’interface ICommand . Un certain nombre de contrôles fournissent une Command propriété, qui peut être liée à un ICommand objet fourni par le modèle d’affichageXamarin.Forms. L’interface ICommand définit une méthode Execute, qui encapsule l’opération proprement dite, une méthode CanExecute, qui indique si la commande peut être appelée ainsi qu’un événement CanExecuteChanged, qui a lieu quand des changement se produisent et qu’ils impactent la décision d’exécuter ou non la commande. Les Command classes et Command<T> les classes fournies par Xamarin.Forms, implémentent l’interface ICommand , où T est le type des arguments vers Execute et CanExecute.

Dans un modèle d’affichage, il doit y avoir un objet de type Command ou Command<T> pour chaque propriété publique dans le modèle d’affichage de type ICommand. Le ou Command<T> le Command constructeur nécessite un Action objet de rappel appelé lorsque la ICommand.Execute méthode est appelée. La CanExecute méthode est un paramètre de constructeur facultatif et est un Func qui retourne un bool.

Le code suivant montre comment une Command instance, qui représente une commande register, est construite en spécifiant un délégué à la méthode de Register modèle d’affichage :

public ICommand RegisterCommand => new Command(Register);

La commande est exposée à la vue via une propriété qui retourne une référence à ICommand. Quand la méthode Execute est appelée sur l’objet Command, elle transfère simplement l’appel à la méthode dans le modèle de vue via le délégué spécifié dans le constructeur Command.

Une méthode asynchrone peut être appelée par une commande à l’aide des mots clés et await des async mots clés lors de la spécification du délégué de Execute la commande. Cela indique que le rappel est un Task et qu’il doit être attendu. Par exemple, le code suivant montre comment une Command instance, qui représente une commande de connexion, est construite en spécifiant un délégué à la méthode de SignInAsync modèle d’affichage :

public ICommand SignInCommand => new Command(async () => await SignInAsync());

Vous pouvez passer des paramètres aux actions Execute et CanExecute à l’aide de la classe Command<T> pour instancier la commande. Par exemple, le code suivant montre comment une Command<T> instance est utilisée pour indiquer que la NavigateAsync méthode nécessite un argument de type string:

public ICommand NavigateCommand => new Command<string>(NavigateAsync);

Dans les classes Command et Command<T>, le délégué de la méthode CanExecute dans chaque constructeur est facultatif. Si un délégué n’est pas spécifié, le Command retour est retourné true pour CanExecute. Toutefois, le modèle de vue peut indiquer un changement dans l’état CanExecute de la commande en appelant la méthode ChangeCanExecute sur l’objet Command. Cela entraîne le déclenchement de l’événement CanExecuteChanged. Tous les contrôles de l’interface utilisateur liés à la commande mettent ensuite à jour leur état activé pour refléter la disponibilité de la commande liée aux données.

Appel de commandes à partir d’un affichage

L’exemple de code suivant montre comment un Grid dans LoginView se lie à RegisterCommand dans la classe LoginViewModel à l’aide d’une instance de TapGestureRecognizer :

<Grid Grid.Column="1" HorizontalOptions="Center">  
    <Label Text="REGISTER" TextColor="Gray"/>  
    <Grid.GestureRecognizers>  
        <TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="1" />  
    </Grid.GestureRecognizers>  
</Grid>

Vous pouvez également définir éventuellement un paramètre de commande à l’aide de la propriété CommandParameter. Le type de l’argument attendu est spécifié dans les méthodes cibles Execute et CanExecute. Le TapGestureRecognizer appelle automatiquement la commande cible quand l’utilisateur interagit avec le contrôle attaché. Le paramètre de commande, s’il est fourni, est transmis en tant qu’argument au délégué de Execute la commande.

Implémentation de comportements

Les comportements permettent d’ajouter des fonctionnalités aux contrôles d’IU sans avoir à les sous-classer. En effet, vous implémentez les fonctionnalités dans une classe de comportement et les attachez au contrôle comme si elles en faisaient partie. Les comportements vous permettent d’implémenter du code que vous devrez normalement écrire en tant que code-behind, car il interagit directement avec l’API du contrôle, de telle sorte qu’il puisse être attaché de manière concise au contrôle et empaqueté pour la réutilisation sur plusieurs vues ou applications. Dans le contexte de MVVM, les comportements sont une approche utile pour connecter des contrôles aux commandes.

Un comportement attaché à un contrôle via des propriétés jointes est appelé comportement attaché. Le comportement peut ensuite utiliser l’API exposée de l’élément auquel il est attaché pour ajouter des fonctionnalités à ce contrôle, ou à d’autres contrôles, dans l’arborescence d’éléments visuels de la vue. L’application mobile eShopOnContainers contient la LineColorBehavior classe, qui est un comportement attaché. Pour plus d’informations sur ce comportement, consultez Affichage des erreurs de validation.

Un Xamarin.Forms comportement est une classe qui dérive de la ou Behavior<T> de la Behavior classe, où T est le type du contrôle auquel le comportement doit s’appliquer. Ces classes fournissent les méthodes OnAttachedTo et OnDetachingFrom, qui doivent être remplacées pour offrir une logique à exécuter quand le comportement est attaché et détaché des contrôles.

Dans l’application mobile eShopOnContainers, la BindableBehavior<T> classe dérive de la Behavior<T> classe. L’objectif de la BindableBehavior<T> classe est de fournir une classe de base pour Xamarin.Forms les comportements qui nécessitent que le BindingContext comportement soit défini sur le contrôle attaché.

La classe BindableBehavior<T> fournit une méthode OnAttachedTo substituable qui définit le BindingContext du comportement ainsi qu’une méthode OnDetachingFrom substituable, qui nettoie le BindingContext. De plus, la classe stocke une référence au contrôle attaché dans la propriété AssociatedObject.

L’application mobile eShopOnContainers inclut une EventToCommandBehavior classe, qui exécute une commande en réponse à un événement se produisant. Cette classe dérive de la classe BindableBehavior<T> pour que le comportement puisse se lier et exécuter un ICommand spécifié par une propriété Command quand le comportement est consommé. L’exemple de code suivant illustre la classe EventToCommandBehavior :

public class EventToCommandBehavior : BindableBehavior<View>  
{  
    ...  
    protected override void OnAttachedTo(View visualElement)  
    {  
        base.OnAttachedTo(visualElement);  

        var events = AssociatedObject.GetType().GetRuntimeEvents().ToArray();  
        if (events.Any())  
        {  
            _eventInfo = events.FirstOrDefault(e => e.Name == EventName);  
            if (_eventInfo == null)  
                throw new ArgumentException(string.Format(  
                        "EventToCommand: Can't find any event named '{0}' on attached type",   
                        EventName));  

            AddEventHandler(_eventInfo, AssociatedObject, OnFired);  
        }  
    }  

    protected override void OnDetachingFrom(View view)  
    {  
        if (_handler != null)  
            _eventInfo.RemoveEventHandler(AssociatedObject, _handler);  

        base.OnDetachingFrom(view);  
    }  

    private void AddEventHandler(  
            EventInfo eventInfo, object item, Action<object, EventArgs> action)  
    {  
        ...  
    }  

    private void OnFired(object sender, EventArgs eventArgs)  
    {  
        ...  
    }  
}

Les méthodes OnAttachedTo et OnDetachingFrom permettent d’inscrire et d’annuler l’inscription d’un gestionnaire d’événements pour l’événement défini dans la propriété EventName. Puis, quand l’événement se déclenche, la méthode OnFired est appelée, ce qui entraîne l’exécution de la commande.

L’utilisation de EventToCommandBehavior pour exécuter une commande quand un événement se déclenche présente l’avantage suivant : les commandes peuvent être associées à des contrôles qui n’ont pas été conçus pour interagir avec les commandes. De plus, cela permet de déplacer le code de gestion des événements vers les modèles de vue, où il peut faire l’objet d’un test unitaire.

Appel de comportements à partir d’une vue

Le EventToCommandBehavior est particulièrement utile pour attacher une commande à un contrôle qui ne prend pas en charge les commandes. Par exemple, l’utilisateur ProfileView utilise l’instruction EventToCommandBehavior pour exécuter le OrderDetailCommand moment où l’événement ItemTapped se déclenche sur celui ListView qui répertorie les commandes de l’utilisateur, comme indiqué dans le code suivant :

<ListView>  
    <ListView.Behaviors>  
        <behaviors:EventToCommandBehavior             
            EventName="ItemTapped"  
            Command="{Binding OrderDetailCommand}"  
            EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" />  
    </ListView.Behaviors>  
    ...  
</ListView>

Au moment de l’exécution, le EventToCommandBehavior répond à l’interaction avec le ListView. Lorsqu’un élément est sélectionné dans le ListView, l’événement ItemTapped se déclenche, ce qui exécute le ProfileViewModelOrderDetailCommand fichier . Par défaut, les arguments d’événement de l’événement sont passés à la commande. Ces données sont converties au fur et à mesure qu’elles sont passées entre la source et la cible par le convertisseur spécifié dans la EventArgsConverter propriété, qui retourne le Item résultat du ListView fichier .ItemTappedEventArgs Par conséquent, lorsque l’objet OrderDetailCommand est exécuté, l’élément sélectionné Order est transmis en tant que paramètre à l’action inscrite.

Pour plus d’informations sur les comportements, consultez Comportements.

Résumé

Le modèle Model-View-ViewModel (MVVM) permet de séparer correctement la logique métier et de présentation d’une application de son interface utilisateur. La maintenance d’une séparation propre entre la logique d’application et l’interface utilisateur permet de résoudre de nombreux problèmes de développement et peut faciliter le test, la maintenance et l’évolution d’une application. Il peut également améliorer considérablement les opportunités de réutilisation du code et permet aux développeurs et aux concepteurs d’interface utilisateur de collaborer plus facilement lors du développement de leurs parties respectives d’une application.

À l’aide du modèle MVVM, l’interface utilisateur de l’application et la logique métier sous-jacentes sont séparées en trois classes distinctes : la vue, qui encapsule l’interface utilisateur et la logique d’interface utilisateur ; le modèle d’affichage, qui encapsule la logique de présentation et l’état ; et le modèle, qui encapsule la logique métier et les données de l’application.