En MVVM, ¿debería ViewModel o Model implementar INotifyPropertyChanged?

165

La mayoría de los ejemplos de MVVM con los que he trabajado han implementado el ModeloINotifyPropertyChanged , pero en el ejemplo CommandSink de Josh Smith, los implementos ViewModelINotifyPropertyChanged .

Todavía estoy reuniendo cognitivamente los conceptos de MVVM, así que no sé si:

  • Tienes que ponerlo INotifyPropertyChangeden ViewModel para ponerte CommandSinka trabajar
  • Esto es solo una aberración de la norma y realmente no importa
  • Siempre debe tener el modelo implementado INotifyPropertyChangedy esto es solo un error que se corregiría si se desarrollara a partir de un ejemplo de código para una aplicación

¿Cuáles han sido las experiencias de otros en los proyectos MVVM en los que has trabajado?

Edward Tanguay
fuente
44
si implementa INPC, pruebe github.com/Fody/PropertyChanged ; le ahorrará semanas de escritura.
CAD bloke

Respuestas:

104

Yo diría todo lo contrario, siempre pongo mi INotifyPropertyChangeden mi ViewModel: realmente no quieres contaminar tu modelo con una característica bastante específica de WPF como INotifyPropertyChanged, esas cosas deberían estar en el ViewModel.

Estoy seguro de que otros no estarían de acuerdo, pero esa es la forma en que trabajo.

Steven Robbins
fuente
84
¿Qué haces si una propiedad cambia en el modelo? Necesitas llevarlo al modelo de vista de alguna manera. Pregunta honesta, estoy lidiando con este enigma en este momento.
Roger Lipscombe
44
EventAggregator en el código Prism es una buena alternativa a INotifyPropertyChanged en el modelo, con un tipo de evento modificado de propiedad personalizada. El código de evento en ese proyecto admite el reenvío de eventos entre subprocesos de fondo y de interfaz de usuario, que a veces puede ser un problema.
Steve Mitcham
51
INotifyProperyChanged no es específico de WPF, vive en el espacio de nombres System.ComponentModel, lo he usado en aplicaciones WinForms, también INotifyPropertyChanged ha estado en .Net desde 2.0, WPF solo ha existido desde 3.0
benPearce
40
Soy fanático de poner INotifyPropertyChanged tanto en MODEL como en VIEWMODEL. No puedo pensar en una razón para no hacer esto. Es una manera elegante de informar al VIEWMODEL de cuándo se han producido cambios de fondo en su MODO que afectan al VIEWMODEL al igual que se usa para informar al VIEW y ha habido cambios en el VIEWMODEL.
ScottCher
66
@Steve: sobre informar al ViewModel que la propiedad de un Modelo ha cambiado, parece que INotifyPropertyChanged funciona bien como "un evento en el que el modelo de vista puede engancharse". ¿Por qué no usarlo?
skybluecodeflier
146

Estoy totalmente en desacuerdo con el concepto de que el Modelo no debe implementar el INotifyPropertyChanged. ¡Esta interfaz no es específica de la interfaz de usuario! Simplemente informa de un cambio. De hecho, WPF usa mucho esto para identificar cambios, pero eso no significa que sea una interfaz de UI. Lo compararía con el siguiente comentario: " Un neumático es un accesorio para el automóvil ". Claro que sí, pero las bicicletas, los autobuses, etc. también lo usan. En resumen, no tome esa interfaz como una cosa de UI.

Dicho esto, no necesariamente significa que creo que el Modelo debería proporcionar notificaciones. De hecho, como regla general, el modelo no debe implementar esta interfaz, a menos que sea necesario. En la mayoría de los casos en los que no se envían datos del servidor a la aplicación cliente, el modelo puede quedar obsoleto. Pero si escucho los datos del mercado financiero, entonces no veo por qué el modelo no puede implementar la interfaz. Como ejemplo, ¿qué sucede si tengo una lógica que no es de UI, como un servicio que cuando recibe un precio de compra o venta por un valor determinado emite una alerta (por ejemplo, a través de un correo electrónico) o hace un pedido? Esta podría ser una posible solución limpia.

