In MVVM ViewModel o Model dovrebbero implementare INotifyPropertyChanged?


165

La maggior parte degli esempi MVVM su cui ho lavorato ha avuto l' attrezzo ModelINotifyPropertyChanged , ma nell'esempio CommandSink di Josh Smith implementa ViewModelINotifyPropertyChanged .

Sto ancora mettendo insieme cognitivamente i concetti di MVVM, quindi non so se:

  • Devi mettere INotifyPropertyChangedil ViewModel per metterti CommandSinkal lavoro
  • Questa è solo un'aberrazione della norma e non importa davvero
  • Dovresti sempre avere l'attrezzo Modello INotifyPropertyChangede questo è solo un errore che verrebbe corretto se questo fosse sviluppato da un esempio di codice a un'applicazione

Quali sono state le esperienze degli altri su progetti MVVM su cui hai lavorato?


4
se si implementa INPC, provare github.com/Fody/PropertyChanged - ti farà risparmiare settimane di digitazione.
Bloke CAD,

Risposte:


104

Direi il contrario, metto sempre il mio INotifyPropertyChangedsul mio ViewModel - non vuoi davvero inquinare il tuo modello con una caratteristica abbastanza specifica del WPF come INotifyPropertyChanged, quella roba dovrebbe stare nel ViewModel.

Sono sicuro che gli altri non sarebbero d'accordo, ma è così che lavoro.


84
Cosa fai se una proprietà cambia nel modello? È necessario in qualche modo portarlo al modello di visualizzazione. Domanda onesta, sto affrontando questo enigma in questo momento.
Roger Lipscombe,

4
EventAggregator nel codice Prism è una buona alternativa a INotifyPropertyChanged sul modello, con una proprietà personalizzata modificata del tipo di evento. Il codice evento in quel progetto supporta l'inoltro di eventi tra thread in background e UI, che a volte può essere un problema.
Steve Mitcham,

51
INotifyProperyChanged non è specifico di WPF, vive nello spazio dei nomi System.ComponentModel, l'ho usato nelle applicazioni WinForms, anche INotifyPropertyChanged è stato in .Net dal 2.0, WPF è in circolazione dal 3,0
benPearce

40
Sono un fan del mettere INotifyPropertyChanged sia nel MODELLO che nel MODELLO DI VISTA. Non riesco a pensare a un motivo per non farlo. È un modo elegante per informare il VIEWMODEL di quando sono avvenute modifiche di background nella tua MODALITÀ che influenzano il VIEWMODEL proprio come è stato usato per informare la VIEW e ci sono state delle modifiche a VIEWMODEL.
ScottCher,

6
@Steve: per informare ViewModel che la proprietà di un modello è cambiata, sembra che INotifyPropertyChanged funzioni perfettamente come "un evento a cui il viewmodel può collegarsi". Perché non usarlo?
skybluecodeflier,

146

Sono fortemente in disaccordo con il concetto secondo cui il Modello non dovrebbe implementare INotifyPropertyChanged. Questa interfaccia non è specifica dell'interfaccia utente! Informa semplicemente di un cambiamento. In effetti, WPF lo utilizza pesantemente per identificare le modifiche, ma ciò non significa che sia un'interfaccia utente. Lo confronterei con il seguente commento: " Una gomma è un accessorio per auto ". Certo che lo è, ma lo usano anche biciclette, autobus, ecc. In sintesi, non prendere quell'interfaccia come una cosa dell'interfaccia utente.

