INotifyPropertyChanged vs. DependencyProperty w ViewModel

353

Podczas implementowania ViewModel w aplikacji WPF o architekturze Model-View-ViewModel wydaje się, że istnieją dwie główne opcje, jak sprawić, by można było powiązać dane. Widziałem implementacje, które używają DependencyPropertywłaściwości, z którymi View będzie wiązał się, i INotifyPropertyChangedzamiast tego widziałem implementację ViewModel .

Moje pytanie brzmi, kiedy powinienem preferować jeden od drugiego? Czy są jakieś różnice w wydajności? Czy naprawdę dobrym pomysłem jest nadanie WPF zależności ViewModel? Co jeszcze muszę wziąć pod uwagę przy podejmowaniu decyzji projektowej?

bitbonk
źródło
11
zobacz stackoverflow.com/questions/1329138/... dla sprawdzonego kompilatora sposobu implementacji INotifyPropertyChanged. Unikanie posiadania nazw właściwości jako magicznego ciągu.
Ian Ringrose
10
Zasadniczo istnieje zasadnicza różnica między właściwością zależności a normalną właściwością w klasie, która implementuje INotifyPropertyChanged. Właściwości zależności mogą być źródłowe lub docelowe w powiązaniu danych, ale normalne właściwości z obsługą INotifyPropertyChanged mogą być używane tylko jako źródło. Dlatego te rozwiązania nie są w pełni zamienne. Infrastruktura powiązania danych wymaga DP jako celu do działania, ale źródło może być normalną właściwością z obsługą INotifyPropertyChanged lub wspólnym DP.
Mostafa Rezaei
4
Zobacz stackoverflow.com/a/10595688/200442, aby zapoznać się ze sposobem implementacji .net 4.5 INotifyPropertyChanged.
Daniel Little
najlepiej wyjaśnione tutaj stackoverflow.com/a/3552550/366064
Bizhan

Odpowiedzi:

214

Kent napisał ciekawy blog na ten temat: Zobacz modele: POCO kontra DependencyObjects .

Krótkie podsumowanie:

  1. Obiekty DependencyObjects nie są oznaczone jako szeregowalne
  2. Klasa DependencyObject przesłania i uszczelnia metody Equals () i GetHashCode ()
  3. Obiekt DependencyObject ma powinowactwo do wątku - można uzyskać do niego dostęp tylko w wątku, w którym został utworzony

Wolę podejście POCO. Klasę podstawową dla PresentationModel (aka ViewModel), która implementuje interfejs INotifyPropertyChanged, można znaleźć tutaj: http://compositeextensions.codeplex.com

jbe
źródło
24
DependencyObject bierze również zależność od bibliotek WPF, podczas gdy POCO nie, pozwalając twoim modelom widoków sterować innym stosem interfejsu użytkownika, w którym WPF nie jest dostępny (Compact Framework, Mono).
codekaizen
26
Jest zatem jasne, że Właściwości Dependecy są budowane wyłącznie dla interfejsu użytkownika, a nie dla warstwy biznesowej.
Andrei Rînea
11
Właściwości zależności wymagają również elementu nadrzędnego DependencyObject. Twój ViewModel naprawdę nie powinien dziedziczyć po DependencyObject.
Gusdor
38

Według przewodnika wydajności WPF, DependencyObjects zdecydowanie działają lepiej niż POCO, które implementują INotifyPropertyChanged:

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

James Ashley
źródło
1
Muszę się z tym zgodzić ;-): blog.lexique-du-net.com/index.php?post/2010/02/24/…
Jonatha ANTOINE
Jeśli wybierzesz .NET Framework w wersji 4, łącze nadal będzie działać. Po prostu nie jest dostępny dla „bieżącej wersji”.
podwójnyYou
Dzięki za zwrócenie na to uwagi, jest wiele skandalicznych dezinformacji twórców, którzy twierdzą, że INotifyPropertyChanged jest szybszy lub wiąże się z mniejszymi kosztami niż DP i jest po prostu bezpodstawny. DP to szybkie, eleganckie i wydajne sposoby strukturalnego definiowania drzewa wirtualnego (danych).
tpartee
W DependencyObjects kryje się ukryte zło. Należy je utworzyć w tym samym wątku, co formanty, które się z nimi wiążą. Oznacza to wątek GUI. Oznacza to, że musisz wysłać kreację do tego wątku. Nie można na przykład załadować i utworzyć tych rzeczy w jakimś wątku w tle z DB. Chyba że wyślesz stworzenie. Szalony.
ed22
28

