ViewModel'de INotifyPropertyChanged ve DependencyProperty


353

ViewModel'i bir Model-View-ViewModel mimarisi WPF uygulamasında uygularken, onu nasıl veritabanlanabilir hale getirebileceğimiz iki ana seçenek var gibi görünüyor. DependencyPropertyGörünüm'ün bağlanacağı özellikler için kullanılan uygulamalar gördüm ve INotifyPropertyChangedbunun yerine ViewModel uygulamasını gördüm .

Sorum şu: Birini diğerine ne zaman tercih etmeliyim? Performans farklılıkları var mı? ViewModel bağımlılıklarını WPF'ye vermek gerçekten iyi bir fikir mi? Tasarım kararı verirken nelere dikkat etmeliyim?


11
derleyici tarafından kontrol edilen INotifyPropertyChanged uygulamasını uygulamak için stackoverflow.com/questions/1329138/… adresine bakın . Özellik adlarını sihirli bir dize olarak kullanmaktan kaçınmak.
Ian Ringrose

10
Genellikle INotifyPropertyChanged uygulayan bir sınıfta bağımlılık özelliği ile normal özellik arasında büyük bir fark vardır. Bağımlılık özellikleri veri bağlamada kaynak veya hedef olabilir, ancak INotifyPropertyChanged destekli normal özellikler yalnızca kaynak olarak kullanılabilir. Dolayısıyla bu çözümler tamamen değiştirilemez. Veri bağlama altyapısı, hedefin çalışması için bir DP gerektirir, ancak kaynak INotifyPropertyChanged desteği ile normal bir özellik veya ortak bir DP olabilir.
Mostafa Rezaei

4
.Net 4.5 uygulama yöntemi için stackoverflow.com/a/10595688/200442 adresine bakın INotifyPropertyChanged.
Daniel Little

en iyi burada açıklanır stackoverflow.com/a/3552550/366064
Bizhan

Yanıtlar:


214

Kent bu konu hakkında ilginç bir blog yazdı: Modelleri Görüntüle: POCO'lar ve DependencyObjects .

Kısa özet:

  1. DependencyObjects serileştirilebilir olarak işaretlenmedi
  2. DependencyObject sınıfı, Equals () ve GetHashCode () yöntemlerini geçersiz kılar ve mühürler
  3. Bir DependencyObject öğesinin iş parçacığı yakınlığı vardır - bu öğeye yalnızca oluşturulduğu iş parçacığında erişilebilir

POCO yaklaşımını tercih ediyorum. PresentationModel (diğer adıyla ViewModel) için INotifyPropertyChanged arabirimini uygulayan bir temel sınıf burada bulunabilir: http://compositeextensions.codeplex.com


24
DependencyObject, WPF kitaplıklarına da bağımlıdır, ancak POCO bunu yapmaz, görünüm modellerinizin WPF'nin kullanılamadığı başka bir UI yığınını kullanmasına izin verir (Compact Framework, Mono).
codekaizen

26
O zaman, Dependecy Properties'in iş katmanı için değil sadece kullanıcı arayüzü için oluşturulduğu açıktır.
Andrei Rînea

11
Bağımlılık Özellikleri ayrıca bir DependencyObject üst öğesi gerektirir. ViewModel cihazınız gerçekten DependencyObject öğesinden miras almamalıdır.
Gusdor

38

WPF performans kılavuzuna göre, DependencyObjects kesinlikle INotifyPropertyChanged uygulayan POCO'lardan daha iyi performans gösterir:

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



.NET Framework sürüm 4'ü seçerseniz, bağlantı yine de çalışır. "Mevcut sürüm" için mevcut değildir.
doubleYou

Bunu işaret ettiğiniz için teşekkürler, INTifyPropertyChanged'in DP'lerden daha hızlı veya daha az ek yüke sahip olduğu konusunda müstehcen iddialarda bulunan devasa skandal yanlış bilgileri var ve sadece temelsiz. DP'ler, sanal (veri) ağacı yapısal olarak tanımlamak için hızlı, zarif ve güçlü yollardır.
tpartee

DependencyObjects için gizli bir kötülük var. Kendilerine bağlanan denetimlerle aynı iş parçacığında oluşturulmaları gerekir. GUI iş parçacığı demektir. Bu, oluşturmayı bu iş parçacığına göndermeniz gerektiği anlamına gelir. Örneğin DB'den bazı arka plan iş parçacığında bu şeyleri yükleyip oluşturamazsınız. Yaratılış göndermediğiniz sürece. Çılgın.
ed22

