web-dev-qa-db-fra.com

Dans MVVM, le ViewModel ou le modèle doit-il implémenter INotifyPropertyChanged?

La plupart des exemples MVVM sur lesquels j'ai travaillé ont eu le Model implémente INotifyPropertyChanged, mais dans exemple CommandSink de Josh Smith ViewModel implémente INotifyPropertyChanged .

Je suis encore en train de rassembler cognitivement les concepts MVVM, donc je ne sais pas si:

  • vous devez mettre INotifyPropertyChanged dans le ViewModel pour que CommandSink fonctionne
  • c'est juste une aberration de la norme et ça n'a pas vraiment d'importance
  • vous devriez toujours avoir le modèle implémenter INotifyPropertyChanged et c'est juste une erreur qui serait corrigée si cela était développé à partir d'un exemple de code vers une application

Quelles ont été les expériences des autres sur les projets MVVM sur lesquels vous avez travaillé?

155
Edward Tanguay

Je dirais plutôt le contraire, je mets toujours mon INotifyPropertyChanged sur mon ViewModel - vous ne voulez vraiment pas polluer votre modèle avec une fonctionnalité assez spécifique à WPF comme INotifyPropertyChanged, ce truc devrait asseyez-vous dans le ViewModel.

Je suis sûr que d'autres ne seraient pas d'accord, mais c'est ainsi que je travaille.

100
Steven Robbins

Je suis fortement en désaccord avec le concept selon lequel le modèle ne devrait pas implémenter le INotifyPropertyChanged. Cette interface n'est pas spécifique à l'interface utilisateur! Il informe simplement d'un changement. En effet, WPF l'utilise fortement pour identifier les changements, mais cela ne signifie pas qu'il s'agit d'une interface utilisateur. Je le comparerais au commentaire suivant: " Un pneu est un accessoire de voiture ". Bien sûr, mais les vélos, les bus, etc. l'utilisent également. En résumé, ne prenez pas cette interface comme une chose d'interface utilisateur.

Cela dit, cela ne signifie pas nécessairement que je pense que le modèle devrait fournir des notifications. En fait, en règle générale, le modèle ne doit pas implémenter cette interface, sauf si cela est nécessaire. Dans la plupart des cas où aucune donnée de serveur n'est envoyée à l'application cliente, le modèle peut être périmé. Mais si j'écoute les données des marchés financiers, je ne vois pas pourquoi le modèle ne peut pas implémenter l'interface. Par exemple, que se passe-t-il si j'ai une logique non UI telle qu'un service qui, lorsqu'il reçoit un prix Bid ou Ask pour une valeur donnée, émet une alerte (par exemple via un e-mail) ou passe une commande? Cela pourrait être une solution propre possible.

Cependant, il existe différentes façons de réaliser les choses, mais je plaiderais toujours en faveur de la simplicité et éviter la redondance.

Qu'est-ce qui est mieux? Définir des événements sur une collection ou des changements de propriétés sur le modèle de vue et les propager au modèle ou faire en sorte que la vue mette à jour intrinsèquement le modèle (via le modèle de vue)?

En fin de compte, chaque fois que vous voyez quelqu'un affirmer que " vous ne pouvez pas faire ceci ou cela " c'est un signe dont ils ne savent pas de quoi ils parlent .

Cela dépend vraiment de votre cas et en fait MVVM est un framework avec beaucoup de problèmes et je n'ai pas encore vu une implémentation commune de MVVM à tous les niveaux.

J'aimerais avoir plus de temps pour expliquer les nombreuses versions de MVVM et quelques solutions aux problèmes courants - principalement fournies par d'autres développeurs, mais je suppose que je devrai le faire une autre fois.

134
Paulo Sousa

Dans M-V-VM, le ViewModel implémente toujours (modèle pas toujours) INotifyPropertyChanged

Consultez le modèle/boîte à outils du projet MV-VM à partir de http://blogs.msdn.com/llobo/archive/2009/05/01/download-mv-vm-project-template-toolkit.aspx =. Il utilise le DelegateCommand pour commander et ce devrait être un excellent modèle de démarrage pour vos projets M-V-VM.

28
Soni Ali

Je pense que MVVM est très mal nommé et appeler le ViewModel un ViewModel fait que beaucoup manquent une caractéristique importante d'une architecture bien conçue, qui est un DataController qui contrôle les données, peu importe qui essaie de les toucher.

Si vous considérez le View-Model comme plus un DataController et implémentez une architecture où votre DataController est le seul élément qui touche les données, alors vous ne toucherez jamais directement aux données, mais utilisez toujours le DataController. Le DataController est utile pour l'interface utilisateur, mais pas nécessairement uniquement pour l'interface utilisateur. C'est pour la couche métier, la couche UI, etc ...

DataModel -------- DataController ------ View
                  /
Business --------/

Vous vous retrouvez avec un modèle comme celui-ci. Même l'entreprise ne devrait toucher les données qu'à l'aide du ViewModel. Ensuite, votre énigme disparaît.

11
Rhyous