Sin embargo, hay diferentes formas de lograr las cosas, pero siempre argumentaría a favor de la simplicidad y evitaría la redundancia.

¿Qué es mejor? ¿Definir eventos en una colección o cambios de propiedad en el modelo de vista y propagarlo al modelo o hacer que la vista actualice intrínsecamente el modelo (a través del modelo de vista)?

El resultado final cada vez que ves a alguien que dice " no puedes hacer esto o aquello " es una señal de que no saben de qué están hablando.

Realmente depende de su caso y, de hecho, MVVM es un marco con muchos problemas y todavía no he visto una implementación común de MVVM en todos los ámbitos.

Desearía tener más tiempo para explicar los muchos sabores de MVVM y algunas soluciones a problemas comunes, en su mayoría proporcionados por otros desarrolladores, pero supongo que tendré que hacerlo en otro momento.

Paulo Sousa
fuente
77
Piénsalo de esta manera. Si usted, como desarrollador, consume un archivo .dll con Modelos, seguramente no los volvería a escribir para admitir INotifyPropertyChanged.
Lee Treveil
2
Muy de acuerdo contigo. Al igual que yo, también le complacerá descubrir que la documentación oficial de MVVM < msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx > (sección Modelo) está de acuerdo con nosotros. :-)
Noldorin
"Sin embargo, hay diferentes maneras de lograr las cosas, pero siempre argumentaría a favor de la simplicidad y evitar la redundancia". Muy importante.
Bastien Vandamme
1
INotifyPropertyChangedes parte del System.ComponentModelespacio de nombres que es para el " comportamiento en tiempo de ejecución y tiempo de diseño de componentes y controles ". NO UTILICE INotifyPropertyChanged en Modelos, solo en ViewModels. Enlace a documentos: docs.microsoft.com/en-us/dotnet/api/system.componentmodel
Igor Popov
1
Publicación anterior, lo sé, pero a menudo vuelvo a ella cuando comienzo un nuevo proyecto usando MVVM. Recientemente comencé a aplicar el Principio de Responsabilidad Única de manera mucho más estricta. Un modelo es tener una responsabilidad. Ser modelo Tan pronto como agregue INotifyPropertyChanged al modelo, ya no seguirá el Principio de responsabilidad única. La razón por la que existe el ViewModel es para dejar que el modelo sea el modelo, para que el modelo tenga una única responsabilidad.
Rhyous
13

Creo que MVVM tiene un nombre muy pobre y llamar al ViewModel un ViewModel hace que muchos pierdan una característica importante de una arquitectura bien diseñada, que es un DataController que controla los datos sin importar quién esté tratando de tocarlos.

Si piensa que el Modelo de vista es más como un DataController e implementa una arquitectura donde su DataController es el único elemento que toca los datos, entonces nunca tocaría los datos directamente, sino que siempre usaría el DataController. DataController es útil para la IU, pero no necesariamente solo para la IU. Es para la capa empresarial, la capa de interfaz de usuario, etc.

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

Terminas con un modelo como este. Incluso la empresa solo debe tocar los datos con ViewModel. Entonces tu enigma simplemente desaparece.

Rhyous
fuente
3
Eso es genial si sus datos solo cambian cuando DataController lo cambia. Si los datos provienen de una base de datos o algún otro almacén de datos que puede proporcionar otra vía para el cambio, es posible que deba tener una manera de informar al VIEWMODEL (DataController en su patrón) y VER cuando eso suceda. Puede sondear usando el DataController o pasar de algún proceso externo a su DataModel y permitir que su DataModel envíe notificaciones de cambio a su DataController.
ScottCher
44
Tienes toda la razón. Los patrones de diseño son de muy alto nivel. La mayoría de las veces, el patrón de diseño te lleva a hacer las cosas bien, pero de vez en cuando te vuelven al camino equivocado. Nunca debe evitar hacer algo bien porque está fuera de su patrón de diseño.
Rhyous
También presionaría a su DataController mientras controla y al modelo de datos y le diría que se actualice.
Rhyous
Además, el Modelo en MVVM debe mantenerse específico según lo necesite la UI (por ejemplo, DTO). Por lo tanto, cualquier base de datos o lógica empresarial compleja debe ocurrir en una capa diferente, y luego se deben proporcionar datos brutos a través del modelo de vista.
Nombre
9

