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


165

La plupart des exemples MVVM sur lesquels j'ai travaillé ont eu l' implémentation ModelINotifyPropertyChanged , mais dans l'exemple CommandSink de Josh Smith, ViewModel implémenteINotifyPropertyChanged .

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

  • Vous devez mettre le INotifyPropertyChangeddans le ViewModel pour vous mettre CommandSinkau travail
  • Ce n'est qu'une aberration de la norme et cela n'a pas vraiment d'importance
  • Vous devriez toujours avoir le modèle implémenté INotifyPropertyChangedet ce n'est qu'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é?


4
si vous implémentez INPC, essayez github.com/Fody/PropertyChanged - cela vous évitera des semaines de saisie.
Bloke CAD

Réponses:


104

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

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


84
Que faites-vous si une propriété change dans le modèle? Vous devez l'obtenir d'une manière ou d'une autre dans le modèle de vue. Question honnête, je suis confronté à cette énigme en ce moment.
Roger Lipscombe

4
EventAggregator dans le code Prism est une bonne alternative à INotifyPropertyChanged sur le modèle, avec une propriété personnalisée modifiée de type d'événement. Le code d'événement de ce projet prend en charge le transfert d'événements entre les threads d'arrière-plan et d'interface utilisateur, ce qui peut parfois être un problème.
Steve Mitcham

51
INotifyProperyChanged n'est pas spécifique à WPF, il vit dans l'espace de noms System.ComponentModel, je l'ai utilisé dans les applications WinForms, aussi INotifyPropertyChanged est dans .Net depuis 2.0, WPF n'existe que depuis 3.0
benPearce

40
Je suis fan de mettre INotifyPropertyChanged dans le MODEL et le VIEWMODEL. Je ne peux pas penser à une raison de ne pas faire ça. C'est une manière élégante d'informer le VIEWMODEL du moment où des changements d'arrière-plan se sont produits dans votre MODE qui affectent le VIEWMODEL, tout comme il est utilisé pour informer le VIEW et qu'il y a eu des changements dans le VIEWMODEL.
ScottCher

6
@Steve - pour informer le ViewModel que la propriété d'un Model a changé, il semble que INotifyPropertyChanged fonctionne très bien comme "un événement auquel le viewmodel peut s'accrocher". Pourquoi ne pas l'utiliser?
skybluecodeflier

146

Je suis totalement en désaccord avec le concept selon lequel le modèle ne devrait pas mettre en œuvre 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 considérez pas cette interface comme une 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 transmise à l'application cliente, le modèle peut être obsolète. Mais si j'écoute les données du marché financier, alors je ne vois pas pourquoi le modèle ne peut pas implémenter l'interface. À titre d'exemple, que se passe-t-il si j'ai une logique non-UI telle qu'un service qui, lorsqu'il reçoit une offre ou une demande de prix 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 manières de réaliser les choses, mais je plaiderais toujours en faveur de la simplicité et éviterais la redondance.

Qu'est-ce qui est mieux? Définir des événements sur une collection ou des modifications de propriété sur le modèle de vue et les propager au modèle ou demander à la vue de mettre à 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 qu'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'aurais aimé avoir plus de temps pour expliquer les nombreuses saveurs de MVVM et quelques solutions aux problèmes courants - principalement fournis par d'autres développeurs, mais je suppose que je devrai le faire une autre fois.


7
Pense-y de cette façon. Si vous, en tant que développeur, consommez un .dll contenant des modèles, vous ne les réécririez certainement pas pour prendre en charge INotifyPropertyChanged.
Lee Treveil

2
Tout à fait d'accord avec vous. Comme moi, vous serez peut-être également ravi de découvrir que la documentation MVVM officielle < msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx > (section Modèle) est d'accord avec nous. :-)
Noldorin le

"Cependant, il existe différentes manières de réaliser les choses, mais je plaiderais toujours en faveur de la simplicité et éviterait la redondance." Très important.
Bastien Vandamme

1
INotifyPropertyChangedfait partie de l' System.ComponentModelespace de noms qui est pour "le comportement d'exécution et de conception des composants et des contrôles ". NE PAS UTILISER INotifyPropertyChanged dans les modèles, uniquement dans ViewModels. Lien vers la documentation: docs.microsoft.com/en-us/dotnet/api/system.componentmodel
Igor Popov