Cela dépend de la façon dont vous avez implémenté votre modèle. Mon entreprise utilise des objets métier similaires aux objets CSLA de Lhotka et utilise largement INotifyPropertyChanged dans tout le modèle commercial.

Notre moteur de validation repose fortement sur la notification des changements de propriétés via ce mécanisme et il fonctionne très bien. De toute évidence, si vous utilisez une implémentation différente des objets métier où la notification des modifications n'est pas aussi critique pour l'opération, vous pouvez avoir d'autres méthodes pour détecter les changements dans votre modèle d'entreprise.

Nous avons également des modèles de vue qui propagent les modifications du modèle lorsque cela est nécessaire, mais les modèles de vue eux-mêmes écoutent les modifications du modèle sous-jacent.

8
Steve Mitcham

Je suis d'accord avec la réponse de Paulo, l'implémentation de INotifyPropertyChanged dans les modèles est totalement acceptable et est même suggérée par Microsoft -

En règle générale, le modèle implémente les fonctionnalités qui facilitent la liaison à la vue. Cela signifie généralement qu'il prend en charge les notifications de modification de propriété et de collection via les interfaces INotifyPropertyChanged et INotifyCollectionChanged. Les classes de modèles qui représentent des collections d'objets dérivent généralement de ObservableCollection<T> class, qui fournit une implémentation de l'interface INotifyCollectionChanged.

Bien que ce soit à vous de décider si vous voulez ou non ce type de mise en œuvre, mais n'oubliez pas -

Et si vos classes de modèles n'implémentent pas les interfaces requises?

Parfois, vous devrez travailler avec des objets de modèle qui n'implémentent pas les interfaces INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfo ou INotifyDataErrorInfo. Dans ces cas, le modèle de vue peut avoir besoin d'envelopper les objets du modèle et d'exposer les propriétés requises à la vue. Les valeurs de ces propriétés seront fournies directement par les objets du modèle. Le modèle de vue implémentera les interfaces requises pour les propriétés qu'il expose afin que la vue puisse facilement se lier aux données.

Extrait de - http://msdn.Microsoft.com/en-us/library/gg405484 (PandP.40) .aspx

J'ai travaillé sur certains projets où nous n'avons pas implémenté INotifyPropertyChanged dans nos modèles et à cause de cela nous avons rencontré beaucoup de problèmes; une duplication inutile des propriétés était nécessaire dans VM et en même temps, nous devions mettre à jour l'objet sous-jacent (avec les valeurs mises à jour) avant de les transmettre à BL/DL.

Vous rencontrerez des problèmes spécialement si vous devez travailler avec la collection de vos objets de modèle (par exemple dans une grille ou une liste modifiable) ou des modèles complexes; les objets du modèle ne seront pas mis à jour automatiquement et vous devrez gérer tout cela dans votre machine virtuelle.

4
akjoshi

Mais parfois (comme dans cette présentation texte du lien ) le modèle est le service, qui fournit à l'application certaines données en ligne, puis vous devez avertir que de nouvelles données sont arrivées ou que les données ont changé à l'aide d'événements ...

3
Andrey Khataev

Je pense que la réponse est assez claire si vous souhaitez adhérer à la MV-VM.

voir: http://msdn.Microsoft.com/en-us/library/gg405484 (v = PandP.40) .aspx

Dans le modèle MVVM, la vue encapsule l'interface utilisateur et toute logique d'interface utilisateur, le modèle de vue encapsule la logique et l'état de présentation, et le modèle encapsule la logique métier et les données.

"La vue interagit avec le modèle de vue via la liaison de données, les commandes et les événements de notification de modification. Le modèle de vue interroge, observe et coordonne les mises à jour du modèle, convertissant, validant et agrégeant les données nécessaires pour l'affichage dans la vue."

3
John D

Je dirais dans votre ViewModel. Il ne fait pas partie du modèle car le modèle est indépendant de l'interface utilisateur. Le modèle devrait être "tout ce qui n'est pas lié aux affaires"

2
Steve Dunn

Utilisez simplement le INotifyPropertyChange dans votre modèle de vue et non dans le modèle,

le modèle utilise généralement le IDataErrorInfo pour gérer les erreurs de validation, alors restez dans votre ViewModel et vous avez raison sur votre route MVVM.

1
Adam

Je pense que tout dépend du cas d'utilisation.

Lorsque vous avez un modèle simple avec de nombreuses propriétés, vous pouvez le faire implémenter INPC. Par simple je veux dire que ce modèle ressemble plutôt à un POCO .

Si votre modèle est plus complexe et vit dans un domaine de modèle interactif - modèles référençant des modèles, abonnement aux événements d'autres modèles - avoir des événements de modèle implémentés comme INPC est un cauchemar.

Mettez-vous dans la position d'une entité modèle qui doit collaborer avec d'autres modèles. Vous avez différents événements auxquels vous abonner. Tous sont mis en œuvre comme INPC. Imaginez ces gestionnaires d'événements que vous avez. Une énorme cascade de clauses if et/ou de commutateurs.

Un autre problème avec INPC. Vous devez concevoir vos applications pour qu'elles reposent sur l'abstraction et non sur la mise en œuvre. Cela se fait généralement à l'aide d'interfaces.

