Saya akan membaca beberapa artikel MVVM, terutama ini dan ini .
Pertanyaan khusus saya adalah: Bagaimana cara mengkomunikasikan perubahan Model dari Model ke ViewModel?
Dalam artikel Josh, saya tidak melihat dia melakukan ini. ViewModel selalu meminta properti dari Model. Dalam contoh Rachel, dia memang memiliki model yang diimplementasikan INotifyPropertyChanged
, dan memunculkan kejadian dari model, tetapi kejadian tersebut untuk dikonsumsi oleh tampilan itu sendiri (lihat artikel / kode miliknya untuk detail lebih lanjut tentang mengapa dia melakukan ini).
Saya tidak melihat contoh di mana model memberi tahu ViewModel tentang perubahan pada properti model. Ini membuat saya khawatir bahwa mungkin itu tidak dilakukan karena suatu alasan. Apakah ada pola untuk memberi tahu ViewModel tentang perubahan dalam Model? Tampaknya diperlukan karena (1) mungkin ada lebih dari 1 ViewModel untuk setiap model, dan (2) meskipun hanya ada satu ViewModel, beberapa tindakan pada model dapat mengakibatkan properti lain diubah.
Saya menduga bahwa mungkin ada jawaban / komentar dalam bentuk "Mengapa Anda ingin melakukan itu?" komentar, jadi inilah deskripsi program saya. Saya baru mengenal MVVM jadi mungkin seluruh desain saya rusak. Saya akan menjelaskannya secara singkat.
Saya memprogram sesuatu yang lebih menarik (setidaknya, bagi saya!) Daripada kelas "Pelanggan" atau "Produk". Saya memprogram BlackJack.
Saya memiliki Tampilan yang tidak memiliki kode apa pun di belakang dan hanya mengandalkan pengikatan ke properti dan perintah di ViewModel (lihat artikel Josh Smith).
Untuk lebih baik atau lebih buruk, saya mengambil sikap bahwa Model harus berisi tidak hanya kelas seperti PlayingCard
, Deck
, tetapi juga BlackJackGame
kelas yang membuat keadaan seluruh permainan, dan tahu kapan pemain memiliki payudara hilang, dealer harus menarik kartu, dan apa skor pemain dan dealer saat ini (kurang dari 21, 21, bust, dll.).
Dari BlackJackGame
saya mengekspos metode seperti "DrawCard" dan terpikir oleh saya bahwa ketika kartu ditarik, properti seperti CardScore
, dan IsBust
harus diperbarui dan nilai-nilai baru ini dikomunikasikan ke ViewModel. Mungkin itu pemikiran yang salah?
Seseorang dapat mengambil sikap bahwa ViewModel memanggil DrawCard()
metode tersebut sehingga dia harus tahu untuk meminta skor terbaru dan mencari tahu apakah dia gagal atau tidak. Opini?
Dalam ViewModel saya, saya memiliki logika untuk mengambil gambar sebenarnya dari kartu remi (berdasarkan jenis, peringkat) dan membuatnya tersedia untuk tampilan. Model tidak perlu khawatir dengan ini (mungkin ViewModel lain hanya akan menggunakan angka daripada memainkan gambar kartu). Tentu saja, mungkin beberapa orang akan memberi tahu saya bahwa Model seharusnya tidak memiliki konsep permainan BlackJack dan itu harus ditangani dalam ViewModel?
OnBust
, dan VM dapat berlangganan padanya. Saya rasa Anda juga bisa menggunakan pendekatan IEA.Jawaban:
Jika Anda ingin Model Anda memberi tahu ViewModels tentang perubahan, mereka harus mengimplementasikan INotifyPropertyChanged , dan ViewModels harus berlangganan untuk menerima pemberitahuan PropertyChange.
Kode Anda mungkin terlihat seperti ini:
Tetapi biasanya ini hanya diperlukan jika lebih dari satu objek akan membuat perubahan pada data Model, yang biasanya tidak terjadi.
Jika Anda pernah memiliki kasus di mana Anda tidak benar-benar memiliki referensi ke properti Model Anda untuk melampirkan acara PropertyChanged padanya, maka Anda dapat menggunakan sistem Perpesanan seperti Prism's
EventAggregator
atau MVVM Light'sMessenger
.Saya memiliki gambaran singkat tentang sistem perpesanan di blog saya, namun untuk meringkasnya, objek apa pun dapat menyiarkan pesan, dan objek apa pun dapat berlangganan untuk mendengarkan pesan tertentu. Jadi, Anda mungkin menyiarkan
PlayerScoreHasChangedMessage
dari satu objek, dan objek lain bisa berlangganan untuk mendengarkan tipe pesan tersebut dan memperbaruiPlayerScore
propertinya ketika mendengarnya.Tapi saya rasa ini tidak diperlukan untuk sistem yang telah Anda jelaskan.
Dalam dunia MVVM yang ideal, aplikasi Anda terdiri dari ViewModels, dan Model Anda adalah satu-satunya blok yang digunakan untuk membangun aplikasi Anda. Mereka biasanya hanya berisi data, jadi tidak akan memiliki metode seperti
DrawCard()
(yang akan ada di ViewModel)Jadi, Anda mungkin memiliki objek data Model biasa seperti ini:
dan Anda akan memiliki objek ViewModel seperti
(Semua objek di atas harus diimplementasikan
INotifyPropertyChanged
, tetapi saya meninggalkannya untuk kesederhanaan)sumber
DrawCard()
metodenya akan ada di ViewModel, bersama dengan logika permainan Anda yang lain. Dalam aplikasi MVVM yang ideal, Anda harus dapat menjalankan aplikasi Anda tanpa UI sepenuhnya, cukup dengan membuat ViewModels dan menjalankan metodenya, seperti melalui skrip pengujian atau jendela prompt perintah. Model biasanya hanya model data yang berisi data mentah dan validasi data dasar.DrawCardCommand()
akan ada di ViewModel, tapi saya rasa Anda bisa memilikiBlackjackGameModel
objek yang berisiDrawCard()
metode yang dipanggil oleh perintah jika Anda mauJawaban singkatnya: tergantung pada spesifikasinya.
Dalam contoh Anda, model sedang diperbarui "sendiri" dan perubahan ini tentu saja perlu disebarkan ke tampilan. Karena tampilan hanya dapat mengakses langsung model tampilan, itu berarti model harus mengomunikasikan perubahan ini ke model tampilan yang sesuai. Mekanisme yang ditetapkan untuk melakukannya tentu saja
INotifyPropertyChanged
, yang berarti Anda akan mendapatkan alur kerja seperti ini:PropertyChanged
acara modelDataContext
, properti terikat, dllPropertyChanged
dan memunculkan modelnya sendiriPropertyChanged
sebagai tanggapanDi sisi lain, jika model Anda berisi sedikit (atau tidak ada) logika bisnis, atau jika karena alasan lain (seperti memperoleh kemampuan transaksional) Anda memutuskan untuk membiarkan setiap model tampilan "memiliki" model yang dibungkusnya, semua modifikasi pada model akan lolos model tampilan sehingga pengaturan seperti itu tidak diperlukan.
Saya menjelaskan desain seperti itu dalam pertanyaan MVVM lain di sini .
sumber
Pilihan Anda:
Seperti yang saya lihat,
INotifyPropertyChanged
ini adalah bagian fundamental dari .Net. yaitu dalamSystem.dll
. Menerapkannya dalam "Model" Anda sama dengan menerapkan struktur acara.Jika Anda menginginkan POCO murni, maka Anda secara efektif harus memanipulasi objek Anda melalui proxy / layanan dan kemudian ViewModel Anda diberi tahu tentang perubahan dengan mendengarkan proxy.
Secara pribadi saya baru saja mengimplementasikan INotifyPropertyChanged secara longgar dan kemudian menggunakan FODY untuk melakukan pekerjaan kotor untuk saya. Ini terlihat dan terasa POCO.
Contoh (menggunakan FODY ke IL Weave the PropertyChanged raiser):
maka Anda dapat meminta ViewModel Anda mendengarkan PropertyChanged untuk setiap perubahan; atau perubahan khusus properti.
Keindahan dari rute INotifyPropertyChanged adalah Anda menghubungkannya dengan Extended ObservableCollection . Jadi, Anda membuang objek near poco ke dalam koleksi, dan mendengarkan koleksi ... jika ada perubahan, di mana saja, Anda mempelajarinya.
Sejujurnya, ini bisa bergabung dengan diskusi "Mengapa INotifyPropertyChanged tidak ditangani secara otomatis oleh kompilator", yang berpindah ke: Setiap objek di c # harus memiliki fasilitas untuk memberi tahu jika ada bagian darinya yang diubah; yaitu mengimplementasikan INotifyPropertyChanged secara default. Tetapi tidak dan rute terbaik, yang membutuhkan sedikit usaha, adalah menggunakan tenun IL (khususnya FODY ).
sumber
Utas yang cukup lama tetapi setelah banyak pencarian, saya menemukan solusi saya sendiri: A PropertyChangedProxy
Dengan kelas ini, Anda dapat dengan mudah mendaftar ke NotifyPropertyChanged orang lain dan mengambil tindakan yang sesuai jika dipicu untuk properti terdaftar.
Berikut adalah contoh bagaimana hal ini akan terlihat ketika Anda memiliki properti model "Status" yang dapat berubah sendiri dan kemudian harus memberi tahu ViewModel secara otomatis untuk mengaktifkan PropertyChanged miliknya pada properti "Status" sehingga tampilan juga diberi tahu: )
dan inilah kelasnya sendiri:
sumber
-= my_event_handler
), karena itu lebih mudah dilacak daripada masalah zombie langka + tak terduga yang mungkin-atau-mungkin-tidak pernah terjadi.Saya menemukan artikel ini bermanfaat: http://social.msdn.microsoft.com/Forums/vstudio/en-US/3eb70678-c216-414f-a4a5-e1e3e557bb95/mvvm-businesslogic-is-part-of-the-?forum = wpf
Ringkasan saya:
Ide di balik organisasi MVVM adalah untuk memungkinkan penggunaan kembali tampilan dan model dengan lebih mudah dan juga untuk memungkinkan pengujian terpisah. Model tampilan Anda adalah model yang mewakili entitas tampilan, model Anda mewakili entitas bisnis.
Bagaimana jika nanti Anda ingin membuat permainan poker? Sebagian besar UI harus dapat digunakan kembali. Jika logika permainan Anda terikat dalam model tampilan, akan sangat sulit untuk menggunakan kembali elemen tersebut tanpa harus memprogram ulang model tampilan. Bagaimana jika Anda ingin mengubah antarmuka pengguna Anda? Jika logika game Anda digabungkan dengan logika model tampilan, Anda perlu memeriksa ulang apakah game Anda masih berfungsi. Bagaimana jika Anda ingin membuat desktop dan aplikasi web? Jika model tampilan Anda berisi logika permainan, akan menjadi rumit jika mencoba mempertahankan kedua aplikasi ini secara berdampingan karena logika aplikasi pasti akan terikat dengan logika bisnis dalam model tampilan.
Pemberitahuan perubahan data dan validasi data terjadi di setiap lapisan (tampilan, model tampilan, dan model).
Model tersebut berisi representasi data (entitas) dan logika bisnis Anda yang khusus untuk entitas tersebut. Setumpuk kartu adalah 'benda' logis dengan properti yang melekat. Dek yang bagus tidak bisa memasukkan kartu duplikat ke dalamnya. Itu perlu mengungkapkan cara untuk mendapatkan kartu teratas. Perlu diketahui untuk tidak membagikan kartu lebih dari yang tersisa. Perilaku dek seperti itu adalah bagian dari model karena mereka melekat pada setumpuk kartu. Ada juga model dealer, model pemain, model tangan, dll. Model ini dapat dan akan berinteraksi.
Model tampilan akan terdiri dari presentasi dan logika aplikasi. Semua pekerjaan yang terkait dengan menampilkan game terpisah dari logika game. Ini dapat mencakup menampilkan tangan sebagai gambar, permintaan kartu ke model dealer, pengaturan tampilan pengguna, dll.
Isi artikel:
sumber
Pemberitahuan berdasarkan INotifyPropertyChanged dan INotifyCollectionChanged persis seperti yang Anda butuhkan. Untuk menyederhanakan hidup Anda dengan berlangganan perubahan properti, validasi waktu kompilasi nama properti, menghindari kebocoran memori, saya akan menyarankan Anda untuk menggunakan PropertyObserver dari Yayasan MVVM Josh Smith . Karena proyek ini open source, Anda hanya dapat menambahkan kelas itu ke proyek Anda dari sumber.
Untuk memahami, bagaimana menggunakan PropertyObserver baca artikel ini .
Juga, lihat lebih dalam di Ekstensi Reaktif (Rx) . Anda dapat mengekspos IObserver <T> dari model Anda dan berlangganan di model tampilan.
sumber
Orang-orang melakukan pekerjaan luar biasa menjawab ini tetapi dalam situasi seperti ini saya benar-benar merasa bahwa pola MVVM menyebalkan jadi saya akan pergi dan menggunakan Pengontrol Pengawas atau pendekatan Tampilan Pasif dan melepaskan sistem pengikatan setidaknya untuk objek model yang menghasilkan perubahan sendiri.
sumber
Saya telah menganjurkan Model arah -> Model Tampilan -> Lihat aliran perubahan untuk waktu yang lama, seperti yang Anda lihat di bagian Aliran Perubahan pada artikel MVVM saya dari tahun 2008. Hal ini memerlukan penerapan
INotifyPropertyChanged
pada model. Sejauh yang saya tahu, itu sudah menjadi praktik umum.Karena Anda menyebut Josh Smith, lihat kelas PropertyChanged miliknya . Ini adalah kelas pembantu untuk berlangganan acara model
INotifyPropertyChanged.PropertyChanged
.Anda benar-benar dapat mengambil pendekatan ini lebih jauh, seperti yang saya lakukan baru-baru ini dengan membuat kelas PropertiesUpdater saya . Properti pada model tampilan dihitung sebagai ekspresi kompleks yang menyertakan satu atau beberapa properti pada model.
sumber
Tidak ada yang salah untuk mengimplementasikan INotifyPropertyChanged di dalam Model dan mendengarkannya di dalam ViewModel. Bahkan Anda bahkan bisa masuk ke properti model langsung di XAML: {Binding Model.ModelProperty}
Mengenai properti hanya baca yang bergantung / dihitung, sejauh ini saya belum melihat yang lebih baik dan lebih sederhana dari ini: https://github.com/StephenCleary/CalculatedProperties . Ini sangat sederhana tetapi sangat berguna, ini benar-benar "rumus Excel untuk MVVM" - bekerja dengan cara yang sama seperti Excel menyebarkan perubahan ke sel formula tanpa usaha ekstra dari pihak Anda.
sumber
Anda bisa memunculkan kejadian dari model, yang viewmodel perlu berlangganan.
Misalnya, saya baru-baru ini mengerjakan sebuah proyek yang harus saya hasilkan tampilan pohonnya (secara alami, model tersebut memiliki sifat hierarkis). Dalam model, saya memiliki koleksi yang dapat diamati yang disebut
ChildElements
.Dalam viewmodel, saya telah menyimpan referensi ke objek dalam model, dan berlangganan
CollectionChanged
acara koleksi observasi, seperti:ModelObject.ChildElements.CollectionChanged += new CollectionChangedEventHandler(insert function reference here)
...Kemudian viewmodel Anda secara otomatis diberitahu setelah perubahan terjadi dalam model. Anda dapat mengikuti konsep yang sama dengan menggunakan
PropertyChanged
, tetapi Anda perlu secara eksplisit meningkatkan peristiwa perubahan properti dari model Anda agar berfungsi.sumber
Bagi saya ini seperti pertanyaan yang sangat penting - bahkan ketika tidak ada tekanan untuk melakukannya. Saya sedang mengerjakan proyek uji, yang melibatkan TreeView. Ada item menu dan semacamnya yang dipetakan ke perintah, misalnya Hapus. Saat ini, saya memperbarui model dan model tampilan dari dalam model tampilan.
Sebagai contoh,
Ini sederhana, tetapi tampaknya memiliki kelemahan yang sangat mendasar. Tes unit tipikal akan menjalankan perintah, lalu memeriksa hasilnya dalam model tampilan. Tetapi ini tidak menguji apakah pembaruan model benar, karena keduanya diperbarui secara bersamaan.
Jadi mungkin lebih baik menggunakan teknik seperti PropertyObserver agar pembaruan model dapat memicu pembaruan model tampilan. Pengujian unit yang sama sekarang hanya akan berfungsi jika kedua tindakan berhasil.
Saya sadari, ini bukanlah jawaban yang potensial, tetapi tampaknya layak untuk disampaikan.
sumber