Detto questo, ciò non significa necessariamente che ritengo che il Modello debba fornire notifiche. In effetti, come regola generale, il modello non dovrebbe implementare questa interfaccia, a meno che non sia necessario. Nella maggior parte dei casi in cui i dati del server non vengono trasferiti all'app client, il modello può essere obsoleto. Ma se ascolto i dati del mercato finanziario, non vedo perché il modello non possa implementare l'interfaccia. Ad esempio, cosa succede se ho una logica non UI come un servizio che quando riceve un prezzo Bid o Ask per un determinato valore emette un avviso (ad es. Tramite un'e-mail) o effettua un ordine? Questa potrebbe essere una possibile soluzione pulita.

Tuttavia, ci sono diversi modi per raggiungere le cose, ma vorrei sempre discutere a favore della semplicità ed evitare la ridondanza.

Cos'è meglio? Definizione di eventi in una raccolta o modifiche delle proprietà sul modello di vista e propagazione al modello o con l'aggiornamento della vista intrinsecamente il modello (tramite il modello di vista)?

La linea di fondo ogni volta che vedi qualcuno che afferma che " non puoi fare questo o quello " è un segno che non sanno di cosa stanno parlando.

Dipende molto dal tuo caso e in effetti MVVM è un framework con molti problemi e devo ancora vedere un'implementazione comune di MVVM su tutta la linea.

Vorrei avere più tempo per spiegare i molti gusti di MVVM e alcune soluzioni ai problemi più comuni, per lo più forniti da altri sviluppatori, ma credo che dovrò farlo un'altra volta.


7
Pensare in questo modo. Se tu, come sviluppatore, consumassi una DLL con modelli in te, sicuramente non li riscriveresti per supportare INotifyPropertyChanged.
Lee Treveil,

2
Sono fortemente d'accordo con te. Come me, potresti anche essere contento di scoprire che la documentazione ufficiale MVVM < msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx > (sezione Modello) è d'accordo con noi. :-)
Noldorin,

"Tuttavia, ci sono diversi modi per raggiungere le cose, ma vorrei sempre discutere a favore della semplicità ed evitare la ridondanza." Molto importante.
Bastien Vandamme,

1
INotifyPropertyChangedfa parte dello System.ComponentModelspazio dei nomi che è per " comportamento in fase di esecuzione e in fase di progettazione di componenti e controlli ". NON UTILIZZARE INotifyPropertyChanged nei modelli, solo in ViewModels. Link alla documentazione: docs.microsoft.com/en-us/dotnet/api/system.componentmodel
Igor Popov

1
Vecchio post, lo so, ma ci torno spesso quando inizio un nuovo progetto usando MVVM. Di recente ho iniziato a far applicare il principio della responsabilità singola molto più rigorosamente. Un modello è avere una responsabilità. Essere un modello. Non appena aggiungi INotifyPropertyChanged al modello, non segue più il principio di responsabilità singola. L'intera ragione per cui ViewModel esiste è lasciare che il modello sia il modello, per lasciare che il modello abbia una sola responsabilità.
Rhyous


13

Penso che MVVM abbia un nome molto scadente e che chiama ViewModel un ViewModel fa perdere a molti un'importante caratteristica di un'architettura ben progettata, che è un DataController che controlla i dati indipendentemente da chi sta provando a toccarlo.

Se si considera il View-Model come più di un DataController e si implementa un'architettura in cui DataController è l'unico elemento che tocca i dati, non si toccherebbe mai direttamente i dati, ma si utilizza sempre DataController. DataController è utile per l'interfaccia utente ma non necessariamente solo per l'interfaccia utente. È per livello aziendale, livello interfaccia utente, ecc ...

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

Finisci con un modello come questo. Anche l'azienda dovrebbe solo toccare i dati utilizzando ViewModel. Quindi il tuo enigma scompare.


3
È fantastico se i tuoi dati cambiano solo quando DataController li modifica. Se i dati provengono da un database o da qualche altro archivio dati che può fornire un'altra via di modifica, potrebbe essere necessario disporre di un modo per informare VIEWMODEL (DataController nel modello) e VIEW quando ciò accade. È possibile eseguire il polling utilizzando DataController o passare da un processo esterno a DataModel e consentire a DataModel di inviare notifiche di modifica a DataController.
ScottCher,

4
Hai esattamente ragione. Gli schemi di design sono di altissimo livello. Il più delle volte il modello di progettazione ti porta a fare le cose nel modo giusto, ma ogni tanto ti fanno girare nel modo sbagliato. Non dovresti mai evitare di fare qualcosa di giusto perché è al di fuori del tuo modello di progettazione.
Rhyous,