28

Seçim tamamen iş mantığınıza ve kullanıcı arayüzü soyutlama seviyenize dayanır. İyi bir ayrılma istemiyorsanız DP sizin için çalışacaktır.

DependencyProperties temel olarak VisualElements düzeyinde uygulanacaktır, bu nedenle her bir iş gereksinimimiz için çok sayıda DP oluşturmamız iyi bir fikir olmayacaktır. Ayrıca DP için INotifyPropertyChanged'den daha yüksek bir maliyet vardır. Bir WPF / Silverlight tasarladığınızda UI ve ViewModel'i tamamen ayrı tasarlamaya çalışın, böylece istediğiniz zaman Düzen ve UI denetimlerini değiştirebiliriz (temaya ve stillere dayalı olarak)

Bu yazıyı da inceleyin - /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . Bağlantının, bu tartışmayla çok ilgili olan Model-View-ViewModel deseni için çok fazla referansı vardır.


9
Jbe tarafından gönderilen mesaj farklılıkları daha doğru bir şekilde cevaplıyor. Bir VM'nin (veya Presenter) DependencyObject öğesinden miras alması, stilleştirilemeyeceği veya Görünüm'den mantıksal olarak ayrı olmadığı anlamına gelmez, yalnızca özellik değerlerinin depolamasının, alandaki açıkça bildirilen alanlardan farklı olduğu anlamına gelir. POCO tarzı. Bununla birlikte, serileştirme, mantıksal eşitlik ve iş parçacığı yakınlığı, DepedencyObject tabanlı VM'lerin ele alması gereken gerçek sorunlardır.
micahtan

"Ayrıca DP için bir INotifyPropertyChanged'den daha büyük bir maliyet var" - bunun kanıtı kaynağınız nerede? Birçok geliştirici, bu iddiayı destekleyecek hiçbir kanıt olmadan yapar. MSDN'ye göre bu doğru değil. "UI ve ViewModel'i tamamen ayrı tasarlamaya çalışın, böylece her zaman Düzen ve UI denetimlerini değiştirebiliriz" - yine, bunun DO / DP'ye karşı POCO + PropChange ile kesinlikle bir ilgisi yoktur. Bir şey varsa, DO / DP'deki Yansıma ve Yol kayıt defteri görsel tarafta çalışma yeteneğinizi geliştirir.
tpartee

20

Etkileyici bir bakış açısından, bağımlılık özelliklerini ve cringe'yi düşünmekten iyice zevk alıyorum INotifyPropertyChanged. stringEtkinlik aboneliği nedeniyle özellik adları ve olası bellek sızıntıları dışında INotifyPropertyChangedçok daha açık bir mekanizmadır.

Bağımlılık özellikleri, kolayca anlaşılabilen statik meta verileri kullanarak "bunu yaptığınızda" anlamına gelir. Zarafet için oyumu alan açıklayıcı bir yaklaşım.


1
Dize parçasının şimdi nameof operatörü ile bir çözümü var.
Newtopian

@Newtopian: Doğru. Bazı ilginç şeyler de var [CallerMemberName].
Bryan Watts

Bir POCO'ya karşı bir DO / DP modeli kullanırken WPF ve CLR'deki Mülk Kaydı (Yansıma) avantajlarından bahsetmiyoruz.
tpartee

16

INotifyPropertyChanged Ayrıca kullanıldığında, alıcılarınızın koduna ve mülklerinizin ayarlayıcısına daha fazla mantık ekleme olanağı sağlar.

DependencyProperty misal:

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

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

Alıcı ve ayarlayıcınızda --- yapabileceğiniz tek şey sırasıyla SetValue ve GetValue'yu çağırmaktır. güvenilir bir şekilde yürütülür.

İle INotifyPropertyChangedbir etkinlik tanımlayın:

public event PropertyChangedEventHandler PropertyChanged;

Ve sonra kodunuzda herhangi bir yerde bir mantık olsun, sonra arayın:

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

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

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

Bu bir alıcı / ayarlayıcıda veya başka bir yerde olabilir.


11
Değişiklik bildirimlerini DependencyProperties sitesinden de alabilirsiniz. Bkz. PropertyMetadata.PropertyChangedCallback. Örnek: msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White

2
Ayrıca, SetValue'yu sadece tesis içinden değil, her yerden de arayabilirsiniz
aL3891

Bu yanıltıcı ve yanlıştır - 'dahili olarak' değiştirilse bile DP'deki değişiklik olaylarına bağlanmanın birçok yolu vardır. Bunlardan biri yukarıda Joe White tarafından işaret edildi
tpartee

