Sayangnya tidak ada satu pun aplikasi contoh MVVM yang hebat yang melakukan segalanya, dan ada banyak pendekatan berbeda untuk melakukan sesuatu. Pertama, Anda mungkin ingin membiasakan diri dengan salah satu kerangka kerja aplikasi di luar sana (Prism adalah pilihan yang layak), karena mereka memberi Anda alat yang mudah digunakan seperti injeksi ketergantungan, perintah, agregasi acara, dll. Untuk dengan mudah mencoba berbagai pola yang sesuai dengan Anda .
Rilis prisma:
http://www.codeplex.com/CompositeWPF
Ini termasuk contoh aplikasi yang cukup baik (pedagang saham) bersama dengan banyak contoh yang lebih kecil dan cara melakukannya. Paling tidak itu adalah demonstrasi yang baik dari beberapa sub-pola umum yang digunakan orang untuk membuat MVVM benar-benar berfungsi. Mereka memiliki contoh untuk CRUD dan dialog, saya percaya.
Prisma belum tentu untuk setiap proyek, tetapi merupakan hal yang baik untuk membiasakan diri.
CRUD:
Bagian ini cukup mudah, pengikatan dua arah WPF membuatnya sangat mudah untuk mengedit sebagian besar data. Trik sebenarnya adalah menyediakan model yang membuatnya mudah untuk mengatur UI. Paling tidak Anda ingin memastikan bahwa ViewModel Anda (atau objek bisnis) menerapkan INotifyPropertyChanged
untuk mendukung pengikatan dan Anda dapat mengikat properti langsung ke kontrol UI, tetapi Anda juga mungkin ingin menerapkan IDataErrorInfo
untuk validasi. Biasanya, jika Anda menggunakan semacam solusi ORM mengatur CRUD adalah mudah.
Artikel ini menunjukkan operasi kasar sederhana:
http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx
Itu dibangun di atas LinqToSql, tapi itu tidak relevan dengan contoh - yang penting adalah bahwa objek bisnis Anda diimplementasikan INotifyPropertyChanged
(kelas mana yang dihasilkan oleh LinqToSql lakukan). MVVM bukan poin dari contoh itu, tapi saya pikir itu tidak penting dalam kasus ini.
Artikel ini menunjukkan validasi data
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx
Sekali lagi, sebagian besar solusi ORM menghasilkan kelas yang sudah diimplementasikan IDataErrorInfo
dan biasanya menyediakan mekanisme untuk memudahkan menambahkan aturan validasi khusus.
Sebagian besar waktu Anda dapat mengambil objek (model) yang dibuat oleh beberapa ORM dan membungkusnya dalam ViewModel yang memegangnya dan memerintahkan untuk menyimpan / menghapus - dan Anda siap untuk mengikat UI langsung ke properti model.
Tampilan akan terlihat seperti ini (ViewModel memiliki properti Item
yang menampung model, seperti kelas yang dibuat dalam ORM):
<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>
Dialog:
Dialog dan MVVM agak rumit. Saya lebih suka menggunakan pendekatan Mediator dengan dialog, Anda dapat membaca sedikit lebih banyak tentang hal itu dalam pertanyaan StackOverflow ini:
Contoh dialog WPF MVVM
Pendekatan saya yang biasa, yang tidak cukup klasik MVVM, dapat diringkas sebagai berikut:
Kelas dasar untuk dialog ViewModel yang memaparkan perintah untuk melakukan dan membatalkan tindakan, acara untuk membuat tampilan tahu bahwa dialog siap ditutup, dan apa pun yang Anda butuhkan dalam semua dialog Anda.
Tampilan umum untuk dialog Anda - ini bisa berupa jendela, atau kontrol jenis overlay "modal" khusus. Pada intinya itu adalah presenter konten yang kita masukkan ke viewmodel, dan menangani kabel untuk menutup jendela - misalnya pada perubahan konteks data Anda dapat memeriksa apakah ViewModel baru diwarisi dari kelas dasar Anda, dan jika ya, berlangganan acara tutup yang relevan (pawang akan menetapkan hasil dialog). Jika Anda memberikan fungsionalitas universal tutup alternatif (tombol X, misalnya), Anda harus memastikan untuk menjalankan perintah tutup yang relevan pada ViewModel juga.
Di suatu tempat Anda perlu menyediakan templat data untuk ViewModels Anda, mereka bisa sangat sederhana terutama karena Anda mungkin memiliki tampilan untuk setiap dialog yang dienkapsulasi dalam kontrol terpisah. Templat data default untuk ViewModel kemudian akan terlihat seperti ini:
<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
<views:AddressEditView DataContext="{Binding}" />
</DataTemplate>
Tampilan dialog perlu memiliki akses ke ini, karena jika tidak maka tidak akan tahu cara menampilkan ViewModel, selain dari UI dialog bersama isinya pada dasarnya adalah ini:
<ContentControl Content="{Binding}" />
Templat data implisit akan memetakan tampilan ke model, tetapi siapa yang meluncurkannya?
Ini adalah bagian yang tidak terlalu mvvm. Salah satu cara untuk melakukannya adalah dengan menggunakan acara global. Apa yang saya pikir adalah hal yang lebih baik untuk dilakukan adalah menggunakan pengaturan jenis agregator acara, yang disediakan melalui injeksi ketergantungan - dengan cara ini acara bersifat global ke sebuah wadah, bukan seluruh aplikasi. Prism menggunakan kerangka kerja kesatuan untuk semantik wadah dan injeksi ketergantungan, dan secara keseluruhan saya suka Unity sedikit.
Biasanya, masuk akal untuk jendela root untuk berlangganan acara ini - ia dapat membuka dialog dan mengatur konteks datanya ke ViewModel yang akan diteruskan dengan acara yang dimunculkan.
Pengaturan ini dengan cara ini memungkinkan ViewModels meminta aplikasi untuk membuka dialog dan menanggapi tindakan pengguna di sana tanpa mengetahui apa pun tentang UI sehingga sebagian besar MVVM-ness tetap lengkap.
Namun, ada kalanya UI harus memunculkan dialog, yang dapat membuat segalanya sedikit lebih rumit. Pertimbangkan misalnya, jika posisi dialog tergantung pada lokasi tombol yang membukanya. Dalam hal ini Anda perlu memiliki beberapa info khusus UI ketika Anda meminta dialog terbuka. Saya biasanya membuat kelas terpisah yang menampung ViewModel dan beberapa info UI yang relevan. Sayangnya beberapa kopling tampaknya tidak dapat dihindari di sana.
Kode pseudo dari penangan tombol yang memunculkan dialog yang membutuhkan data posisi elemen:
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
}
Tampilan dialog akan mengikat ke posisi data, dan meneruskan ViewModel yang terkandung ke dalam ContentControl
. ViewModel sendiri masih tidak tahu apa-apa tentang UI.
Secara umum saya tidak menggunakan DialogResult
properti pengembalian ShowDialog()
metode atau mengharapkan utas untuk memblokir sampai dialog ditutup. Dialog modal non-standar tidak selalu bekerja seperti itu, dan dalam lingkungan gabungan Anda sering tidak benar-benar ingin event handler diblokir seperti itu. Saya lebih suka membiarkan ViewModels berurusan dengan ini - pencipta ViewModel dapat berlangganan acara yang relevan, mengatur metode komit / batal, dll, jadi tidak perlu bergantung pada mekanisme UI ini.
Jadi alih-alih aliran ini:
// in code behind
var result = somedialog.ShowDialog();
if (result == ...
Saya menggunakan:
// 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
Saya lebih suka cara ini karena sebagian besar dialog saya adalah kontrol pseudo-modal non-blocking dan melakukannya dengan cara ini tampaknya lebih mudah daripada bekerja di sekitarnya. Mudah untuk unit test juga.