Dovresti anche spingere verso il tuo DataController mentre controlla e il modello di dati e gli diresti di aggiornare.
Rhyous

Inoltre, il modello in MVVM deve essere mantenuto specifico per quanto richiesto dall'interfaccia utente (ad es. DTO). Pertanto, qualsiasi DB o logica aziendale complessa dovrebbe avvenire in un livello diverso e quindi dovrebbero essere forniti dati grezzi tramite il modello di visualizzazione.
Nome in codice Jack

9

Dipende da come hai implementato il tuo modello. La mia azienda utilizza oggetti business simili agli oggetti CSLA di Lhotka e ne fa un ampio usoINotifyPropertyChanged tutto il modello di business.

Il nostro motore di convalida si basa fortemente sulla notifica che le proprietà cambiano attraverso questo meccanismo e funziona molto bene. Ovviamente, se stai utilizzando un'implementazione diversa da quella degli oggetti business in cui la notifica delle modifiche non è così critica per l'operazione, potresti avere altri metodi per rilevare le modifiche nel tuo modello di business.

Abbiamo anche modelli vista che propagano le modifiche dal modello dove necessario, ma i modelli vista stessi ascoltano le modifiche del modello sottostante.


3
Come si propagano esattamente OnPropertyChanged di Model a OnPropertyChanged di ViewModel? Ho un problema quando ViewModel ha nomi di proprietà diversi rispetto a Model: sarebbe necessario un tipo di mappatura da nome a nome, giusto?
Martin Konicek,

Non è niente di veramente sofisticato, semplicemente inoltriamo gli eventi. Suppongo che se i nomi fossero diversi, si potrebbe usare una tabella di ricerca. Se la modifica non fosse un mapping uno a uno, allora potresti semplicemente agganciare l'evento e quindi attivare gli eventi necessari nel gestore.
Steve Mitcham,

6

Sono d'accordo con la risposta di Paulo, l'implementazione INotifyPropertyChangedin Modelli è totalmente accettabile ed è persino suggerita da Microsoft -

In genere, il modello implementa le funzionalità che semplificano il collegamento alla vista. Questo di solito significa che supporta la notifica di modifica di proprietà e raccolta tramite le interfacce INotifyPropertyChangede INotifyCollectionChanged. Le classi di modelli che rappresentano raccolte di oggetti derivano in genere dalla ObservableCollection<T>classe, che fornisce un'implementazione INotifyCollectionChangeddell'interfaccia.

Anche se spetta a te decidere se vuoi quel tipo di implementazione o meno, ma ricorda:

Cosa succede se le classi del modello non implementano le interfacce richieste?

A volte hai bisogno di lavorare con oggetti del modello che non implementano i INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfoo INotifyDataErrorInfointerfacce. In questi casi, potrebbe essere necessario che il modello di vista includa gli oggetti modello ed esponga le proprietà richieste alla vista. I valori per queste proprietà saranno forniti direttamente dagli oggetti del modello. Il modello di vista implementerà le interfacce richieste per le proprietà che espone in modo che la vista possa facilmente associarsi ai dati.

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

Ho lavorato in alcuni progetti in cui non abbiamo implementato i INotifyPropertyChangednostri modelli e per questo abbiamo dovuto affrontare molti problemi; la duplicazione non necessaria delle proprietà era necessaria nella VM e allo stesso tempo dovevamo aggiornare l'oggetto sottostante (con valori aggiornati) prima di passarli a BL / DL.

Dovrai affrontare i problemi specialmente se devi lavorare con la raccolta dei tuoi oggetti modello (diciamo in una griglia o un elenco modificabile) o modelli complessi; gli oggetti modello non verranno aggiornati automaticamente e dovrai gestire tutto ciò nella tua VM.


3

Ma a volte (come in questo testo del link di presentazione ) è il servizio, che fornisce all'applicazione alcuni dati online e quindi è necessario implementare la notifica che sono arrivati ​​nuovi dati o che i dati sono cambiati usando gli eventi ...


3

Penso che la risposta sia abbastanza chiara se desideri aderire alla MV-VM.

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