16

Bağımlılık özellikleri, veri bağlamaya kaynak olmayan UI öğelerinde bağlanmayı (hedef olarak) desteklemeyi amaçlamaktadır, bu noktada INotifyProperty devreye girer. Saf bir bakış açısından bir ViewModels üzerinde DP kullanmamalısınız.

"Bir bağlamanın kaynağı olabilmek için, bir özelliğin bağımlılık özelliği olması gerekmez; bağlayıcı bir kaynak olarak herhangi bir CLR özelliğini kullanabilirsiniz. Ancak, bir bağlamanın hedefi olabilmesi için, özelliğin bir Tek yönlü veya iki yönlü bağlamanın etkili olması için, kaynak özelliğinin bağlayıcı sisteme ve dolayısıyla hedefe yayılan değişiklik bildirimlerini desteklemesi gerekir Özel CLR bağlama kaynakları için, özelliğin INotifyPropertyChanged'i desteklemesi gerektiği anlamına gelir. Koleksiyonlar INotifyCollectionChanged'i desteklemelidir. "

Tüm bağımlılık nesneleri serileştirilemez (Bu, ViewModels ve DTO (POCO) 'lerin kullanımını engelleyebilir.

Silverlight içinde DP arasında WPF'ye göre farklılıklar vardır.

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

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


Ben 2009 yılından bu yana serileştirilmiş Bağımlılık Nesneleri kullanıyorum, bu yüzden "Tüm bağımlılık nesneleri serileştirilemez" derken ne hakkında konuştuğunuzdan emin değilim - evet yapabilirler. Aslında birçok seçenek var: codeproject.com/Articles/61440/… emphess.net/2008/11/25/dependencyproperty-serialization Ve kişisel favorilerimden biri : tüm DP'leriniz için destek depoları sağlayın ve bunları serileştirilebilir hale getirin ( Google'da arama yaptıktan sonra 2 dakika içinde iyi bir basit örnek mevcut değildi, ancak bunun işe yaradığını garanti ediyorum).
tpartee

7

Son zamanlarda bu kararı da değerlendirmek zorunda kaldım.

INotifyPropertyChanged mekanizmasının ihtiyaçlarımı daha uygun bulduğunu çünkü GUI'mi mevcut bir iş mantığı çerçevesine durumu çoğaltmadan yapıştırmamı sağladı. Kullandığım çerçevenin kendi gözlemci modeli vardı ve bir bildirim düzeyini diğerine iletmek kolaydı. Sadece iş mantık çerçevemden ve INotifyPropertyChanged arabiriminden gözlemci arabirimini uygulayan bir sınıfım vardı.

DP ile devleti kendiniz saklayan arka ucu tanımlayamazsınız. .Net'in bağlayıcı olduğum her durumun bir kopyasını önbelleğe almasına izin vermek zorunda kalıyordum. Bu gereksiz bir ek yük gibi görünüyordu - durumum büyük ve karmaşık.

Bu yüzden burada iş mantığından GUI'ye özellikler göstermek için INotifyPropertyChanged daha iyi buldum.

Ben bir özellik göstermek için özel bir GUI widget gerekli ve bu özelliği DP diğer GUI widget'ları etkilemek için değişiklikler için basit bir çözüm olduğunu söyledi.

Bu yüzden orada DP GUI GUI bildirimi için yararlı buldum.


6

ViewModel bağımlılıklarını WPF'ye vermek gerçekten iyi bir fikir mi?

.NET 4.0'da System.Xaml.dll olacaktır, bu nedenle onu kullanmak için rasgele bir çerçeveye bağımlı olmak zorunda kalmazsınız. Rob Relyea'nın PDC oturumu hakkındaki yazısına bakın .

Benim almam

XAML, nesneleri tanımlamak için kullanılan bir dildir ve WPF, açıklanan nesneleri UI öğeleri olan bir çerçevedir.

İlişkileri mantığı tanımlamak için bir dil olan C # ve belirli mantık türlerini uygulayan bir çerçeve olan .NET'e benzer.

XAML'nin amacı bildirici nesne grafikleri. W * F teknolojileri bu paradigma için büyük adaylardır, ancak XAML bunlardan bağımsız olarak mevcuttur.

XAML ve tüm bağımlılık sistemi WF ve WPF için ayrı ayrı yığınlar olarak uygulandı, muhtemelen aralarında bir bağımlılık yaratmadan (cinayet istenmiyor) farklı takımların deneyimlerinden yararlanmak için.