Wybór jest całkowicie oparty na logice biznesowej i poziomie abstrakcji interfejsu użytkownika. Jeśli nie chcesz dobrej separacji, DP będzie dla ciebie działać.

DependencyProperties będzie mieć zastosowanie głównie na poziomie VisualElements, więc nie będzie dobrym pomysłem, jeśli stworzymy wiele DP dla każdego z naszych wymagań biznesowych. Ponadto DP ma wyższy koszt niż INotifyPropertyChanged. Kiedy projektujesz WPF / Silverlight, spróbuj zaprojektować interfejs użytkownika i ViewModel całkowicie osobno, aby w dowolnym momencie mogliśmy zmienić układ i interfejs użytkownika (na podstawie motywu i stylów)

Zapoznaj się również z tym postem - /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . Link ma wiele odniesień do wzorca Model-View-ViewModel, co jest bardzo istotne w tej dyskusji.

Jobi Joy
źródło
9
Post autorstwa jbe dokładniej odpowiada na różnice. To, że maszyna wirtualna (lub prezenter) dziedziczy po DependencyObject, nie oznacza, że ​​nie można jej nadać stylu lub nie jest logicznie oddzielona od widoku, oznacza tylko, że pamięć dla wartości właściwości jest inna niż jawnie zadeklarowane pola w Styl POCO. Biorąc to pod uwagę, serializacja, logiczna równość i powinowactwo wątków są prawdziwymi problemami, z którymi muszą sobie radzić maszyny wirtualne oparte na DepedencyObject.
micahtan
„Ponadto DP ma wyższy koszt niż INotifyPropertyChanged” - gdzie jest to twoje źródło dowodu? Wielu deweloperów wysuwa to twierdzenie bez żadnych dowodów na poparcie tego. Według MSDN to nieprawda. „spróbuj zaprojektować interfejs użytkownika i ViewModel całkowicie osobno, aby w dowolnym momencie zmienić układ i układ interfejsu” - znowu, nie ma to absolutnie nic wspólnego z POCO + PropChange w porównaniu do DO / DP. Jeśli już, rejestr Reflection and Path w DO / DP poprawia twoją zdolność do pracy po stronie wizualnej.
tpartee
20

Z punktu widzenia ekspresji bardzo lubię używać właściwości zależności i kulić się na samą myśl INotifyPropertyChanged. Oprócz stringnazw właściwości i możliwych wycieków pamięci z powodu subskrypcji zdarzeń, INotifyPropertyChangedmechanizm jest znacznie bardziej wyraźny.

Właściwości zależności oznaczają „kiedy to zrób to”, używając łatwo zrozumiałych metadanych statycznych. To deklaratywne podejście zyskuje mój głos na elegancję.

Bryan Watts
źródło
1
Część strun ma teraz rozwiązanie z operatorem nameof.
Newtopian
@Newtopian: True. Istnieje również kilka interesujących rzeczy [CallerMemberName].
Bryan Watts
Nie wspominając już o bogactwie korzyści związanych z rejestracją nieruchomości (odbicie) w WPF i CLR podczas korzystania z modelu DO / DP w porównaniu z POCO.
tpartee
16

INotifyPropertyChanged gdy jest używany, daje również możliwość dodania większej logiki w kodzie twoich metod pobierających i ustawiających właściwości.

DependencyProperty przykład:

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

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

W swoim narzędziu pobierającym i ustawiającym --- wszystko, co możesz zrobić, to po prostu wywołać odpowiednio SetValue i GetValue, b / c w innych częściach struktury, w których getter / setter nie jest wywoływany, zamiast tego wywołuje bezpośrednio SetValue, GetValue, więc logika właściwości nie niezawodnie być wykonane.

Za pomocą INotifyPropertyChangedzdefiniuj zdarzenie:

public event PropertyChangedEventHandler PropertyChanged;

A potem po prostu umieść dowolną logikę w dowolnym miejscu kodu, a następnie wywołaj:

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

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

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

Może to być getter / setter lub gdziekolwiek indziej.