Nel modello MVVM, la vista incapsula l'interfaccia utente e qualsiasi logica dell'interfaccia utente, il modello della vista incapsula la logica e lo stato della presentazione e il modello incapsula la logica e i dati aziendali.

"La vista interagisce con il modello della vista attraverso l'associazione di dati, i comandi e gli eventi di notifica di modifica. Il modello della vista richiede, osserva e coordina gli aggiornamenti del modello, convertendo, convalidando e aggregando i dati necessari per la visualizzazione nella vista."


4
La citazione è aperta all'interpretazione. Penso che dovresti aggiungere la tua interpretazione, per chiarire la tua risposta :-)
Søren Boisen,

@John D: Questo articolo fornisce solo un'interpretazione di MVVM e un modo per implementarlo, non definisce MVVM.
Akjoshi,

Inoltre, se leggi l'articolo completo definisce la classe Model in questo modo: "In genere, il modello implementa le funzionalità che semplificano il bind alla vista. Questo di solito significa che supporta la notifica di modifica proprietà e raccolta tramite le interfacce INotifyPropertyChanged e INotifyCollectionChanged Le classi di modelli che rappresentano raccolte di oggetti derivano in genere dalla classe ObservableCollection <T>, che fornisce un'implementazione dell'interfaccia INotifyCollectionChanged. "
Akjoshi,

2

Direi nel tuo ViewModel. Non fa parte del modello in quanto il modello è agnostico dell'interfaccia utente. Il modello dovrebbe essere "tutto ECCEZIONATO dal punto di vista aziendale"


2

L'implementazione di INPC nei modelli potrebbe essere utilizzata se i modelli sono chiaramente esposti in ViewModel. Ma in generale ViewModel racchiude i modelli nelle sue classi per ridurre la complessità del modello (che non dovrebbe essere utile per l'associazione). In questo caso l'INPC dovrebbe essere implementato nel ViewModel.


1

Sto usando il INotifyPropertyChange interfaccia in un modello. In realtà, una modifica della proprietà del modello deve essere attivata solo dall'interfaccia utente o dal client esterno.

Ho notato diversi vantaggi e svantaggi:

vantaggi

Notifier è nel modello aziendale

  1. Come per dominio guidato, è giusto. Dovrebbe decidere quando rilanciare e quando non farlo.

svantaggi

Il modello ha proprietà (qty, rate, commission, totalfrieght). Totalfrieght è calcolato usando qty, rate, cambio di commissione.

  1. Quando si caricano i valori da db, il calcolo totale del frieght viene chiamato 3 volte (quantità, velocità, commissione). Dovrebbe essere una volta.

  2. Se rate, qty è assegnato nel livello aziendale, viene chiamato nuovamente notificatore.

  3. Dovrebbe esserci un'opzione per disabilitarlo, possibilmente nella classe base. Tuttavia, gli sviluppatori potrebbero aver dimenticato di farlo.


A causa di tutti gli svantaggi che hai elencato, abbiamo appena deciso che nel nostro progetto WPF relativamente grande era implementare l'INPC nei modelli. Dovrebbero occuparsi solo del livello di persistenza. Tutte le altre cose come la convalida, la notifica di modifica e le proprietà calcolate devono essere gestite in ViewModel. Ora capisco chiaramente che ripetere le proprietà del modello in ViewModel NON è sempre una violazione del principio DRY.
try2fly.b4ucry

1

Penso che tutto dipenda dal caso d'uso.

Quando si dispone di un modello semplice con un sacco di proprietà, è possibile implementarlo INPC. Per semplice intendo che questo modello sembra piuttosto un POCO .

Se il tuo modello è più complesso e vive in un dominio di modello interattivo - modelli che fanno riferimento a modelli, abbonamento a eventi di altri modelli - avere eventi modello implementati come INPC è un incubo.

Mettiti in una posizione di qualche entità modello che deve collaborare con alcuni altri modelli. Hai vari eventi a cui iscriverti. Tutti sono implementati come INPC. Immagina quei gestori di eventi che hai. Un'enorme cascata di clausole if e / o switch clausses.

