INotifyPropertyChanged vs. DependencyProperty in ViewModel

353

Bei der Implementierung des ViewModel in einer WPF-Anwendung mit Model-View-ViewModel-Architektur scheint es zwei Hauptoptionen zu geben, wie es datenbindbar gemacht werden kann. Ich habe Implementierungen gesehen, die DependencyPropertyfür Eigenschaften verwendet werden, an die die Ansicht gebunden werden soll, und ich habe INotifyPropertyChangedstattdessen die Implementierung des ViewModel gesehen .

Meine Frage ist, wann ich eines dem anderen vorziehen soll? Gibt es Leistungsunterschiede? Ist es wirklich eine gute Idee, WPF die ViewModel-Abhängigkeiten zu geben? Was muss ich bei der Entwurfsentscheidung noch beachten?

Bitbonk
quelle
11
Unter stackoverflow.com/questions/1329138/… finden Sie eine vom Compiler überprüfte Methode zur Implementierung von INotifyPropertyChanged. Vermeiden Sie es, die Eigenschaftsnamen als magische Zeichenfolge zu verwenden.
Ian Ringrose
10
Im Allgemeinen gibt es einen großen Unterschied zwischen einer Abhängigkeitseigenschaft und einer normalen Eigenschaft in einer Klasse, die INotifyPropertyChanged implementiert. Abhängigkeitseigenschaften können Quelle oder Ziel bei der Datenbindung sein, aber normale Eigenschaften mit INotifyPropertyChanged-Unterstützung können nur als Quelle verwendet werden. Diese Lösungen sind also nicht vollständig austauschbar. Für die Datenbindungsinfrastruktur ist ein DP als Ziel erforderlich, die Quelle kann jedoch entweder eine normale Eigenschaft mit INotifyPropertyChanged-Unterstützung oder ein gemeinsamer DP sein.
Mostafa Rezaei
4
Informationen zur Implementierung von .net 4.5 finden Sie unter stackoverflow.com/a/10595688/200442INotifyPropertyChanged .
Daniel Little
am besten hier erklärt stackoverflow.com/a/3552550/366064
Bizhan

Antworten:

214

Kent hat einen interessanten Blog zu diesem Thema geschrieben: Modelle anzeigen: POCOs versus DependencyObjects .

Kurze Zusammenfassung:

  1. DependencyObjects sind nicht als serialisierbar markiert
  2. Die DependencyObject-Klasse überschreibt und versiegelt die Methoden Equals () und GetHashCode ()
  3. Ein DependencyObject hat eine Thread-Affinität - es kann nur auf den Thread zugegriffen werden, auf dem es erstellt wurde

Ich bevorzuge den POCO-Ansatz. Eine Basisklasse für PresentationModel (auch bekannt als ViewModel), die die INotifyPropertyChanged-Schnittstelle implementiert, finden Sie hier: http://compositeextensions.codeplex.com

jbe
quelle
24
DependencyObject ist auch von den WPF-Bibliotheken abhängig, POCO jedoch nicht. Dadurch können Ihre Ansichtsmodelle einen anderen UI-Stack steuern, für den WPF nicht verfügbar ist (Compact Framework, Mono).
Codekaizen
26
Es ist dann klar, dass die Abhängigkeitseigenschaften ausschließlich für die Benutzeroberfläche und nicht für die Geschäftsschicht erstellt wurden.
Andrei Rînea
11
Abhängigkeitseigenschaften erfordern auch ein übergeordnetes DependencyObject. Ihr ViewModel sollte wirklich nicht von DependencyObject erben.
Gusdor
38

Laut dem WPF-Leistungsleitfaden weisen DependencyObjects definitiv eine bessere Leistung auf als POCOs, die INotifyPropertyChanged implementieren:

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