Jetons un œil à 2 implémentations différentes de la même abstraction:

public class ConnectionStateChangedEventArgs : EventArgs
{
    public bool IsConnected {get;set;}
}

interface IConnectionManagerINPC : INotifyPropertyChanged
{
    string Name {get;}
    int ConnectionsLimit {get;}
    /*

    A few more properties

    */
    bool IsConnected {get;}
}

interface IConnectionManager
{
    string Name {get;}
    int ConnectionsLimit {get;}
    /*

    A few more properties

    */
    event EventHandler<ConnectionStateChangedEventArgs> ConnectionStateChanged;
    bool IsConnected {get;}
}

Maintenant, regardez-les tous les deux. Que vous dit IConnectionManagerINPC? Que certaines de ses propriétés peuvent changer. Vous ne savez pas lequel d'entre eux. En fait, la conception est que seuls les changements IsConnected, car les autres sont en lecture seule.

Au contraire, les intentions de IConnectionManager sont claires: "Je peux vous dire que la valeur de ma propriété IsConnected peut avoir changé".

1
dzendras

L'implémentation d'INPC dans les modèles peut être utilisée si les modèles sont clairement exposés dans le ViewModel. Mais généralement, le ViewModel enveloppe les modèles est ses propres classes pour réduire la complexité du modèle (qui ne devrait pas être utile pour la liaison). Dans ce cas, l'INPC doit être implémenté dans le ViewModel.

0

Toutes les propriétés, qui sont liées à ma vue, se trouvent dans mes ViewModel (s). Ils doivent donc implémenter l'interface INotifyPropertyChanged. Par conséquent, la vue obtient toutes les modifications.

[À l'aide de la boîte à outils MVVM Light, je les ai laissés hériter de ViewModelBase.]

Le modèle contient la logique métier, mais n'a rien à voir avec la vue. Il n'y a donc pas besoin de l'interface INotifyPropertyChanged.

0
donotbefake

Normalement, ViewModel implémentera le INotifyPropertyChanged. Le modèle peut être n'importe quoi (fichier xml, base de données ou même objet). Le modèle est utilisé pour donner les données au modèle de vue, qui se propage à la vue.

voir ici

0
Syed

à mon humble avis, le viewmodel implémente INotifyPropertyChange et le modèle pourrait utiliser la notification à un "niveau" différent.

par exemple, avec un service de document et un objet de document, vous avez un événement documentChanged qu'un viewmodel écoute pour effacer et reconstruire la vue. Dans le modèle de vue d'édition, vous avez un changement de propriété pour les propriétés du document afin de prendre en charge les vues. Si le service fait beaucoup avec le document lors de la sauvegarde (mise à jour de la date de modification, dernier utilisateur, etc.), vous obtenez facilement une surcharge d'événements Ipropertychanged et un simple documentchanged suffit.

Mais si vous utilisez INotifyPropertyChange dans votre modèle, je pense que c'est une bonne pratique de le relayer dans votre modèle de vue au lieu de vous y abonner directement dans votre vue. Dans ce cas, lorsque les événements changent dans votre modèle, il vous suffit de modifier le modèle de vue et la vue reste intacte.

0
Bram

J'utilise l'interface INotifyPropertyChange dans un modèle. En fait, un changement de propriété de modèle doit être déclenché par l'interface utilisateur ou le client externe uniquement.

J'ai remarqué plusieurs avantages et inconvénients:

Avantages

Notifier est dans le modèle commercial

  1. Selon le domaine, c'est juste. Il devrait décider quand relancer et quand ne pas le faire.

Inconvénients

Le modèle a des propriétés (quantité, taux, commission, prix total). Le montant total est calculé en utilisant la quantité, le taux et le changement de commission.

  1. Lors du chargement des valeurs à partir de db, le calcul de la valeur totale de la charge est appelé 3 fois (quantité, taux, commission). Ça devrait être une fois.

  2. Si le taux, la quantité est attribué dans la couche de gestion, le notificateur est à nouveau appelé.

  3. Il devrait y avoir une option pour désactiver cela, éventuellement dans la classe de base. Cependant, les développeurs pourraient oublier de le faire.

0
Anand Kumar

Supposons que la référence de l'objet dans votre vue change. Comment notifierez-vous toutes les propriétés à mettre à jour afin d'afficher les valeurs correctes? Appeler OnPropertyChanged dans votre vue pour toutes les propriétés de l'objet est un déchet à mon point de vue.

Donc, ce que je fais, c'est de laisser l'objet lui-même informer n'importe qui quand une valeur dans une propriété change, et à mon avis j'utilise des liaisons comme Object.Property1, Object.Property2 Et ainsi de suite. De cette façon, si je veux juste changer l'objet qui est actuellement maintenu dans ma vue, je fais juste OnPropertyChanged("Object").

Pour éviter des centaines de notifications lors du chargement des objets, j'ai un indicateur booléen privé que je mets à true pendant le chargement qui est vérifié à partir de OnPropertyChanged de l'objet et ne fait rien.

0
Dummy01