Ne yazık ki, her şeyi yapan harika bir MVVM örnek uygulaması yok ve bir şeyler yapmak için birçok farklı yaklaşım var. İlk olarak, size uygun farklı desenleri kolayca denemek için size bağımlılık enjeksiyonu, komuta, olay toplama, vb.Gibi kullanışlı araçlar sağladığından, uygulama çerçevelerinden birini tanımak isteyebilirsiniz (Prizma iyi bir seçimdir). .
Prizma sürümü:
http://www.codeplex.com/CompositeWPF
Bir sürü küçük örnek ve nasıl yapılır ile birlikte oldukça iyi bir örnek uygulama (borsa tüccar) içerir. En azından insanların MVVM'nin gerçekten çalışması için kullandıkları birkaç ortak alt modelin iyi bir gösterimi. Hem CRUD hem de diyaloglar için örnekleri var.
Prizma her proje için mutlaka gerekli değildir, ancak aşina olmak iyi bir şeydir.
CRUD:
Bu bölüm oldukça kolay, WPF iki yönlü bağlamalar çoğu verinin düzenlenmesini gerçekten kolaylaştırıyor. Gerçek hile UI kurulumunu kolaylaştıran bir model sağlamaktır. En azından ViewModel (veya iş nesnesi) INotifyPropertyChanged
bağlamayı desteklemek için uygulandığından emin olmak istersiniz ve özellikleri doğrudan UI denetimlerine bağlayabilirsiniz, ancak IDataErrorInfo
doğrulama için de uygulamak isteyebilirsiniz . Genellikle, bir çeşit ORM çözümü kullanırsanız CRUD kurmak çok kolaydır.
Bu makalede, basit crud işlemleri gösterilmektedir:
http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx
LinqToSql üzerine inşa edilmiştir, ancak bu örnekle ilgisizdir - önemli olan iş nesnelerinizin INotifyPropertyChanged
(LinqToSql tarafından oluşturulan sınıfların yaptığı) uygulanmasıdır. MVVM bu örneğin amacı değil, ama bu durumda önemli olduğunu düşünmüyorum.
Bu makalede veri doğrulaması gösterilmektedir
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx
Yine, çoğu ORM çözümü zaten uygulanmış IDataErrorInfo
ve tipik olarak özel doğrulama kuralları eklemeyi kolaylaştıran bir mekanizma sağlayan sınıflar oluşturur .
Çoğu zaman bir ORM tarafından oluşturulan bir nesneyi (modeli) alabilir ve onu tutan ve kaydet / sil komutlarını içeren bir ViewModel'e sarabilirsiniz ve kullanıcı arayüzünü doğrudan modelin özelliklerine bağlamaya hazırsınız.
Görünüm şöyle görünecektir (ViewModel, ORM'de Item
oluşturulan bir sınıf gibi modeli tutan bir özelliğe sahiptir):
<StackPanel>
<StackPanel DataContext=Item>
<TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
</StackPanel>
<Button Command="{Binding SaveCommand}" />
<Button Command="{Binding CancelCommand}" />
</StackPanel>
Diyaloglar:
Diyaloglar ve MVVM biraz zor. İletişim kutuları ile arabulucu yaklaşımının bir lezzetini kullanmayı tercih ederim, bu StackOverflow sorusunda biraz daha okuyabilirsiniz:
WPF MVVM iletişim örneği
Oldukça klasik MVVM olmayan her zamanki yaklaşımım şöyle özetlenebilir:
Tamamlama ve iptal eylemleri için komutları gösteren bir iletişim kutusu ViewModel için temel sınıf, görünümün bir iletişim kutusunun kapatılmaya hazır olduğunu bilmesini sağlayan bir olay ve tüm iletişim kutularında ihtiyacınız olan her şey.
Diyalog pencereniz için genel bir görünüm - bu bir pencere veya özel bir "kalıcı" kaplama türü kontrolü olabilir. Kalbinde, viewmodel'i içine döktüğümüz bir içerik sunucusu ve pencereyi kapatmak için kabloları kullanıyor - örneğin veri bağlamında yeni ViewModel'in temel sınıfınızdan miras alınıp alınmadığını kontrol edebilir ve ilgili kapatma etkinliğine abone olun (işleyici iletişim kutusu sonucunu atar). Alternatif evrensel kapatma işlevselliği sağlarsanız (örneğin X düğmesi), ViewModel'de de ilgili kapatma komutunu çalıştırdığınızdan emin olmalısınız.
ViewModels'ınız için veri şablonları sağlamanız gereken bir yerde, özellikle ayrı bir kontrolde kapsüllenmiş her iletişim kutusu için bir görünümünüz olduğundan, bunlar çok basit olabilir. ViewModel için varsayılan veri şablonu aşağıdaki gibi görünecektir:
<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
<views:AddressEditView DataContext="{Binding}" />
</DataTemplate>
İletişim kutusu görünümünün bunlara erişimi olması gerekir, aksi takdirde paylaşılan iletişim arabirimi arayüzünün içeriği temel olarak şudur: Aksi takdirde ViewModel'in nasıl gösterileceğini bilemez:
<ContentControl Content="{Binding}" />
Örtük veri şablonu, görünümü modele eşler, ancak bunu kim başlatır?
Bu öyle değil mvvm kısmı. Bunu yapmanın bir yolu küresel bir olay kullanmaktır. Ne yapmak daha iyi bir şey olduğunu düşünüyorum bağımlılık enjeksiyon yoluyla sağlanan bir olay toplayıcı türü kurulum kullanmaktır - bu şekilde olay tüm uygulama değil, bir kapsayıcı için küreseldir. Prizma kap semantiği ve bağımlılık enjeksiyonu için birlik çerçevesini kullanır ve genel olarak Unity'yi biraz severim.
Genellikle, kök pencerenin bu olaya abone olması mantıklıdır - iletişim kutusunu açabilir ve veri içeriğini yükseltilmiş bir olayla aktarılan ViewModel'e ayarlayabilir.
Bunu bu şekilde ayarlamak, ViewModels'ın uygulamadan kullanıcı arabirimi hakkında hiçbir şey bilmeden bir iletişim kutusu açmasını ve kullanıcı işlemlerine yanıt vermesini ister, böylece çoğunlukla MVVM-ness tamamlanır.
Bununla birlikte, kullanıcı arayüzünün diyalogları yükseltmesi gereken zamanlar vardır, bu da işleri biraz daha zorlaştırabilir. Örneğin, iletişim kutusu konumu onu açan düğmenin konumuna bağlıysa düşünün. Bu durumda, bir iletişim kutusu açılmasını istediğinizde kullanıcı arayüzüne özgü bazı bilgilere sahip olmanız gerekir. Genelde bir ViewModel ve bazı ilgili UI bilgilerini tutan ayrı bir sınıf oluştururum. Ne yazık ki, bazı kaplin kaçınılmaz gibi görünüyor.
Öğe konum verisi gerektiren bir iletişim kutusunu yükselten bir düğme işleyicinin sözde kodu:
ButtonClickHandler(sender, args){
var vm = DataContext as ISomeDialogProvider; // check for null
var ui_vm = new ViewModelContainer();
// assign margin, width, or anything else that your custom dialog might require
...
ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
// raise the dialog show event
}
İletişim kutusu görünümü konum verilerine bağlanır ve içerilen ViewModel'i içe aktarır ContentControl
. ViewModel'in kendisi hala kullanıcı arayüzü hakkında hiçbir şey bilmiyor.
Genelde yöntemin DialogResult
return özelliğini kullanmıyorum ShowDialog()
veya iletişim kutusu kapatılana kadar iş parçacığının engellenmesini beklemiyorum. Standart olmayan bir mod iletişim kutusu her zaman böyle çalışmaz ve kompozit bir ortamda bir olay işleyicinin böyle bir şekilde engellemesini istemezsiniz. ViewModels'ın bununla başa çıkmasına izin vermeyi tercih ediyorum - bir ViewModel'in yaratıcısı ilgili etkinliklerine abone olabilir, taahhüt / iptal yöntemlerini ayarlayabilir vb. Bu yüzden bu kullanıcı arayüzü mekanizmasına güvenmeye gerek yoktur.
Bu akış yerine:
// in code behind
var result = somedialog.ShowDialog();
if (result == ...
Kullanırım:
// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container
Bu şekilde tercih ediyorum çünkü diyaloglarımın çoğu engellemeyen sahte mod kontrolleri ve bu şekilde yapmak, etrafta çalışmaktan daha basit görünüyor. Birim testi de kolaydır.