James Ashley
quelle
1
Da muss ich zustimmen ;-)
Jonatha ANTOINE
Wenn Sie .NET Framework Version 4 auswählen, funktioniert der Link weiterhin. Es ist nur nicht für "aktuelle Version" verfügbar.
doubleYou
Vielen Dank, dass Sie darauf hingewiesen haben. Es gibt viele skandalöse Fehlinformationen von Entwicklern, die brutale Behauptungen aufstellen, dass INotifyPropertyChanged schneller ist oder weniger Overhead verursacht als DPs und einfach unbegründet ist. DPs sind schnelle, elegante und leistungsstarke Methoden, um den virtuellen (Daten-) Baum strukturell zu definieren.
Tpartee
Die DependencyObjects haben ein verstecktes Übel. Sie müssen im selben Thread wie die Steuerelemente erstellt werden, die an sie gebunden sind. Das bedeutet GUI-Thread. Das bedeutet, dass Sie die Erstellung an diesen Thread senden müssen. Sie können diese Dinge beispielsweise nicht in einem Hintergrundthread aus der Datenbank laden und erstellen lassen. Es sei denn, Sie versenden die Erstellung. Wahnsinnig.
ed22
28

Die Auswahl basiert vollständig auf Ihrer Geschäftslogik und der Abstraktionsstufe der Benutzeroberfläche. Wenn Sie keine gute Trennung wünschen, funktioniert DP für Sie.

DependencyProperties wird hauptsächlich auf VisualElements-Ebene angewendet. Daher ist es keine gute Idee, wenn wir für jede unserer Geschäftsanforderungen viele DPs erstellen. Außerdem fallen für DP höhere Kosten an als für INotifyPropertyChanged. Wenn Sie ein WPF / Silverlight entwerfen, versuchen Sie, Benutzeroberfläche und ViewModel vollständig getrennt zu gestalten, damit wir jederzeit die Steuerelemente für Layout und Benutzeroberfläche ändern können (basierend auf Thema und Stilen).

Lesen Sie auch diesen Beitrag - /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . Der Link enthält viele Verweise auf das Model-View-ViewModel-Muster, das für diese Diskussion sehr relevant ist.

Jobi Joy
quelle
9
Der Beitrag von jbe beantwortet die Unterschiede genauer. Nur weil eine VM (oder ein Presenter) von DependencyObject erbt, bedeutet dies nicht, dass sie nicht formatiert werden kann oder nicht logisch von der Ansicht getrennt ist, sondern nur, dass sich der Speicher für die Eigenschaftswerte von den explizit deklarierten Feldern in der Ansicht unterscheidet POCO-Stil. Davon abgesehen sind Serialisierung, logische Gleichheit und Thread-Affinität echte Probleme, mit denen sich DepedencyObject-basierte VMs befassen müssen.
Micahtan
"Außerdem fallen für DP höhere Kosten an als für INotifyPropertyChanged" - wo liegt Ihre Beweisquelle dafür? Viele Entwickler machen diese Behauptung ohne Beweise dafür. Laut MSDN ist das nicht wahr. "Versuchen Sie, UI und ViewModel vollständig getrennt zu gestalten, damit wir jederzeit die Layout- und UI-Steuerelemente ändern können" - auch dies hat absolut nichts mit POCO + PropChange im Vergleich zu DO / DP zu tun. Wenn überhaupt, verbessert die Reflection and Path-Registrierung in DO / DP Ihre Fähigkeit, auf der visuellen Seite zu arbeiten.
Tpartee
20

Vom Standpunkt der Ausdruckskraft aus genieße ich es sehr, Abhängigkeitseigenschaften zu verwenden und bei dem Gedanken daran zusammenzucken INotifyPropertyChanged. Abgesehen von den stringEigenschaftsnamen und möglichen Speicherverlusten aufgrund von Ereignisabonnements INotifyPropertyChangedist dies ein viel expliziterer Mechanismus.

Abhängigkeitseigenschaften implizieren "wenn dies der Fall ist" unter Verwendung leicht verständlicher statischer Metadaten. Es ist ein deklarativer Ansatz, der meine Stimme für Eleganz erhält.