1
Ancien post, je sais, mais j'y reviens souvent lors du démarrage d'un nouveau projet en utilisant MVVM. J'ai récemment commencé à appliquer le principe de responsabilité unique de manière beaucoup plus stricte. Un modèle, c'est avoir une responsabilité. Être mannequin. Dès que vous ajoutez INotifyPropertyChanged au modèle, il ne suit plus le principe de responsabilité unique. La raison pour laquelle le ViewModel existe est de laisser le modèle être le modèle, de laisser le modèle avoir une seule responsabilité.
Rhyous


13

Je pense que MVVM est très mal nommé et appeler le ViewModel un ViewModel fait manquer à beaucoup 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 le toucher.

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

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

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


3
C'est génial si vos données ne changent que lorsque le DataController les modifie. Si les données proviennent d'une base de données ou d'une autre banque de données qui peut fournir une autre voie de changement, vous devrez peut-être avoir un moyen d'informer le VIEWMODEL (DataController dans votre modèle) et VIEW lorsque cela se produit. Vous pouvez interroger à l'aide du DataController ou pousser depuis un processus externe vers votre DataModel et autoriser votre DataModel à envoyer des notifications de modification à votre DataController.
ScottCher

4
Vous avez tout à fait raison. Les modèles de conception sont de très haut niveau. La plupart du temps, le modèle de conception vous amène à bien faire les choses, mais de temps en temps, ils vous tournent dans le mauvais sens. Vous ne devriez jamais éviter de faire quelque chose de bien parce que cela est en dehors de votre modèle de conception.
Rhyous

Vous devez également pousser vers votre DataController car il contrôle et le modèle de données et lui diriez de se mettre à jour.
Rhyous

En outre, le modèle dans MVVM doit rester spécifique selon les besoins de l'interface utilisateur (par exemple, DTO). Ainsi, toute base de données ou logique métier complexe doit se produire dans une couche différente, puis des données brutes doivent être fournies via le modèle de vue.
Nom de code Jack

9

Cela dépend de la manière dont vous avez implémenté votre modèle. Mon entreprise utilise des objets métier similaires aux objets CSLA de Lhotka et les utilise largement INotifyPropertyChangeddans tout le modèle commercial.

Notre moteur de validation dépend fortement de la notification que les propriétés changent grâce à ce mécanisme et cela fonctionne très bien. Évidemment, si vous utilisez une implémentation différente autre que 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 modifications dans votre modèle métier.

Nous avons également des modèles de vue qui propagent les modifications à partir du modèle si nécessaire, mais les modèles de vue eux-mêmes écoutent les modifications de modèle sous-jacentes.


3
Comment propagez-vous exactement OnPropertyChanged de Model à OnPropertyChanged de ViewModel? J'ai un problème lorsque ViewModel a des noms de propriété différents de ceux de Model - une sorte de mappage de nom à nom serait alors nécessaire, non?
Martin Konicek

Ce n'est pas quelque chose de vraiment sophistiqué, nous transmettons simplement les événements. Je suppose que si les noms étaient différents, une table de consultation pourrait être utilisée. Si le changement n'était pas un mappage un-à-un, vous pouvez simplement accrocher l'événement, puis déclencher les événements nécessaires dans le gestionnaire.
Steve Mitcham

6

Je suis d'accord avec la réponse de Paulo, la mise INotifyPropertyChangeden œuvre dans Models 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 la notification de modification de propriété et de collection via les interfaces INotifyPropertyChangedet INotifyCollectionChanged. Les classes de modèles qui représentent des collections d'objets dérivent généralement de la ObservableCollection<T>classe, qui fournit une implémentation de l' INotifyCollectionChangedinterface.

Bien que ce soit à vous de décider si vous voulez ou non ce type d'implémentation, mais rappelez-vous -

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

Parfois , vous aurez besoin de travailler avec des objets modèles qui ne mettent pas en œuvre les INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfoou INotifyDataErrorInfointerfaces. Dans ces cas, le modèle de vue peut avoir besoin d'encapsuler 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.

Tiré de - http://msdn.microsoft.com/en-us/library/gg405484(PandP.40).aspx

J'ai travaillé dans certains projets que nous n'avons pas mis INotifyPropertyChangeden œuvre dans nos modèles et à cause de cela, nous avons été confrontés à de nombreux 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 des 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 de modèle ne seront pas mis à jour automatiquement et vous devrez gérer tout cela dans votre VM.


3

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


3

