INotifyPropertyChanged vs. DependencyProperty in ViewModel


353

Quando si implementa ViewModel in un'applicazione WPF con architettura Model-View-ViewModel, sembrano esserci due scelte principali su come renderlo indicizzabile. Ho visto implementazioni che usano DependencyPropertyper le proprietà a cui la View si legherà e invece ho visto l'implementazione di ViewModel INotifyPropertyChanged.

La mia domanda è quando dovrei preferire l'uno all'altro? Ci sono differenze di prestazioni? È davvero una buona idea assegnare le dipendenze di ViewModel a WPF? Cos'altro devo considerare quando devo prendere la decisione di progettazione?


11
consultare stackoverflow.com/questions/1329138/… per un modo verificato dal compilatore di implementare INotifyPropertyChanged. Evitare di avere i nomi delle proprietà come una stringa magica.
Ian Ringrose,

10
Generalmente esiste una differenza sostanziale tra una proprietà di dipendenza e una proprietà normale in una classe che implementa INotifyPropertyChanged. Le proprietà di dipendenza potrebbero essere l'origine o la destinazione nell'associazione dati, ma le proprietà normali con supporto INotifyPropertyChanged potrebbero essere utilizzate solo come origine. Quindi queste soluzioni non sono completamente intercambiabili. L'infrastruttura di associazione dei dati richiede una DP come destinazione per funzionare, ma l'origine può essere una proprietà normale con supporto INotifyPropertyChanged o una DP comune.
Mostafa Rezaei,

4
Vedi stackoverflow.com/a/10595688/200442 per il modo di implementazione .net 4.5 INotifyPropertyChanged.
Daniel Little,

Risposte:


214

Kent ha scritto un blog interessante su questo argomento: Visualizza modelli: POCO contro DependencyObjects .

Breve riassunto:

  1. DependencyObjects non sono contrassegnati come serializzabili
  2. La classe DependencyObject sovrascrive e sigilla i metodi Equals () e GetHashCode ()
  3. Un oggetto Dependency ha affinità di thread: è possibile accedervi solo sul thread su cui è stato creato

Preferisco l'approccio POCO. Una classe base per PresentationModel (aka ViewModel) che implementa l'interfaccia INotifyPropertyChanged è disponibile qui: http://compositeextensions.codeplex.com


24
DependencyObject prende anche una dipendenza dalle librerie WPF, mentre POCO no, consentendo ai modelli di visualizzazione di guidare un altro stack dell'interfaccia utente in cui WPF non è disponibile (Compact Framework, Mono).
codekaizen,

26
È quindi chiaro che le proprietà di Dependecy sono create esclusivamente per l'interfaccia utente e non per il livello aziendale.
Andrei Rînea,

11
Le proprietà di dipendenza richiedono anche un genitore DependencyObject. ViewModel non dovrebbe davvero ereditare da DependencyObject.
Gusdor,

38

Secondo la guida alle prestazioni di WPF, DependencyObjects ha sicuramente prestazioni migliori rispetto ai POCO che implementano INotifyPropertyChanged:

http://msdn.microsoft.com/en-us/library/bb613546.aspx



Se si seleziona .NET Framework versione 4, il collegamento funziona comunque. Non è disponibile per "versione corrente".
doubleYou

Grazie per averlo sottolineato, ci sono molte scandalose informazioni sbagliate là fuori che gli sviluppatori fanno affermazioni salaci che INotifyPropertyChanged è più veloce o comporta meno spese generali rispetto alle DP ed è semplicemente infondata. I DP sono modi rapidi, eleganti e potenti per definire strutturalmente l'albero virtuale (dei dati).
tpartee

C'è un male nascosto nei DependencyObjects. Devono essere creati sullo stesso thread dei controlli che si legano a loro. Ciò significa thread GUI. Ciò significa che devi inviare la creazione a quel thread. Ad esempio, non è possibile caricare e creare tali elementi su un thread in background dal DB. A meno che non invii la creazione. Insane.
ed22

28