Bryan Watts
quelle
1
Der String-Teil hat jetzt eine Lösung mit dem Operator nameof.
Newtopian
@ Newtopian: Stimmt. Es sind auch einige interessante Dinge möglich [CallerMemberName].
Bryan Watts
Ganz zu schweigen von den zahlreichen Vorteilen der Immobilienregistrierung (Reflection) in WPF und CLR bei Verwendung eines DO / DP-Modells im Vergleich zu einem POCO.
tpartee
16

INotifyPropertyChanged Bei Verwendung haben Sie auch die Möglichkeit, dem Code Ihrer Getter und dem Setter Ihrer Eigenschaften mehr Logik hinzuzufügen.

DependencyProperty Beispiel:

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

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

In Ihrem Getter und Setter - alles, was Sie tun können, ist einfach SetValue bzw. GetValue aufzurufen. In anderen Teilen des Frameworks wird der Getter / Setter nicht aufgerufen, sondern er ruft direkt SetValue, GetValue auf, sodass Ihre Eigenschaftslogik dies nicht tun würde zuverlässig ausgeführt werden.

Mit INotifyPropertyChangeddefiniert ein Ereignis:

public event PropertyChangedEventHandler PropertyChanged;

Und dann haben Sie einfach irgendwo in Ihrem Code eine Logik und rufen dann auf:

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

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

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

Dies kann in einem Getter / Setter oder irgendwo anders sein.

Adam
quelle
11
Sie können Änderungsbenachrichtigungen auch von DependencyProperties erhalten. Siehe PropertyMetadata.PropertyChangedCallback. Beispiel unter: msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White
2
Sie können SetValue auch von überall aus anrufen, nicht nur von der Unterkunft aus
aL3891
Dies ist irreführend und unwahr - es gibt mehrere Möglichkeiten, sich an Änderungsereignisse auf einem DP zu binden, selbst wenn dieser "intern" geändert wird. Einer von ihnen wurde oben von Joe White
tpartee
16

Abhängigkeitseigenschaften sollen die Bindung (als Ziel) an UI-Elemente unterstützen, nicht als Quelle für die Datenbindung. Hier kommt INotifyProperty ins Spiel. Aus reiner Sicht sollten Sie DP nicht in ViewModels verwenden.

"Um die Quelle einer Bindung zu sein, muss eine Eigenschaft keine Abhängigkeitseigenschaft sein. Sie können jede CLR-Eigenschaft als Bindungsquelle verwenden. Um jedoch das Ziel einer Bindung zu sein, muss die Eigenschaft a sein Abhängigkeitseigenschaft: Damit eine Einweg- oder Zweiwegbindung wirksam ist, muss die Quelleigenschaft Änderungsbenachrichtigungen unterstützen, die an das Bindungssystem und damit an das Ziel weitergegeben werden. Für benutzerdefinierte CLR-Bindungsquellen bedeutet dies, dass die Eigenschaft INotifyPropertyChanged unterstützen muss. Sammlungen sollten INotifyCollectionChanged unterstützen. "