Adam
źródło
11
Możesz również otrzymywać powiadomienia o zmianach z DependencyProperties. Zobacz PropertyMetadata.PropertyChangedCallback. Przykład: msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White
2
Możesz także wywoływać SetValue z dowolnego miejsca, nie tylko z wnętrza nieruchomości
aL3891
Jest to mylące i nieprawdziwe - istnieje wiele sposobów zaczepienia się o zdarzenia zmiany w DP, nawet jeśli są one zmieniane „wewnętrznie”. Jeden z nich został wskazany powyżej przez Joe White'a
partnerkę
16

Właściwości zależności mają na celu wspieranie wiązania (jako celu) elementów interfejsu użytkownika, nie jako źródła powiązania danych, tutaj przychodzi INotifyProperty. Z czystego punktu widzenia nie należy używać DP w modelach ViewModels.

„Aby być źródłem powiązania, właściwość nie musi być właściwością zależności; można użyć dowolnej właściwości CLR jako źródła powiązania. Jednak aby być celem powiązania, właściwość musi być właściwość zależności. Aby wiązanie jedno- lub dwukierunkowe było skuteczne, właściwość źródłowa musi obsługiwać powiadomienia o zmianach propagowane do systemu powiązań, a tym samym do celu. W przypadku niestandardowych źródeł powiązania CLR oznacza to, że właściwość musi obsługiwać INotifyPropertyChanged. Kolekcje powinny obsługiwać INotifyCollectionChanged. ”

Nie można serializować wszystkich obiektów zależności (może to utrudnić korzystanie z ViewModels i DTO (POCO)).

Istnieją różnice między DP w Silverlight w porównaniu z 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
źródło
Używam serializowanych Obiektów Zależności od 2009 roku bez problemu, więc nie jestem pewien, o czym mówisz, gdy mówisz „Wszystkie obiekty zależności nie mogą być serializowane” - tak, mogą. W rzeczywistości istnieje wiele opcji: codeproject.com/Articles/61440/… emphess.net/2008/11/25/dependencyproperty-serialization I jeden z moich ulubionych: po prostu zapewnij zaplecze dla wszystkich twoich DP i spraw, aby były one możliwe do serializacji ( żadne dobre proste przykłady nie były łatwo dostępne w ciągu 2 minut wyszukiwania w Google, ale zapewniam, że to działa).
tpartee
7

Ja też musiałem ostatnio rozważyć tę decyzję.

Odkryłem, że mechanizm INotifyPropertyChanged lepiej odpowiada moim potrzebom, ponieważ pozwolił mi przykleić GUI do istniejącej struktury logiki biznesowej bez powielania stanu. Framework, którego używałem, miał swój własny wzorzec obserwatora i łatwo było przekazać jeden poziom powiadomienia na następny. Po prostu miałem klasę, która zaimplementowała interfejs obserwatora z mojej struktury logiki biznesowej i interfejsu INotifyPropertyChanged.

W DP nie można zdefiniować backendu, który sam przechowuje stan. Musiałbym pozwolić .net buforować kopię każdego elementu stanu, który był dla mnie wiążący. Wydawało się to niepotrzebnym kosztem ogólnym - mój stan jest duży i skomplikowany.

Więc tutaj znalazłem INotifyPropertyChanged lepiej do eksponowania właściwości z logiki biznesowej na GUI.

Biorąc to pod uwagę, gdy potrzebowałem niestandardowego widżetu GUI do ujawnienia właściwości, a zmiany w tej właściwości wpływały na inne widżety GUI DP okazał się prostym rozwiązaniem.

Więc znalazłem DP przydatny do powiadomienia GUI do GUI.

morechilli
źródło
6

Czy naprawdę dobrym pomysłem jest nadanie WPF zależności ViewModel?

.NET 4.0 będzie miał System.Xaml.dll, więc nie będziesz musiał polegać na dowolnej strukturze, aby z niego korzystać. Zobacz post Roba Relyea na temat jego sesji PDC.

Moje podanie

XAML to język opisujący obiekty, a WPF to framework, którego opisywane obiekty są elementami interfejsu użytkownika.

Ich związek jest podobny do C #, języka opisującego logikę, oraz .NET, frameworku, który implementuje określone rodzaje logiki.

Celem XAML są deklaratywne wykresy obiektowe. Technologie W * F są świetnymi kandydatami do tego paradygmatu, ale XAML istnieje niezależnie od nich.

XAML i cały system zależności zostały zaimplementowane jako osobne stosy dla WF i WPF, prawdopodobnie w celu wykorzystania doświadczenia różnych zespołów bez tworzenia zależności między nimi (bez zamierzonej gry słów).