Depende de cómo hayas implementado tu modelo. Mi empresa utiliza objetos comerciales similares a los objetos CSLA de Lhotka y los utiliza ampliamente en INotifyPropertyChangedtodo el modelo comercial.

Nuestro motor de validación depende en gran medida de que se le notifique que las propiedades cambian a través de este mecanismo y funciona muy bien. Obviamente, si está utilizando una implementación diferente a la de los objetos comerciales donde la notificación de cambios no es tan crítica para la operación, puede tener otros métodos para detectar cambios en su modelo comercial.

También tenemos modelos de vista que propagan los cambios del modelo donde sea necesario, pero los modelos de vista están escuchando los cambios subyacentes del modelo.

Steve Mitcham
fuente
3
¿Cómo propaga exactamente OnPropertyChanged de Model a OnPropertyChanged de ViewModel? Tengo un problema cuando ViewModel tiene diferentes nombres de propiedad que Modelo: entonces se necesitaría algún tipo de mapeo de nombre a nombre, ¿verdad?
Martin Konicek
No es nada realmente sofisticado, simplemente reenviamos los eventos. Supongo que si los nombres fueran diferentes, entonces podría usarse una tabla de búsqueda. Si el cambio no fuera un mapeo uno a uno, entonces simplemente podría conectar el evento y luego disparar los eventos necesarios en el controlador.
Steve Mitcham
6

Estoy de acuerdo con la respuesta de Paulo, la implementación INotifyPropertyChangeden Modelos es totalmente aceptable e incluso es sugerida por Microsoft:

Por lo general, el modelo implementa las instalaciones que facilitan la vinculación a la vista. Esto generalmente significa que admite notificación de cambio de propiedad y colección a través de las interfaces INotifyPropertyChangedy INotifyCollectionChanged. Las clases de modelos que representan colecciones de objetos generalmente derivan de la ObservableCollection<T>clase, que proporciona una implementación de la INotifyCollectionChangedinterfaz.

Aunque depende de usted decidir si desea ese tipo de implementación o no, pero recuerde:

¿Qué sucede si sus clases de modelo no implementan las interfaces requeridas?

A veces será necesario trabajar con los objetos del modelo que no implementan los INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfo, o INotifyDataErrorInfointerfaces. En esos casos, el modelo de vista puede necesitar envolver los objetos del modelo y exponer las propiedades requeridas a la vista. Los valores de estas propiedades serán proporcionados directamente por los objetos del modelo. El modelo de vista implementará las interfaces necesarias para las propiedades que expone para que la vista pueda enlazar fácilmente los datos con ellas.

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

He trabajado en algunos proyectos donde no hemos implementado INotifyPropertyChangeden nuestros modelos y debido a esto nos enfrentamos a muchos problemas; Se necesitaba una duplicación innecesaria de propiedades en VM y al mismo tiempo tuvimos que actualizar el objeto subyacente (con valores actualizados) antes de pasarlos a BL / DL.

Enfrentará problemas especialmente si necesita trabajar con la colección de sus objetos modelo (digamos en una cuadrícula o lista editable) o modelos complejos; los objetos de modelo no se actualizarán automáticamente y tendrá que administrar todo eso en su VM.

akjoshi
fuente
3

Pero a veces (como en este texto de enlace de presentación ) el modelo es el servicio, que proporciona a la aplicación algunos datos en línea y luego debe notificar al empleador que llegaron nuevos datos o que los datos han cambiado usando eventos ...

Andrey Khataev
fuente
3