Je pense que la réponse est assez claire si vous souhaitez adhérer au 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 des liaisons de données, des commandes et des é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 selon les besoins pour les afficher dans la vue."


4
La citation est sujette à interprétation. Je pense que vous devriez ajouter votre interprétation, pour que votre réponse soit claire :-)
Søren Boisen

@John D: Cet article ne donne qu'une interprétation de MVVM et un moyen de l'implémenter, il ne définit pas MVVM.
akjoshi

De plus, si vous lisez l'article complet, il définit la classe Model comme ceci: "En général, le modèle implémente les fonctionnalités qui facilitent la liaison à la vue. Cela signifie généralement qu'il prend en charge la notification 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 la classe ObservableCollection <T>, qui fournit une implémentation de l'interface INotifyCollectionChanged. "
akjoshi

2

Je dirais dans votre ViewModel. Cela ne fait pas partie du modèle car le modèle est indépendant de l'interface utilisateur. Le modèle devrait être `` tout sauf indépendant des affaires ''


2

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 encapsule les modèles dans ses propres classes pour réduire la complexité du modèle (ce qui ne devrait pas être utile pour la liaison). Dans ce cas, l'INPC doit être implémenté dans le ViewModel.


1

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

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

Avantages

Notifier fait partie du modèle commercial

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

Désavantages

Le modèle a des propriétés (quantité, taux, commission, longueur totale). Totalfrieght 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 frieght totale 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.


En raison de tous les inconvénients que vous avez énumérés, nous venons de décider qu'il s'agissait d'une mauvaise décision dans notre projet WPF relativement important d'implémenter INPC dans les modèles. Ils ne doivent traiter que de la couche de persistance. Toutes les autres choses telles que la validation, la notification de modification et les propriétés calculées doivent être gérées dans ViewModel. Maintenant, je comprends clairement que la répétition des propriétés du modèle dans ViewModel n'est PAS toujours une violation du principe DRY.
try2fly.b4ucry

1

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, souscription 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 inscrire. Tous sont mis en œuvre en tant qu'INPC. Imaginez ces gestionnaires d'événements que vous avez. Une énorme cascade de clauses if et / ou de clausses de commutation.

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 coup d'œ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 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 d'IConnectionManager sont claires: "Je peux vous dire que la valeur de ma propriété IsConnected peut changer".


1

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

le modèle utilise généralement le IDataErrorInfopour gérer les erreurs de validation, alors gardez simplement votre ViewModel et vous êtes sur la bonne voie pour MVVM.


1
Qu'en est-il des modèles avec plusieurs propriétés? vous répétez le code dans VM.
Luis

0

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? Faire appel OnPropertyChangedà votre vue pour toutes les propriétés de l'objet est une foutaise à mon point de vue.

Donc, ce que je fais est de laisser l'objet lui-même pour avertir quiconque lorsqu'une valeur dans une propriété change, et à mon avis, j'utilise des liaisons comme Object.Property1, Object.Property2et sur. De cette façon, si je veux juste changer l'objet qui est actuellement maintenu dans ma vue, je le fais simplement OnPropertyChanged("Object").

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


0

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.

vois ici


1
emm .. non. Le modèle ne peut pas être un fichier xml ou une base de données. Et le modèle n'est pas utilisé pour donner les données. Sinon, il ne faut pas l'appeler "modèle" mais "données" ..? Le modèle est utilisé pour décrire les données. Tout à fait auto-explicatif, n'est-ce pas? :)
Taras

1
Si vous avez une meilleure réponse, veuillez partager! nous sommes tous ici pour partager nos connaissances et non pour rivaliser
Adam

0

à mon humble avis, je pense que les implémentations viewmodel INotifyPropertyChangeet le modèle pourraient 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 juste un document modifié suffit.

Mais si vous l'utilisez INotifyPropertyChangedans 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 changer le modèle de vue et la vue reste intacte.


0

Toutes les propriétés, qui sont liées à ma vue, sont dans mon (mes) ViewModel (s). Ainsi, ils doivent implémenter l'interface INotifyPropertyChanged. Par conséquent, la vue reçoit toutes les modifications.

[En utilisant la boîte à outils MVVM Light, je leur ai laissé hériter de ViewModelBase.]

Le modèle contient la logique métier, mais n'a rien à voir avec la vue. Ainsi, l'interface INotifyPropertyChanged n'est pas nécessaire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.