Bryan Watts
źródło
Odpowiadając, wydaje się, że zakładasz, że bitbonk uważa XAML i WPF za takie same. ViewModels powinien mieć możliwie mało zależności WPF, nie w celu zwiększenia separacji logicznej, ale w celu zmniejszenia złożoności kodu i uniknięcia wszystkich problemów związanych z prostym pisaniem logiki za kodem użytkownika. Nieuchronnie zaimplementujesz koncepcje WPF, takie jak ICommand, i zaprezentujesz zachowanie, które tylko WPF / Silverlight będzie w stanie łatwo zawinąć - jedynymi problemami związanymi z wątkami prezentacji w modelu widoku powinny być CollectionViews i ObservableCollection.
Gusdor,
6

Właściwości zależności są klejnotem tworzenia niestandardowych elementów sterujących. Jeśli jesteś zainteresowany wykorzystaniem Intelli-sense do wyświetlania swoich właściwości w oknie właściwości w czasie projektowania XAML, musisz użyć właściwości zależności. INPC nigdy nie pokaże właściwości w oknie właściwości w czasie projektowania.

John Peters
źródło
4

Wygląda na to, że właściwości zależności powinny być używane w tworzonych kontrolkach, takich jak przyciski. Aby użyć właściwości w XAML i użyć wszystkich funkcji WPF, te właściwości muszą mieć właściwości zależności.

Lepiej jednak użyć ViewModel przy użyciu INotifyPropertyChanged. Korzystanie z INotifyPropertyChanged da ci możliwość posiadania logiki getter / setter, jeśli zajdzie taka potrzeba.

Polecam sprawdzenie wersji podstawowej klasy Josha Smitha dla modelu ViewModel, który już implementuje INotifyPropertyChanged:

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

Myślę, że jest to doskonały przykład tego, jak zrobić ViewModel.

timothymcgrath
źródło
4

Myślę, że DependencyProperty i INotifyPropertyChanged są używane do dwóch różnych rzeczy w Binding: po pierwsze, aby umożliwić właściwość jako cel wiązania i otrzymać dane wejściowe z innej właściwości (użyj {Binding ...}, aby ustawić właściwość), ostatni gdy chcesz, aby wartość właściwości była używana jako źródło wiązania (nazwa w wyrażeniu ścieżki wiązania). Zatem wybór jest wyłącznie techniczny.

Domnik
źródło
2
W obu przypadkach można użyć INotifyPropertyChanged. Możesz powiązać z nią TwoWay. DependencyProperty jest wymagana ze względów technicznych tylko w przypadku niektórych akcji wykonywanych na obiekcie View (na przykład ustawianie niektórych właściwości podczas tworzenia wystąpienia obiektu View w XAML). DependencyProperty nigdy nie jest wymagana dla ViewModel.
oillio
3

Wolę bardziej bezpośrednie podejście, o którym pisałem w blogu w Modelu prezentacji bez INotifyPropertyChanged . Korzystając z alternatywy dla wiązania danych, można powiązać bezpośrednio z właściwościami CLR bez żadnego kodu księgowego. Po prostu piszesz zwykły kod .NET w swoim Modelu widoku i jest on aktualizowany, gdy zmienia się Twój model danych.

Michael L. Perry
źródło
Używane są bez INotifyPropertyChanged, PropertyDescriptorco powoduje wycieki pamięci
Tilak
Biblioteka Update Controls, którą przedstawiam w tym poście na blogu, używa słabych referencji, a nie deskryptorów właściwości. Nie przecieka pamięć.
Michael L Perry
1
Michael, twoja biblioteka generuje dużo kodu. Nie widzę korzyści. Mogę to osiągnąć, generując opakowanie modelu z wygenerowanymi wywołaniami zdarzeń PropertyChanged.
Der_Meister
3

Jest tylko jedna rzecz, dlaczego preferować DependencyObject- Wiązanie będzie działać lepiej. Po prostu spróbuj przykładu z ListBoxi TextBox, wypełnij listę danymi z INotifyPropertyChangedwłaściwości vs. DependencyPropertyi edytuj bieżący element z TextBox...

ramos
źródło
1
Próbka kodu, proszę
Hassan Tareq
1

Jeśli chcesz udostępnić właściwości innym kontrolkom, musisz użyć właściwości Zależności ... Powodzenia, ponieważ ich znalezienie zajmuje trochę czasu ...

JWP
źródło