Creo que la respuesta es bastante clara si desea adherirse a la MV-VM.

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

En el patrón MVVM, la vista encapsula la interfaz de usuario y cualquier lógica de interfaz de usuario, el modelo de vista encapsula la lógica y el estado de presentación, y el modelo encapsula la lógica y los datos comerciales.

"La vista interactúa con el modelo de vista a través de enlaces de datos, comandos y eventos de notificación de cambio. El modelo de vista consulta, observa y coordina las actualizaciones del modelo, convirtiendo, validando y agregando datos según sea necesario para mostrar en la vista".

John D
fuente
44
La cita está abierta a interpretación. Creo que deberías agregar tu interpretación, para aclarar tu respuesta :-)
Søren Boisen
@John D: ese artículo solo da una interpretación de MVVM y una forma de implementarlo, no define MVVM.
akjoshi
Además, si lee el artículo completo, define la clase Modelo de la siguiente manera: "Por lo general, el modelo implementa las facilidades que facilitan el enlace a la vista. Esto generalmente significa que admite notificación de cambio de propiedad y colección a través de las interfaces INotifyPropertyChanged e INotifyCollectionChanged . Las clases de modelos que representan colecciones de objetos suelen derivar de la clase ObservableCollection <T>, que proporciona una implementación de la interfaz INotifyCollectionChanged ".
akjoshi
2

Yo diría en tu ViewModel. No es parte del modelo, ya que el modelo es independiente de la interfaz de usuario. El modelo debe ser 'todo, EXCEPTO agnóstico empresarial'

Steve Dunn
fuente
2

La implementación de INPC en los modelos se podría usar si los modelos se exponen claramente en ViewModel. Pero en general, ViewModel ajusta los modelos en sus propias clases para reducir la complejidad del modelo (que no debería ser útil para el enlace). En este caso, el INPC debe implementarse en ViewModel.

Stéphane Boutinet
fuente
1

Estoy usando la INotifyPropertyChangeinterfaz en un modelo. En realidad, la UI o el cliente externo solo deben activar un cambio de propiedad del modelo.

He notado varias ventajas y desventajas:

Ventajas

Notifier está en el modelo de negocio

  1. Según el dominio impulsado, es correcto. Debería decidir cuándo subir y cuándo no.

Desventajas

El modelo tiene propiedades (qty, rate, comisión, totalfrieght). Totalfrieght se calcula utilizando cantidad, tasa, cambio de comisión.

  1. En los valores de carga de db, el cálculo total de la longitud se llama 3 veces (cantidad, tasa, comisión). Debería ser una vez.

  2. Si se asigna rate, qty en la capa empresarial, nuevamente se llama al notificador.

  3. Debería haber una opción para deshabilitar esto, posiblemente en la clase base. Sin embargo, los desarrolladores podrían olvidarse de hacer esto.

Anand Kumar
fuente
Debido a todas las desventajas que mencionó, simplemente decidimos que era una decisión INCORRECTA en nuestro proyecto relativamente grande de WPF implementar INPC en los modelos. Deben tratar solo con la capa de persistencia. Todas las demás cosas, como validación, notificación de cambio y propiedades calculadas, deben manejarse en ViewModel. Ahora entiendo claramente que repetir las propiedades del modelo en ViewModel NO siempre es una violación del principio DRY.
try2fly.b4ucry
1

Creo que todo depende del caso de uso.

Cuando tiene un modelo simple con muchas propiedades, puede hacer que implemente INPC. Por simple quiero decir que este modelo se parece bastante a un POCO .

Si su modelo es más complejo y vive en un dominio de modelo interactivo (modelos que hacen referencia a modelos, se suscriben a eventos de otros modelos), implementar eventos modelo como INPC es una pesadilla.

Póngase en una posición de alguna entidad modelo que tenga que colaborar con otros modelos. Tienes varios eventos para suscribirte. Todos ellos se implementan como INPC. Imagina esos controladores de eventos que tienes. Una enorme cascada de cláusulas if y / o clauss de cambio.

