Contoh bagus Template MVVM

141

Saat ini saya sedang bekerja dengan template Microsoft MVVM dan menemukan kurangnya contoh detail yang membuat frustrasi. Contoh ContactBook yang disertakan menunjukkan sedikit penanganan Komando dan satu-satunya contoh lain yang saya temukan adalah dari artikel Majalah MSDN di mana konsepnya mirip tetapi menggunakan pendekatan yang sedikit berbeda dan masih kurang dalam kompleksitas. Apakah ada contoh MVVM yang layak yang setidaknya menunjukkan operasi CRUD dasar dan pengalihan dialog / konten?


Saran semua orang sangat berguna dan saya akan mulai menyusun daftar sumber daya yang baik

Kerangka / Template

Artikel yang Berguna

Screencasts

Perpustakaan tambahan

Jagwar
sumber
Saya senang bahwa sumber daya ini telah membantu. Saat ini saya sedang dalam aplikasi MVVM produksi kedua saya dan akan terus menambahkan konten yang akan membantu bagi mereka yang memulai ketika saya menjumpainya.
jwarzech

Jawaban:

59

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 INotifyPropertyChangeduntuk mendukung pengikatan dan Anda dapat mengikat properti langsung ke kontrol UI, tetapi Anda juga mungkin ingin menerapkan IDataErrorInfountuk 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 IDataErrorInfodan 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 Itemyang 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 DialogResultproperti 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.

Egor
sumber
Terima kasih atas jawaban terincinya! Baru-baru ini saya menemukan bahwa masalah terbesar saya adalah ketika saya perlu memiliki MainViewModel berkomunikasi dengan model tampilan lain untuk menangani aliran aplikasi. Namun tampaknya MVVM + Mediator tampaknya menjadi pendekatan yang populer.
jwarzech
2
Mediator pasti membantu, pola agregator acara (Prism memiliki implementasi yang baik) juga sangat membantu ketika kopling rendah adalah tujuan. Juga, model tampilan utama Anda biasanya memiliki model view anak sendiri dan seharusnya tidak memiliki masalah berkomunikasi dengan mereka. Anda perlu menggunakan mediator atau / dan agregator acara ketika model tampilan anak Anda perlu berinteraksi dengan modul lain di aplikasi Anda yang belum tentu mereka ketahui - termasuk UI (contoh dialog saya adalah tentang kasus khusus ini).
Egor
1
Pedoman untuk bekerja dengan dialog dan windows sangat membantu. Namun, saya terjebak dengan beberapa masalah: 1. Bagaimana Anda mengatur judul jendela dari tampilan? 2. Bagaimana Anda menangani pengaturan jendela pemilik?
djskinner
@ Daniel Skinner: Saya berasumsi Anda berbicara tentang dialog di sini, koreksi saya jika saya salah. Judul dialog hanyalah properti lain dan Anda dapat mengikatnya ke apa pun yang Anda suka. Jika Anda mengikuti pendekatan saya dengan kelas viewmodel dialog dialog (mari kita berpura-pura memiliki properti judul), maka di semua jendela dialog generik Anda, Anda dapat menggunakan UI ke UI mengikat untuk mengatur judul ke {Binding Path = DataContext.Title, ElementName = NameOfContentPresenter}. Jendela pemilik sedikit penipu - artinya mediator yang benar-benar memunculkan dialog perlu tahu tentang tampilan aplikasi root.
Egor
Sebenarnya saya mengambil kembali - terlepas dari bagaimana Anda menyusun ini pada beberapa titik siapa pun yang sebenarnya muncul dialog perlu memiliki referensi ke jendela / tampilan aplikasi root. Perhatikan di mana saya berkata, "Biasanya, masuk akal bagi jendela root untuk berlangganan acara ini - ini dapat membuka dialog dan mengatur konteks datanya ke viewmodel yang diteruskan dengan peristiwa yang dinaikkan." Di sinilah Anda akan mengatur pemilik.
Egor
6

Jason Dolinger membuat screencast yang baik dari MVVM. Seperti yang disebutkan Egor, tidak ada satu contoh yang baik. Semuanya sudah berakhir. Sebagian besar adalah contoh MVVM yang baik, tetapi tidak ketika Anda masuk ke masalah yang kompleks. Setiap orang memiliki caranya masing-masing. Laurent Bugnion juga memiliki cara yang baik untuk berkomunikasi antar model. http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx Cinch juga merupakan contoh yang baik. Paul Stovel memiliki postingan bagus yang menjelaskan banyak hal dengan kerangka Magellan-nya.

nportelli
sumber
3

Apakah Anda sudah melihat Caliburn ? Sampel ContactManager memiliki banyak hal bagus di dalamnya. Sampel WPF generik juga memberikan ikhtisar perintah yang baik. Dokumentasinya cukup bagus dan forumnya aktif. Direkomendasikan!

Andy S
sumber
2

Proyek sampel dalam kerangka kerja Cinch menunjukkan CRUD dasar dan alat navigasi. Ini adalah contoh yang cukup baik untuk menggunakan MVVM, dan termasuk artikel multi-bagian yang menjelaskan penggunaan dan motivasinya.

Reed Copsey
sumber
2

Saya juga berbagi dalam frustrasi Anda. Saya sedang menulis aplikasi dan saya memiliki 3 persyaratan ini:

  • Dapat diperpanjang
  • WPF dengan MVVM
  • Contoh yang kompatibel dengan GPL

Yang saya temukan hanyalah potongan-potongan, jadi saya baru mulai menulisnya sebaik mungkin. Setelah saya membahasnya sedikit, saya menyadari mungkin ada orang lain (seperti Anda) yang dapat menggunakan aplikasi referensi, jadi saya refactored hal-hal umum ke dalam kerangka aplikasi WPF / MVVM dan dirilis di bawah LGPL. Saya menamainya SoapBox Core . Jika Anda membuka halaman unduhan, Anda akan melihatnya datang dengan aplikasi demo kecil, dan kode sumber untuk aplikasi demo itu juga tersedia untuk diunduh. Semoga bermanfaat. Juga, kirimkan email kepada saya di scott {at} soapboxautomation.com jika Anda ingin info lebih lanjut.

EDIT : Juga memposting artikel CodeProject yang menjelaskan cara kerjanya.

Scott Whitlock
sumber
2

Saya telah menulis contoh MVVM sederhana dari awal pada proyek kode di sini adalah tautan MVVM WPF langkah demi langkah . Dimulai dari arsitektur 3 layer yang sederhana dan membuat Anda menggunakan beberapa kerangka kerja seperti PRISM.

masukkan deskripsi gambar di sini

Shivprasad Koirala
sumber
1

Bahkan saya berbagi frustrasi sampai saya mengambil masalah itu ke tangan saya. Saya memulai IncEditor.

IncEditor ( http://inceditor.codeplex.com ) adalah editor yang mencoba memperkenalkan pengembang ke WPF, MVVM & MEF. Saya memulainya dan berhasil mendapatkan beberapa fungsi seperti dukungan 'tema'. Saya bukan ahli WPF atau MVVM atau MEF jadi saya tidak bisa memasukkan banyak fungsi di dalamnya. Saya membuat permintaan tulus kepada kalian untuk membuatnya lebih baik sehingga orang gila seperti saya dapat memahaminya dengan lebih baik.

Abdulsattar Mohammed
sumber