La scelta è totalmente basata sulla logica aziendale e sul livello di astrazione dell'interfaccia utente. Se non desideri una buona separazione, DP funzionerà per te.

DependencyProperties sarà applicabile principalmente a livello di VisualElements, quindi non sarà una buona idea se creiamo molte DP per ciascuno dei nostri requisiti aziendali. Inoltre, vi è un costo maggiore per DP rispetto a INotifyPropertyChanged. Quando si progetta un WPF / Silverlight, provare a progettare l'interfaccia utente e ViewModel completamente separati in modo che in qualsiasi momento possiamo modificare i controlli di layout e interfaccia utente (in base al tema e agli stili)

Fai riferimento anche a questo post - /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . Il collegamento ha molti riferimenti al modello Model-View-ViewModel, che è molto rilevante per questa discussione.


9
Il post di jbe risponde alle differenze in modo più accurato. Solo perché una VM (o Presenter) eredita da DependencyObject non significa che non può essere designato o non è logicamente separato dalla vista, significa solo che l'archiviazione per i valori delle proprietà è diversa dai campi dichiarati esplicitamente nella Stile POCO. Detto questo, la serializzazione, l'uguaglianza logica e l'affinità di thread sono problemi reali che le VM basate su DepedencyObject devono affrontare.
micahtan,

"Inoltre, vi è un costo maggiore per DP rispetto a INotifyPropertyChanged" - dov'è la tua fonte di prova al riguardo? Molti sviluppatori sostengono questa affermazione senza alcuna prova a supporto. Secondo MSDN non è vero. "Cerca di progettare UI e ViewModel completamente separati in modo che in qualsiasi momento possiamo modificare i controlli di Layout e UI" - ancora una volta, questo non ha assolutamente nulla a che fare con POCO + PropChange rispetto a DO / DP. Semmai, il registro Reflection and Path in DO / DP migliora la tua capacità di lavorare sul lato visivo.
tpartee

20

Da un punto di vista espressivo, mi piace molto usare le proprietà di dipendenza e la rabbia al pensiero INotifyPropertyChanged. A parte i stringnomi delle proprietà e le possibili perdite di memoria dovute alla sottoscrizione degli eventi, INotifyPropertyChangedè un meccanismo molto più esplicito.

Le proprietà di dipendenza implicano "quando ciò è fatto" utilizzando metadati statici di facile comprensione. È un approccio dichiarativo che ottiene il mio voto per l'eleganza.


1
La parte stringa ora ha una soluzione con l'operatore nameof.
Newtopian,

@Newtopian: True. Ci sono anche alcune cose interessanti possibili con [CallerMemberName].
Bryan Watts,

Per non parlare della ricchezza dei vantaggi della registrazione della proprietà (riflessione) in WPF e CLR quando si utilizza un modello DO / DP rispetto a un POCO.
tpartee

16

INotifyPropertyChanged quando usato ti dà anche la possibilità di aggiungere più logica nel codice dei tuoi getter e setter delle tue proprietà.

DependencyProperty esempio:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

Nel tuo getter e setter --- tutto ciò che puoi fare è semplicemente chiamare SetValue e GetValue rispettivamente, b / c in altre parti del framework il getter / setter non viene chiamato, invece chiama direttamente SetValue, GetValue, quindi la tua logica di proprietà non essere eseguito in modo affidabile.

Con INotifyPropertyChanged, definire un evento:

public event PropertyChangedEventHandler PropertyChanged;

E quindi semplicemente avere qualsiasi logica in qualsiasi punto del codice, quindi chiama:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

Questo potrebbe essere in un getter / setter o altrove.


11
Puoi anche ricevere notifiche di modifica da DependencyProperties. Vedi PropertyMetadata.PropertyChangedCallback. Esempio su: msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White

2
Inoltre, puoi chiamare SetValue anche da qualsiasi luogo, non solo dall'interno della proprietà
aL3891

Questo è fuorviante e falso: ci sono diversi modi per agganciarsi agli eventi di cambiamento su un DP, anche quando è cambiato "internamente". Uno di loro si è ricordato sopra da Joe White
tpartee