Un altro problema con INPC. È necessario progettare le app in modo che facciano affidamento sull'astrazione, non sull'implementazione. Questo in genere viene fatto utilizzando le interfacce.

Diamo un'occhiata a 2 diverse implementazioni della stessa astrazione:

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

Ora guarda entrambi. Cosa ti dice IConnectionManagerINPC? Che alcune delle sue proprietà potrebbero cambiare. Non sai quale di loro. In effetti, la progettazione prevede che solo IsConnected cambi, poiché il resto è di sola lettura.

Al contrario, le intenzioni di IConnectionManager sono chiare: "Posso dirti che il valore della mia proprietà IsConnected potrebbe cambiare".


1

Basta usare il INotifyPropertyChange nel proprio modello di visualizzazione e non nel Modello,

il modello di solito usa il IDataErrorInfoper gestire gli errori di convalida, quindi tieni il tuo ViewModel e hai ragione sulla tua strada MVVM.


1
Che dire di modelli con diverse proprietà? stai ripetendo il codice nella VM.
Luis

0

Supponiamo che il riferimento dell'oggetto nella vista cambi. Come notificherai che tutte le proprietà saranno aggiornate per mostrare i valori corretti? Chiamare OnPropertyChangednella tua vista per tutte le proprietà dell'oggetto è spazzatura dal mio punto di vista.

Quindi quello che faccio è quello di lasciare l'oggetto stesso di notificare a nessuno quando un valore in una proprietà cambia, ea mio parere io uso attacchi come Object.Property1, Object.Property2e su. In questo modo, se voglio solo cambiare l'oggetto che è attualmente gestito a mio avviso, lo faccio OnPropertyChanged("Object").

Per evitare centinaia di notifiche durante il caricamento di oggetti, ho un indicatore booleano privato che l'ho impostato su true durante il caricamento che viene controllato dall'oggetto OnPropertyChangede non fa nulla.


0

Normalmente ViewModel implementerà il INotifyPropertyChanged . Il modello può essere qualsiasi cosa (file xml, database o persino oggetto). Il modello viene utilizzato per fornire i dati al viewmodel, che si propaga alla vista.

Vedere qui


1
emm .. no. Il modello non può essere un file xml o un database. E il modello non viene utilizzato per fornire i dati. Altrimenti dovrebbe essere chiamato non "modello" ma "dati" ..? Il modello viene utilizzato per descrivere i dati. Abbastanza autoesplicativo, no? :)
Taras,

1
Se hai una risposta migliore, condividi! siamo tutti qui per condividere le conoscenze e non competere
Adam

0

Imho, penso INotifyPropertyChangeche il modello di view implementa e il modello potrebbe usare la notifica su un "livello" diverso.

ad es. con alcuni servizi documentali e un oggetto documento si ha un evento documentChanged che un modello di visualizzazione ascolta per cancellare e ricostruire la vista. Nel viewmodel di modifica è presente un cambio di proprietà per le proprietà del documento per supportare le visualizzazioni. Se il servizio fa molto con il documento al momento del salvataggio (aggiornamento della data di modifica, ultimo utente e così via), si ottiene facilmente un sovraccarico di eventi Ipropertychanged e basta solo un documento modificato.

Ma se utilizzi INotifyPropertyChangenel tuo modello, penso che sia una buona pratica trasmetterlo nel tuo modello di visualizzazione invece di iscriverti direttamente alla tua vista. In tal caso, quando gli eventi cambiano nel tuo modello, devi solo cambiare il modello di visualizzazione e la vista rimane intatta.


0

Tutte le proprietà, che sono vincolate alla mia vista, sono nei miei ViewModel. Pertanto, dovrebbero implementare l'interfaccia INotifyPropertyChanged. Pertanto, la vista ottiene tutte le modifiche.

[Usando il toolkit MVVM Light, li lascio ereditare da ViewModelBase.]

Il modello contiene la logica aziendale, ma non ha nulla a che fare con la vista. Pertanto non è necessaria l'interfaccia INotifyPropertyChanged.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.