Otro problema con INPC. Debe diseñar sus aplicaciones para que se basen en la abstracción, no en la implementación. Esto se hace típicamente usando interfaces.

Echemos un vistazo a 2 implementaciones diferentes de la misma abstracción:

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;}
}

Ahora mira a los dos. ¿Qué te dice IConnectionManagerINPC? Que algunas de sus propiedades pueden cambiar. No sabes cuál de ellos. De hecho, el diseño es que solo IsConnected cambia, ya que el resto son de solo lectura.

Por el contrario, las intenciones de IConnectionManager son claras: "Puedo decirle que el valor de mi propiedad IsConnected puede cambiar".

dzendras
fuente
1

Simplemente use el INotifyPropertyChangeen su modelo de vista y no en el Modelo,

el modelo generalmente usa el IDataErrorInfopara manejar los errores de validación, así que solo manténgase en su ViewModel y estará en su camino MVVM.

Adán
fuente
1
¿Qué pasa con los modelos con varias propiedades? Estás repitiendo código en VM.
Luis
0

Suponga que la referencia del objeto en su vista cambia. ¿Cómo notificará que todas las propiedades se actualicen para mostrar los valores correctos? Llamar OnPropertyChangeda su vista para todas las propiedades del objeto es una basura para mi punto de vista.

Entonces, lo que hago es dejar que el objeto mismo notifique a alguien cuando cambia un valor en una propiedad, y en mi opinión, uso enlaces como Object.Property1, Object.Property2y así sucesivamente . De esa manera, si solo quiero cambiar el objeto que se mantiene actualmente en mi vista, simplemente lo hago OnPropertyChanged("Object").

Para evitar cientos de notificaciones durante la carga de objetos, tengo un indicador booleano privado que lo configuré como verdadero durante la carga, que se verifica desde el objeto OnPropertyChangedy no hace nada.

Dummy01
fuente
0

Normalmente ViewModel implementará el INotifyPropertyChanged . El modelo puede ser cualquier cosa (archivo xml, base de datos o incluso objeto). El modelo se usa para dar los datos al modelo de vista, que se propaga a la vista.

mira aquí

Syed
fuente
1
emm .. no. El modelo no puede ser un archivo xml o una base de datos. Y el modelo no se usa para dar los datos. De lo contrario, debería llamarse no "modelo" sino "datos" ... El modelo se usa para describir los datos. Muy explicativo, ¿no? :)
Taras
1
Si tienes una mejor respuesta, ¡comparte! todos estamos aquí para compartir conocimientos y no competir
Adam
0

En mi opinión, creo que el modelo de vista implementa INotifyPropertyChangey el modelo podría utilizar la notificación en un "nivel" diferente.

por ejemplo, con algún servicio de documentos y un objeto de documento, tiene un evento documentChanged que un modelo de vista escucha para borrar y reconstruir la vista. En el modelo de vista de edición, tiene un cambio de propiedad para las propiedades del documento para admitir las vistas. Si el servicio hace mucho con el documento al guardar (fecha de cambio de actualización, último usuario, etc.), es fácil obtener una sobrecarga de eventos cambiados de propiedad y solo un cambio de documento es suficiente.

Pero si lo usa INotifyPropertyChangeen su modelo, creo que es una buena práctica transmitirlo en su modelo de vista en lugar de suscribirse directamente en su vista. En ese caso, cuando los eventos cambian en su modelo, solo tiene que cambiar el modelo de vista y la vista permanece intacta.

Bram
fuente
0

Todas las propiedades, que están vinculadas a mi vista, están en mi ViewModel (s). Por lo tanto, deberían implementar la interfaz INotifyPropertyChanged. Por lo tanto, la Vista obtiene todos los cambios.

[Utilizando el kit de herramientas MVVM Light, los dejo heredar de ViewModelBase.]

El modelo tiene la lógica de negocios, pero no tiene nada que ver con la vista. Por lo tanto, no hay necesidad de la interfaz INotifyPropertyChanged.

No sea falso
fuente