Cevap vererek, bitbonk'un XAML ve WPF'nin aynı olduğunu varsaydığı anlaşılıyor. ViewModels, mantıksal ayrımı artırmak için değil, kod karmaşıklığını azaltmak ve bir kullanıcı denetiminin arkasındaki kodda mantık yazmakla ilişkili tüm sorunlardan kaçınmak için mümkün olduğunca az WPF bağımlılığına sahip olmalıdır. Kaçınılmaz olarak ICommand gibi WPF kavramlarını uygulayacak ve sadece WPF / Silverlight'ın kolayca sarılabileceği davranışı sunacaksınız - bir görünüm modelindeki tek sunum iş parçacığına ilişkin endişeleriniz CollectionViews ve ObservableCollection olmalıdır.
Gusdor

6

Bağımlılık özellikleri, özel kontrol oluşturma tutkusudur. XAML tasarım zamanında özellikler penceresinde özelliklerinizi göstermek için Intelli-sense kullanmak istiyorsanız, Bağımlılık özelliklerini kullanmalısınız. INPC tasarım penceresinde hiçbir zaman özellik penceresinde bir özellik göstermez.


4

Bağımlılık Özellikleri, Düğmeler gibi oluşturduğunuz denetimlerde kullanılmalıdır. XAML'deki özellikleri ve tüm WPF özelliklerini kullanmak için, bu özelliklerin Bağımlılık Özellikleri olması gerekir.

Ancak, ViewModel INotifyPropertyChanged kullanarak daha iyidir. INotifyPropertyChanged kullanmak, ihtiyacınız olduğunda getter / setter mantığına sahip olmanızı sağlar.

Zaten INotifyPropertyChanged uygulayan bir ViewModel için Josh Smith'in temel sınıf sürümünü kontrol etmenizi öneririz:

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

Bu bir ViewModel yapmak için mükemmel bir örnek olduğunu düşünüyorum.


4

Bağımlılık içinde iki farklı şey için kullanılan DependencyProperty ve INotifyPropertyChanged düşünüyorum: ilk bir özellik bir bağlayıcı hedef olmak ve başka bir özellik (özelliği ayarlamak için {Binding ...} kullanın) giriş almak için etkinleştirmek için, son bir özelliğin değerinin bağlamanın kaynağı olarak kullanılmasını istediğinizde (Bağlama Yolu İfadesinde ad). Yani seçim sadece teknik.


2
Her iki durumda da bir INotifyPropertyChanged kullanılabilir. TwoWay'i ona bağlayabilirsiniz. Bir DependencyProperty, yalnızca bir View nesnesi üzerinde gerçekleştirilen bazı eylemler için teknik nedenlerle gereklidir (örneğin, XAML'de bir View nesnesini başlatırken bazı özellikleri ayarlama). Bir ViewModel için asla bir Bağımlılık Özelliği gerekli değildir.
oillio

3

INotifyPropertyChanged Olmadan Sunum Modeli'nde blog yazdığım daha doğrudan bir yaklaşımı tercih ediyorum . Veri bağlama alternatifi kullanarak, herhangi bir defter tutma kodu olmadan doğrudan CLR özelliklerine bağlanabilirsiniz. Görünüm Modelinize yalnızca eski .NET kod yazıyorsunuz ve Veri Modeliniz değiştiğinde güncelleniyor.


Olmadan INotifyPropertyChanged, PropertyDescriptorneden olan kullanıldığında bellek sızıntılarını
Tilak

Bu blog gönderisinde sunduğum Güncelleştirme Denetimleri kitaplığı, özellik tanımlayıcıları değil zayıf başvurular kullanıyor. Hafıza sızdırmaz.
Michael L Perry

1
Michael, kütüphaneniz çok fazla kod üretir. Fayda görmüyorum. Ben üretilen PropertyChanged olay çağrıları ile model sarıcı üreterek aynı elde edebilirsiniz.
Der_Meister

3

Tercih DependencyObjectetmenin tek bir nedeni vardır: - Ciltleme daha iyi çalışır. Sadece ListBoxve ile bir örnek deneyin TextBox, INotifyPropertyChangedözellik vs.'den veri içeren bir listeyi doldurun DependencyPropertyve geçerli öğeyi TextBox...


1
Kod örneği, lütfen
Hassan Tareq

1

Özellikleri diğer denetimlere göstermek istiyorsanız, Bağımlılık özelliklerini kullanmanız gerekir ... Ama iyi şanslar çünkü anlaması biraz zaman alır ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.