16

Le proprietà di dipendenza hanno lo scopo di supportare l'associazione (come destinazione) su elementi dell'interfaccia utente non come un'origine per l'associazione dei dati, è qui che entra in gioco INotifyProperty. Da un punto di vista puro non si dovrebbe usare DP su ViewModels.

"Per essere l'origine di un'associazione, una proprietà non deve necessariamente essere una proprietà di dipendenza; è possibile utilizzare qualsiasi proprietà CLR come origine di associazione. Tuttavia, per essere la destinazione di un'associazione, la proprietà deve essere un proprietà di dipendenza. Perché un'associazione unidirezionale o bidirezionale sia efficace, la proprietà di origine deve supportare le notifiche di modifica che si propagano al sistema di associazione e quindi alla destinazione. Per le origini di associazione CLR personalizzate, ciò significa che la proprietà deve supportare INotifyPropertyChanged. Le raccolte dovrebbero supportare INotifyCollectionChanged. "

Non è possibile serializzare tutti gli oggetti di dipendenza (ciò potrebbe ostacolare l'uso di ViewModels e DTO (POCO).

Esistono differenze tra DP all'interno di Silverlight rispetto a WPF.

http://msdn.microsoft.com/en-us/library/cc221408(v=VS.95).aspx

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx


Sto usando oggetti di dipendenza serializzati dal 2009 senza problemi, quindi non sono sicuro di cosa stai parlando quando dici "Tutti gli oggetti di dipendenza non possono essere serializzati" - sì, possono. In effetti ci sono molte opzioni: codeproject.com/Articles/61440/… emphess.net/2008/11/25/dependencyproperty-serialization E uno dei miei preferiti personali: basta fornire negozi di supporto per tutti i tuoi DP e renderli serializzabili ( non sono stati prontamente disponibili buoni esempi semplici in 2 minuti di ricerca su Google, ma ti assicuro che funziona).
tpartee

7

Anch'io ho dovuto prendere in considerazione questa decisione di recente.

Ho scoperto che il meccanismo INotifyPropertyChanged si adattava meglio alle mie esigenze perché mi permetteva di incollare la mia GUI su un framework logico aziendale esistente senza duplicare lo stato. Il framework che stavo usando aveva il suo modello di osservatore ed era facile inoltrare un livello di notifica al successivo. Avevo semplicemente una classe che implementava l'interfaccia dell'osservatore dal mio framework di logica aziendale e dall'interfaccia INotifyPropertyChanged.

Con DP non è possibile definire da soli il backend che memorizza lo stato. Avrei dovuto lasciare che .net memorizzasse nella cache una copia di ogni elemento di stato a cui ero vincolante. Sembrava un sovraccarico inutile: il mio stato è ampio e complicato.

Quindi qui ho trovato INotifyPropertyChanged migliore per esporre le proprietà dalla logica di business alla GUI.

Detto questo, dove avevo bisogno di un widget GUI personalizzato per esporre una proprietà e affinché le modifiche a quella proprietà influissero su altri widget della GUI DP si è rivelata la soluzione semplice.

Quindi lì ho trovato DP utile per la notifica da GUI a GUI.


6

È davvero una buona idea assegnare le dipendenze di ViewModel a WPF?

.NET 4.0 avrà System.Xaml.dll, quindi non dovrai prendere una dipendenza da un framework arbitrario per utilizzarlo. Vedi il post di Rob Relyea sulla sua sessione PDC.

La mia opinione

XAML è un linguaggio per descrivere oggetti e WPF è un framework i cui oggetti descritti sono elementi dell'interfaccia utente.

La loro relazione è simile a C #, un linguaggio per descrivere la logica, e .NET, un framework che implementa particolari tipi di logica.

Lo scopo di XAML è rappresentato da grafici di oggetti dichiarativi. Le tecnologie W * F sono ottime candidate per questo paradigma, ma XAML esiste indipendentemente da esse.

XAML e l'intero sistema di dipendenza sono stati implementati come stack separati per WF e WPF, probabilmente per sfruttare l'esperienza di diversi team senza creare una dipendenza (nessun gioco di parole previsto) tra di loro.


Rispondendo, sembra che tu stia assumendo il presupposto che bitbonk consideri XAML e WPF uguali. ViewModels dovrebbe avere il minor numero possibile di dipendenze WPF, non per aumentare la separazione logica, ma per ridurre la complessità del codice ed evitare tutti i problemi associati alla semplice scrittura della logica nel code-behind di un controllo utente. Inevitabilmente implementerai concetti WPF come ICommand e presenterai comportamenti che solo WPF / Silverlight saranno in grado di avvolgere facilmente - le tue uniche preoccupazioni di threading di presentazione in un modello di vista dovrebbero essere CollectionViews e ObservableCollection.
Gusdor,

6

Le proprietà di dipendenza sono la colla della creazione di controlli personalizzati. Se si desidera utilizzare Intelli-sense per mostrare le proprietà nella finestra delle proprietà in fase di progettazione XAML, è necessario utilizzare le proprietà di dipendenza. INPC non mostrerà mai una proprietà nella finestra delle proprietà in fase di progettazione.


4

Sembra che le proprietà di dipendenza debbano essere utilizzate nei controlli creati come Pulsanti. Per utilizzare le proprietà in XAML e utilizzare tutte le funzionalità di WPF, tali proprietà devono Proprietà di dipendenza.

Tuttavia, ViewModel sta meglio usando INotifyPropertyChanged. L'uso di INotifyPropertyChanged ti darà la possibilità di avere la logica getter / setter, se necessario.

Consiglio di provare la versione di Josh Smith di una classe base per un ViewModel che implementa già INotifyPropertyChanged:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

Penso che questo sia un eccellente esempio di come fare un ViewModel.


4

Penso che DependencyProperty e INotifyPropertyChanged siano usati per due cose diverse in Binding: il primo per consentire a una proprietà di essere una destinazione di un binding e ricevere l'input da un'altra proprietà (usare {Binding ...} per impostare la proprietà), l'ultimo quando si desidera utilizzare il valore di una proprietà come origine di un'associazione (nome nell'espressione del percorso di associazione). Quindi la scelta è puramente tecnica.


2
In entrambi i casi è possibile utilizzare un INotifyPropertyChanged. Puoi associare TwoWay ad esso. Una DependencyProperty è necessaria per motivi tecnici solo per alcune azioni eseguite su un oggetto View (impostazione di alcune proprietà durante l'istanza di un oggetto View in XAML, ad esempio). Una DependencyProperty non è mai richiesta per un ViewModel.
oillio,

3

Preferisco un approccio più diretto, di cui ho scritto un blog nel modello di presentazione senza INotifyPropertyChanged . Utilizzando un'alternativa all'associazione dati, è possibile associare direttamente alle proprietà CLR senza alcun codice di contabilità. Scrivi semplicemente il vecchio codice .NET nel tuo modello di visualizzazione e viene aggiornato quando il tuo modello di dati cambia.


Senza INotifyPropertyChanged, PropertyDescriptorvengono utilizzati, il che provoca perdite di memoria
Tilak

La libreria dei controlli di aggiornamento che presento in quel post di blog utilizza riferimenti deboli, non descrittori di proprietà. Non perde memoria.
Michael L Perry,

1
Michael, la tua libreria genera molto codice. Non vedo benefici. Posso ottenere lo stesso generando il wrapper di modello con le chiamate agli eventi PropertyChanged generati.
Der_Meister

3

C'è solo una cosa per cui preferire un DependencyObject- Binding funzionerà meglio. Prova un esempio con un ListBoxe TextBox, popola l'elenco con i dati della INotifyPropertyChangedproprietà vs. DependencyPropertye modifica l'elemento corrente da TextBox...


1
Esempio di codice, per favore
Hassan Tareq,

1

Se vuoi esporre le proprietà ad altri controlli devi usare le proprietà di dipendenza ... Ma buona fortuna perché impiegano un po 'di tempo per capire ...

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.