Alle Abhängigkeitsobjekte können nicht serialisiert werden (dies kann die Verwendung von ViewModels und DTOs (POCOs) behindern.

Es gibt Unterschiede zwischen DP in Silverlight und WPF.

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

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

Nick Hermans
quelle
Ich verwende seit 2009 serialisierte Abhängigkeitsobjekte ohne Probleme. Sie sind sich also nicht sicher, wovon Sie sprechen, wenn Sie sagen "Alle Abhängigkeitsobjekte können nicht serialisiert werden" - ja, das können sie. Tatsächlich gibt es viele Optionen: codeproject.com/Articles/61440/… emphess.net/2008/11/25/dependencyproperty-serialization Und einer meiner persönlichen Favoriten : Stellen Sie einfach Backing-Stores für alle Ihre DPs bereit und machen Sie diese serialisierbar ( In 2 Minuten Suche bei Google waren keine guten einfachen Beispiele verfügbar, aber ich versichere Ihnen, dass dies funktioniert.
Tpartee
7

Auch ich musste diese Entscheidung kürzlich in Betracht ziehen.

Ich stellte fest, dass der INotifyPropertyChanged-Mechanismus meinen Anforderungen besser entsprach, da ich meine GUI auf ein vorhandenes Geschäftslogik-Framework kleben konnte, ohne den Status zu duplizieren. Das von mir verwendete Framework hatte ein eigenes Beobachtermuster und es war einfach, eine Benachrichtigungsebene an die nächste weiterzuleiten. Ich hatte einfach eine Klasse, die die Beobachterschnittstelle aus meinem Geschäftslogik-Framework und der INotifyPropertyChanged-Schnittstelle implementierte.

Mit DP können Sie das Backend, in dem der Status gespeichert ist, nicht selbst definieren. Ich hätte .net eine Kopie jedes Statuselements zwischenspeichern lassen müssen, an das ich gebunden war. Dies schien ein unnötiger Aufwand zu sein - mein Zustand ist groß und kompliziert.

Hier fand ich INotifyPropertyChanged besser, um Eigenschaften aus der Geschäftslogik für die GUI verfügbar zu machen.

Davon abgesehen erwies sich DP als einfache Lösung, wenn ich ein benutzerdefiniertes GUI-Widget benötigte, um eine Eigenschaft verfügbar zu machen, und wenn Änderungen an dieser Eigenschaft Auswirkungen auf andere GUI-Widgets hatten.

Dort fand ich DP nützlich für die Benachrichtigung von GUI zu GUI.

morechilli
quelle
6

Ist es wirklich eine gute Idee, WPF die ViewModel-Abhängigkeiten zu geben?

.NET 4.0 verfügt über System.Xaml.dll, sodass Sie nicht von einem beliebigen Framework abhängig sein müssen, um es zu verwenden. Siehe Rob Relyeas Beitrag über seine PDC-Sitzung.

Meine Einstellung

XAML ist eine Sprache zur Beschreibung von Objekten, und WPF ist ein Framework, dessen beschriebene Objekte UI-Elemente sind.

Ihre Beziehung ähnelt C #, einer Sprache zur Beschreibung von Logik, und .NET, einem Framework, das bestimmte Arten von Logik implementiert.

Der Zweck von XAML sind deklarative Objektgraphen. Die W * F-Technologien sind großartige Kandidaten für dieses Paradigma, aber XAML existiert unabhängig von ihnen.

XAML und das gesamte Abhängigkeitssystem wurden als separate Stapel für WF und WPF implementiert, wahrscheinlich um die Erfahrung verschiedener Teams zu nutzen, ohne eine Abhängigkeit (kein Wortspiel beabsichtigt) zwischen ihnen zu schaffen.

Bryan Watts
quelle
Wenn Sie antworten, scheinen Sie davon auszugehen, dass Bitbonk XAML und WPF als gleich betrachtet. ViewModels sollten so wenig WPF-Abhängigkeiten wie möglich aufweisen, um nicht die logische Trennung zu erhöhen, sondern um die Codekomplexität zu verringern und alle Probleme zu vermeiden, die mit dem einfachen Schreiben von Logik in den Code-Behind eines Benutzersteuerelements verbunden sind. Sie werden unweigerlich WPF-Konzepte wie ICommand implementieren und Verhaltensweisen präsentieren, die nur von WPF / Silverlight problemlos umbrochen werden können. Ihre einzigen Bedenken hinsichtlich des Threadings von Präsentationen in einem Ansichtsmodell sollten CollectionViews und ObservableCollection sein.
Gusdor
6

Abhängigkeitseigenschaften sind der Klebstoff für die Erstellung benutzerdefinierter Steuerelemente. Wenn Sie Intelli-sense verwenden möchten, um Ihre Eigenschaften zur XAML-Entwurfszeit im Eigenschaftenfenster anzuzeigen, müssen Sie Abhängigkeitseigenschaften verwenden. INPC zeigt zur Entwurfszeit niemals eine Eigenschaft im Eigenschaftenfenster an.

John Peters
quelle
4

Es scheint, dass Abhängigkeitseigenschaften in Steuerelementen verwendet werden sollten, die Sie erstellen, z. B. Schaltflächen. Um Eigenschaften in XAML zu verwenden und alle WPF-Funktionen zu verwenden, müssen diese Eigenschaften Abhängigkeitseigenschaften sein.

Ihr ViewModel ist jedoch besser dran, wenn Sie INotifyPropertyChanged verwenden. Wenn Sie INotifyPropertyChanged verwenden, können Sie bei Bedarf über eine Getter- / Setter-Logik verfügen.

Ich empfehle, Josh Smiths Version einer Basisklasse auf ein ViewModel zu überprüfen, das bereits INotifyPropertyChanged implementiert:

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

Ich denke, dies ist ein hervorragendes Beispiel für die Erstellung eines ViewModel.

timothymcgrath
quelle
4

Ich denke, DependencyProperty und INotifyPropertyChanged werden für zwei verschiedene Dinge in Binding verwendet: die erste, um eine Eigenschaft als Ziel einer Bindung zu aktivieren und die Eingabe von einer anderen Eigenschaft zu erhalten (verwenden Sie {Binding ...}, um die Eigenschaft festzulegen), die letzte wenn Sie möchten, dass der Wert einer Eigenschaft als Quelle einer Bindung verwendet wird (Name im Bindungspfadausdruck). Die Wahl ist also nur technisch.

Domnik
quelle
2
In beiden Fällen kann ein INotifyPropertyChanged verwendet werden. Sie können TwoWay daran binden. Eine DependencyProperty ist aus technischen Gründen nur für einige Aktionen erforderlich, die für ein View-Objekt ausgeführt werden (z. B. Festlegen einiger Eigenschaften beim Instanziieren eines View-Objekts in XAML). Eine DependencyProperty ist für ein ViewModel niemals erforderlich.
Oillio
3

Ich bevorzuge einen direkteren Ansatz, über den ich in Presentation Model Without INotifyPropertyChanged gebloggt habe . Mithilfe einer Alternative zur Datenbindung können Sie ohne Buchhaltungscode direkt an CLR-Eigenschaften binden. Sie schreiben einfach alten .NET-Code in Ihr Ansichtsmodell und dieser wird aktualisiert, wenn sich Ihr Datenmodell ändert.

Michael L Perry
quelle
Ohne INotifyPropertyChanged, PropertyDescriptorverwendet werden, was bewirkt , dass Speicherlecks
Tilak
Die Update Controls-Bibliothek, die ich in diesem Blogbeitrag präsentiere, verwendet schwache Referenzen und keine Eigenschaftsbeschreibungen. Es geht kein Speicher verloren.
Michael L Perry
1
Michael, deine Bibliothek generiert viel Code. Ich sehe keine Vorteile. Ich kann dasselbe erreichen, indem ich einen Modell-Wrapper mit generierten PropertyChanged-Ereignisaufrufen generiere.
Der_Meister
3

Es gibt nur eine Sache, warum Sie eine bevorzugen sollten DependencyObject- Bindung funktioniert besser. Versuchen Sie einfach ein Beispiel mit einem ListBoxund TextBox, füllen Sie die Liste mit Daten aus der INotifyPropertyChangedEigenschaft vs. DependencyPropertyund bearbeiten Sie das aktuelle Element aus TextBox...

Ramos
quelle
1
Hassan Tareq
1

Wenn Sie Eigenschaften anderen Steuerelementen aussetzen möchten, müssen Sie Abhängigkeitseigenschaften verwenden ... Aber viel Glück, denn es dauert eine Weile, bis sie es herausfinden ...

JWP
quelle