2009-08-26 9 views
36

Nous venons d'entrer dans MVVM dans WPF.MVVM ViewModel doit-il effectuer une conversion/validation de type?

Nous avons implémenté nos ViewModels avec des propriétés 'fortement typées' (int, double? Etc.) auxquelles nous nous lions dans la vue.

La conversion de type fonctionne normalement, et la saisie de données est assez simple. Mais nous rencontrons des problèmes de validation. Si, par exemple, une valeur non numérique est entrée dans une zone de texte liée à une propriété numérique, la conversion échoue, la propriété n'est jamais définie et nous n'avons jamais la possibilité de fournir une rétroaction adéquate à l'utilisateur. Pire encore, la propriété conserve sa valeur actuelle, ce qui entraîne une discordance entre ce qui est affiché dans la vue et ce qui est réellement dans le ViewModel.

Tout cela peut être manipulé avec des convertisseurs de valeur, je sais. Mais j'ai vu plusieurs opinions à l'effet que la conversion ne devrait pas être la responsabilité du point de vue du tout. Ce qui est entré dans la vue sont des chaînes, et la conversion, la validation, etc. devraient être la responsabilité de ViewModel (donc l'argument va). Si tel est le cas, nous devrions réécrire la plupart des propriétés de nos ViewModels en chaîne, et fournir des informations d'erreur via l'interface IErrorInfo, par exemple. Il ferait sûrement pour XAML plus simple, plus léger dans la vue. D'un autre côté, la conversion, la validation, etc. seront moins déclaratives, explicites et flexibles, du point de vue du View Designer. Il nous semble que ces deux approches sont fondamentalement différentes, alors avant de nous décider, nous aimerions avoir des avis éclairés sur le SO. Donc: est-ce que ViewModels devrait exposer une interface simplifiée, basée sur le texte, à la vue et gérer la conversion en interne? Ou est-ce que les propriétés de ViewModel devraient exposer les types de données réels, laissant ces tâches à la vue?

Mise à jour:

difficile de choisir un gagnant, mais j'ai finalement atterri sur un d'entre vous qui se termine plus ou moins comme moi. En particulier, nous avons décidé de conserver les propriétés de ViewModel en les tapant dans le champ. La raison principale de ceci est la flexibilité qu'il nous offre dans la conception de la vue, et la puissance de conversion/formatage explicite et déclarative en XAML.

J'ai noté une hypothèse avec vous qui ne sera pas d'accord avec nous à ce sujet, que la conception de la vue est fixe et prêt. Par conséquent, aucune décision concernant la conversion, le formatage, etc. ne doit être prise dans la vue. Mais le nôtre est un processus agile, et nous n'avons pas compris tous les détails de l'interface utilisateur. En fait, laisser les détails de l'interface utilisateur en cours d'élaboration laisse place à la créativité et, à mon avis, même un design méticuleusement élaboré finira toujours par se transformer tout au long du processus de mise en œuvre. Le but de tout cela est que, bien que l'application des règles métier appartienne certainement au ViewModel, il nous semble que la simple conversion et le formatage sont une vue-chose. Cela peut sembler hérétique, mais je ne pense pas que la conversion de type dans la vue nécessite des tests unitaires du tout (si longtemps nous testons les convertisseurs de type réels).

Dans l'ensemble, une excellente discussion, les gens, avec des opinions bien formulées et bien informées. Merci.

+2

Était sur le point de poser cette question. +1 – Gishu

Répondre

13

C'est une question très intéressante et une que je ne crois pas avoir une réponse définitive, mais je ferai de mon mieux pour lancer mes pensées. Si l'on regarde le modèle MVVM tel que je le comprends, le point de ViewModel est d'exposer les données de manière à ce que View puisse comprendre sans aucune hypothèse sur la façon dont la vue va l'utiliser. Pour un exemple Feignons que nous modélisons la vitesse d'une voiture:

public class CarModel 
{ 
    public int MilesPerHour { get; set; } 
} 

public class CarViewModel 
{ 
    private CarModel _model; 

    public int MilesPerHour 
    { 
     get { return _model.MilesPerHour; } 
     set { _model.MilesPerHour = value; } 
    } 
} 

Dans l'exemple ci-dessus, je l'ai exposé la propriété comme un entier, puisque c'est ce qu'il est dans le modèle. Les inconvénients de ce que vous avez énumérés dans votre question, mais le principal avantage est qu'il donne au créateur de la vue une précieuse information sur la façon d'afficher ces données. Rappelez-vous que nous (en tant qu'auteurs de ViewModel) ne savons pas à quoi ressemble la vue. En validant l'idée que les données sont int, View peut utiliser une zone de texte ou un autre contrôle qui n'accepte que des nombres (un cadran, par exemple) pour afficher les informations. Si nous disons que nous allons formater les données d'une manière que nous considérons comme est utile à la vue, il prend cette puissance importante loin de lui.

D'autre part, nous travaillons dans le monde réel. Nous avons tendance à savoir quelle est la vue. Il est rare que vous branchiez et jouiez des vues différentes sur le même ViewModel et que l'ajout du code de conversion dans le ViewModel soit plus simple. Je ne pense pas que ce soit correct, mais cela ne signifie pas que vous ne trouverez pas mon code de production en l'utilisant ...

Enfin (et je suis sûr que vous le savez, mais pour des fins de complétions ... La logique métier doit être dans le ViewModel. Si nous décidons que la voiture ne devrait pas dépasser 70 mph alors ce n'est pas la responsabilité de l'avis de faire respecter cela. Donc, vous finirez toujours avec une sorte de fournisseur d'erreur, mais à un niveau d'entreprise plutôt que d'affichage.


Ok, peut-être que n'a finalement pas été ....

Je voulais répondre aux observations faites par Kent, et mes pensées ne rentre pas dans un commentaire. Évidemment, la principale différence entre mon point de vue et celui de Kent (tel que je le comprends) est qu'il lit ViewModel comme un modèle de la vue et je le lis comme étant ce qui expose le modèle à la vue. Une subtile différence que j'admets, mais je pense que le résultat est que je ne veux pas supprimer les informations que le modèle fournit, même si cela facilite la vue spécifique que j'utilise.Mon point de vue est basé sur l'hypothèse que vous devriez pouvoir échanger des vues, elles devraient être des choses éphémères qui peuvent changer en fonction des exigences de taille d'écran, de matériel, de plate-forme, de latence et d'environnement. La tournure intéressante est que j'ai jamais réellement besoin de cette fonctionnalité, ni rien vu (au-delà de la preuve d'applications concept) qui ont jamais utilisé, mais si nous acceptons que nous ne l'utiliserons pas maintenant ou à tout moment dans l'avenir , et que chaque ViewModel fonctionnera avec un, et seulement un, View, alors nous pouvons aussi bien mettre tout le code dans le fichier code-behind et jeter le ViewModel complètement - après tout, il est si étroitement couplé qu'il peut ainsi soit la même classe.

Idéalement, je voudrais une situation où le ViewModel peut dire "cette valeur est un int, ce sera toujours un int, et vous pouvez l'afficher de toute façon que vous aimez.Mais vous pouvez me donner quelque chose et je Je ferai de mon mieux pour l'adapter, et si je ne peux pas, je vous le ferai savoir. Fondamentalement, ma propriété MilesPerHour devrait avoir un getter int, mais un setter d'objet. De cette façon, les vues conservent toutes les informations dont elles ont besoin, mais ne vous inquiétez pas des conversions ou de la validation.

+1

Très bien placé, Martin. Merci. –

+0

J'espère que vous savez à quoi ressemble la vue.Sinon, il y a quelque chose de sérieusement rompu avec votre collaboration entre les développeurs et les concepteurs. Assumer la responsabilité de certains actifs n'implique pas que nous opérons dans un vide - nous devons toujours comprendre ce que l'autre fait. –

+0

Je ne sais pas ce que la vue ressemble si je travaille dans une application qui peut être skinned par des tiers. Mais même en dehors de ce scénario, je faisais référence au modèle MVVM qui implique que, dans un monde idéal, le ViewModel ne devrait rien savoir de la vue (même s'il s'agit de WPF, de Webforms ou d'autre chose). –

5

C'est une bonne question, et je peux certainement voir les deux côtés de la discussion.

Ma pensée est que ce que vous cherchez vraiment est un bon NumericInputControl que vous pouvez utiliser dans votre xaml. Cela offrira une meilleure expérience utilisateur car vos utilisateurs ne pourront pas entrer accidentellement du texte dans un champ numérique et, comme le contrôle contraint la saisie sans la valider, vous pouvez conserver le ViewModel plus fortement typé. Je ne suis pas sûr de la façon dont vous voulez mettre en œuvre un, je sais que les commandes classiques de spinner/NumericUpDown tombent en disgrâce parce qu'elles ne sont pas tactiles, mais je ne crois pas que l'introduction d'un tel contrôle viole la pureté de l'approche de conception ou de vos ViewModels. Vous recevrez un numéro que vous pourrez ensuite valider à l'endroit approprié, fournir des commentaires via IDataErrorInfo comme d'habitude, et ainsi de suite. :) Cette technique vous permet d'obtenir le meilleur des deux mondes sans aucun inconvénient réel (sauf la création d'un contrôle numérique).

+1

Hm. Vous proposez une division du travail - la vue est convertie vers et à partir du type correct, et thew viewmodel valide les données converties. Cela a du sens. –

8

appartient absolument dans le modèle de vue, pour toutes les raisons habituelles, y compris:

  • concepteurs possèdent le XAML. Voulez-vous que les concepteurs aient à comprendre et à implémenter la logique de conversion et de validation de type requise?
  • Testabilité. Ne voulez-vous pas valider que votre logique de conversion et de validation fonctionne correctement? C'est beaucoup plus difficile si elle est intégrée dans la vue.

D'autre part, la conversion, validation, etc. seront moins déclarative, explicite et flexible, du point de vue de la vue concepteur

Je pense que ce point est sans objet, car le concepteur de vue devrait être responsable de ces choses. Le concepteur essaie de faire en sorte que l'interface utilisateur ait une certaine apparence; c'est le développeur qui met en œuvre la logique métier, y compris la logique de conversion et de validation.

5

Le MVVM ViewModel doit-il effectuer la conversion/validation de type ?

Oui.

Le modèle d'affichage est une couche d'abstraction entre la vue et le modèle - l'endroit idéal pour effectuer des conversions de types (à la place de convertisseurs de valeur lourdes). La validation doit absolument se produire dans le cadre du modèle de vue.

Nous utilisons notre modèle Voir pour gérer les conversions autant que possible pour les types de données. Cela réduit le besoin d'un convertisseur de valeur dans certaines circonstances très spécifiques. Vous voulez exposer quel type est le plus facile à utiliser pour la vue. Cela a bien fonctionné.

La seule question spécifique que vous soulevé:

Si, par exemple, une valeur non numérique est entré dans une zone de texte lié à une propriété numérique , la conversion échoue, la propriété est jamais définie , et nous n'avons jamais une chance de fournir rétroaction appropriée à l'utilisateur. Pire, la propriété conserve sa valeur actuelle , conduisant à une discordance entre ce qui est affiché dans la vue et ce qui est réellement dans le ViewModel.

peut être manipulé en exposant votre point de vue type de modèle en tant que type Nullable . Cela devrait permettre à la source sous-jacente d'être mise à jour, même si des données invalides sont entrées, et déclencher la validation. Cela a fonctionné dans une situation similaire à celle que nous avions avec DateTime et un sélecteur de date et d'heure.

Conserver la vue comme stupide. Nous n'avons pas les concepteurs officiels, nos développeurs sont nos concepteurs, donc garder la vue stupide a des avantages:

  • Nous (les développeurs) peuvent garder notre santé mentale (XAML est un peu moins bavard)
  • logique métier (y compris la validation) reste dans le modèle de vue et peut permettre le test

Bonne chance!

-Z

0

Ou devraient propriétés ViewModel exposer les types de données réelles, laissant ces tâches à la vue de gérer?

  1. Conversion et modèles se font en View, car les deux sont juste une conversion de values, models et viewmodels dans controls! Controls sont disponibles uniquement à l'intérieur View.

  2. Validation se fait en ViewModel, car la validation se fait selon les règles métier et peut même se faire par un appel à un service distant. View ne connaît rien aux règles métier, mais sait comment présenter les résultats de la validation.

Si, par exemple, une valeur non numérique est entré dans une zone de texte lié à une propriété numérique

Un bien conçu contrôle de zone de texte numérique ne permet jamais à l'utilisateur d'entrer un non valeur numérique.

Questions connexes