MVVM di WPF - Bagaimana cara memperingatkan ViewModel tentang perubahan Model… atau haruskah saya?

112

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 BlackJackGamekelas 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 BlackJackGamesaya mengekspos metode seperti "DrawCard" dan terpikir oleh saya bahwa ketika kartu ditarik, properti seperti CardScore, dan IsBustharus 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?

Dave
sumber
3
Interaksi yang Anda gambarkan terdengar seperti mekanisme peristiwa standar yang Anda butuhkan. Model dapat mengekspos peristiwa yang dipanggil OnBust, dan VM dapat berlangganan padanya. Saya rasa Anda juga bisa menggunakan pendekatan IEA.
code4life
Saya akan jujur, jika saya di mana membuat 'aplikasi' blackjack nyata, data saya akan tersembunyi di balik beberapa lapisan layanan / proxy dan tingkat pengujian unit yang mirip dengan A + B = C. Itu akan menjadi proxy / layanan yang menginformasikan perubahan.
Meirion Hughes
1
Terima kasih semuanya! Sayangnya, saya hanya dapat memilih satu jawaban. Saya memilih Rachel karena saran arsitektur tambahan dan membersihkan pertanyaan asli. Tapi ada banyak jawaban bagus dan saya menghargainya. -Dave
Dave
2
FWIW: Setelah berjuang selama beberapa tahun dengan kerumitan dalam mempertahankan VM dan M per konsep domain, sekarang saya yakin bahwa keduanya gagal KERING; pemisahan perhatian yang diperlukan dapat dilakukan lebih mudah dengan memiliki dua INTERFACES pada satu objek - "Domain Interface" dan "ViewModel Interface". Objek ini dapat diteruskan ke logika bisnis dan logika View, tanpa kebingungan atau kurangnya sinkronisasi. Objek itu adalah "objek identitas" - itu secara unik mewakili entitas. Mempertahankan pemisahan kode domain vs kode tampilan kemudian membutuhkan alat yang lebih baik untuk melakukannya di dalam kelas.
ToolmakerSteve

Jawaban:

61

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:

// Attach EventHandler
PlayerModel.PropertyChanged += PlayerModel_PropertyChanged;

...

// When property gets changed in the Model, raise the PropertyChanged 
// event of the ViewModel copy of the property
PlayerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SomeProperty")
        RaisePropertyChanged("ViewModelCopyOfSomeProperty");
}

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 EventAggregatoratau MVVM Light's Messenger.

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 PlayerScoreHasChangedMessagedari satu objek, dan objek lain bisa berlangganan untuk mendengarkan tipe pesan tersebut dan memperbarui PlayerScorepropertinya 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:

class CardModel
{
    int Score;
    SuitEnum Suit;
    CardEnum CardValue;
}

class PlayerModel 
{
    ObservableCollection<Card> FaceUpCards;
    ObservableCollection<Card> FaceDownCards;
    int CurrentScore;

    bool IsBust
    {
        get
        {
            return Score > 21;
        }
    }
}

dan Anda akan memiliki objek ViewModel seperti

public class GameViewModel
{
    ObservableCollection<CardModel> Deck;
    PlayerModel Dealer;
    PlayerModel Player;

    ICommand DrawCardCommand;

    void DrawCard(Player currentPlayer)
    {
        var nextCard = Deck.First();
        currentPlayer.FaceUpCards.Add(nextCard);

        if (currentPlayer.IsBust)
            // Process next player turn

        Deck.Remove(nextCard);
    }
}

(Semua objek di atas harus diimplementasikan INotifyPropertyChanged, tetapi saya meninggalkannya untuk kesederhanaan)

Rachel
sumber
3
Secara lebih umum, apakah semua logika / aturan bisnis sesuai dengan model? Di mana semua logika pergi yang mengatakan Anda dapat mengambil kartu hingga 21 (tetapi dealer tetap menggunakan 17), bahwa Anda dapat membagi kartu, dll. Saya berasumsi semuanya termasuk dalam kelas model dan untuk alasan itu saya merasa saya perlu kelas pengontrol BlacJackGame dalam model. Saya masih mencoba untuk memahami ini dan sangat menghargai contoh / referensi. Ide blackjack misalnya diangkat dari kelas iTunes pada pemrograman iOS di mana logika / aturan bisnis paling pasti ada di kelas model pola MVC.
Dave
3
@Dave Ya, 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.
Rachel
6
Terima kasih Rachel untuk semua bantuannya. Saya harus meneliti ini lagi atau menulis pertanyaan lain; Saya masih bingung dengan lokasi logika permainan. Anda (dan lainnya) menganjurkan untuk meletakkannya di ViewModel, yang lain mengatakan "logika bisnis" yang dalam kasus saya saya asumsikan adalah aturan permainan dan status permainan termasuk dalam model (lihat misalnya: msdn.microsoft.com/en-us /library/gg405484%28v=pandp.40%29.aspx ) dan stackoverflow.com/questions/10964003/… ). Saya menyadari bahwa dalam permainan sederhana ini, itu mungkin tidak terlalu menjadi masalah. Tapi alangkah baiknya mengetahui. Thxs!
Dave
1
@ Dave Saya mungkin menggunakan istilah "logika bisnis" secara tidak benar dan mencampurnya dengan logika aplikasi. Mengutip artikel MSDN yang Anda tautkan "Untuk memaksimalkan peluang penggunaan kembali, model tidak boleh berisi perilaku atau logika aplikasi khusus kasus penggunaan atau tugas pengguna tertentu" dan "Biasanya, model tampilan akan menentukan perintah atau tindakan yang dapat direpresentasikan di UI dan pengguna dapat memanggil " . Jadi, hal-hal seperti a DrawCardCommand()akan ada di ViewModel, tapi saya rasa Anda bisa memiliki BlackjackGameModelobjek yang berisi DrawCard()metode yang dipanggil oleh perintah jika Anda mau
Rachel
2
Hindari kebocoran memori. Gunakan pola WeakEvent. joshsmithonwpf.wordpress.com/2009/07/11/…
JJS
24

Jawaban 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:

  1. Viewmodel dibuat dan membungkus model
  2. Viewmodel berlangganan PropertyChangedacara model
  3. Viewmodel ditetapkan sebagai tampilan DataContext, properti terikat, dll
  4. View memicu aksi pada viewmodel
  5. Viewmodel memanggil metode pada model
  6. Model memperbarui dirinya sendiri
  7. Viewmodel menangani model PropertyChangeddan memunculkan modelnya sendiri PropertyChangedsebagai tanggapan
  8. Tampilan mencerminkan perubahan dalam bindingnya, menutup loop umpan balik

Di 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 .

Jon
sumber
Halo, daftar yang Anda buat, sangat bagus. Namun saya memiliki masalah dengan 7. dan 8. Secara khusus: Saya memiliki ViewModel, yang tidak menerapkan INotifyPropertyChanged. Ini berisi daftar anak, yang berisi daftar anak itu sendiri (digunakan sebagai ViewModel untuk kontrol WPF Treeview). Bagaimana cara membuat UserControl DataContext ViewModel "mendengarkan" properti-perubahan di salah satu anak (TreeviewItems)? Bagaimana tepatnya saya berlangganan ke semua elemen anak, yang mengimplementasikan INotifyPropertyChanged? Atau haruskah saya membuat pertanyaan terpisah?
Igor
4

Pilihan Anda:

  • Implementasikan INotifyPropertyChanged
  • Acara
  • POCO dengan manipulator Proxy

Seperti yang saya lihat, INotifyPropertyChangedini adalah bagian fundamental dari .Net. yaitu dalam System.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):

public class NearlyPOCO: INotifyPropertyChanged
{
     public string ValueA {get;set;}
     public string ValueB {get;set;}

     public event PropertyChangedEventHandler PropertyChanged;
}

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 ).

Meirion Hughes
sumber
4

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: )

public class MyModel : INotifyPropertyChanged
{
    private string _status;
    public string Status
    {
        get { return _status; }
        set { _status = value; OnPropertyChanged(); }
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MyViewModel : INotifyPropertyChanged
{
    public string Status
    {
        get { return _model.Status; }
    }

    private PropertyChangedProxy<MyModel, string> _statusPropertyChangedProxy;
    private MyModel _model;
    public MyViewModel(MyModel model)
    {
        _model = model;
        _statusPropertyChangedProxy = new PropertyChangedProxy<MyModel, string>(
            _model, myModel => myModel.Status, s => OnPropertyChanged("Status")
        );
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

dan inilah kelasnya sendiri:

/// <summary>
/// Proxy class to easily take actions when a specific property in the "source" changed
/// </summary>
/// Last updated: 20.01.2015
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TPropType">Type of the property</typeparam>
public class PropertyChangedProxy<TSource, TPropType> where TSource : INotifyPropertyChanged
{
    private readonly Func<TSource, TPropType> _getValueFunc;
    private readonly TSource _source;
    private readonly Action<TPropType> _onPropertyChanged;
    private readonly string _modelPropertyname;

    /// <summary>
    /// Constructor for a property changed proxy
    /// </summary>
    /// <param name="source">The source object to listen for property changes</param>
    /// <param name="selectorExpression">Expression to the property of the source</param>
    /// <param name="onPropertyChanged">Action to take when a property changed was fired</param>
    public PropertyChangedProxy(TSource source, Expression<Func<TSource, TPropType>> selectorExpression, Action<TPropType> onPropertyChanged)
    {
        _source = source;
        _onPropertyChanged = onPropertyChanged;
        // Property "getter" to get the value
        _getValueFunc = selectorExpression.Compile();
        // Name of the property
        var body = (MemberExpression)selectorExpression.Body;
        _modelPropertyname = body.Member.Name;
        // Changed event
        _source.PropertyChanged += SourcePropertyChanged;
    }

    private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == _modelPropertyname)
        {
            _onPropertyChanged(_getValueFunc(_source));
        }
    }
}
Roemer
sumber
1
Hindari kebocoran memori. Gunakan pola WeakEvent. joshsmithonwpf.wordpress.com/2009/07/11/…
JJS
1
@JJS - OTOH, pertimbangkan The Weak Event Pattern Is Dangerous . Secara pribadi, saya lebih suka mengambil risiko kebocoran memori jika saya lupa untuk membatalkan registrasi ( -= my_event_handler), karena itu lebih mudah dilacak daripada masalah zombie langka + tak terduga yang mungkin-atau-mungkin-tidak pernah terjadi.
ToolmakerSteve
@ToolmakerSteve terima kasih telah menambahkan argumen yang seimbang. Saya menyarankan agar pengembang melakukan yang terbaik untuk mereka, dalam situasi mereka sendiri. Jangan mengadopsi kode sumber dari internet secara membabi buta. Ada pola lain seperti EventAggregator / EventBus yang biasa digunakan perpesanan lintas komponen (yang juga ditempa dengan bahayanya sendiri)
JJS
2

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:

Pada dasarnya, cara yang ingin saya jelaskan adalah logika bisnis dan entitas Anda membentuk modelnya. Inilah yang digunakan aplikasi spesifik Anda, tetapi dapat dibagikan ke banyak aplikasi.

Tampilan adalah lapisan presentasi - segala sesuatu yang berhubungan dengan langsung berinteraksi dengan pengguna.

ViewModel pada dasarnya adalah "perekat" yang khusus untuk aplikasi Anda yang menghubungkan keduanya.

Saya memiliki diagram yang bagus di sini yang menunjukkan bagaimana antarmuka mereka:

http://reedcopsey.com/2010/01/06/better-user-and-developer-experiences-from-windows-forms-to-wpf-with-mvvm-part-7-mvvm/

Dalam kasus Anda - mari kita tangani beberapa hal spesifik ...

Validasi: Ini biasanya datang dalam 2 bentuk. Validasi yang terkait dengan masukan pengguna akan terjadi di ViewModel (terutama) dan View (yaitu: Kotak Teks "Numerik" yang mencegah teks dimasukkan akan ditangani untuk Anda dalam tampilan, dll). Dengan demikian, validasi input dari pengguna biasanya menjadi perhatian VM. Karena itu, sering kali ada "lapisan" kedua dari validasi - ini adalah validasi bahwa data yang digunakan cocok dengan aturan bisnis. Ini sering kali merupakan bagian dari model itu sendiri - saat Anda memasukkan data ke Model Anda, ini dapat menyebabkan kesalahan validasi. VM kemudian harus memetakan ulang informasi ini kembali ke Tampilan.

Operasi "di belakang layar tanpa tampilan, seperti menulis ke DB, mengirim email, dll": Ini benar-benar bagian dari "Operasi Khusus Domain" dalam diagram saya, dan benar-benar murni bagian dari Model. Inilah yang coba Anda ungkapkan melalui aplikasi. ViewModel bertindak sebagai jembatan untuk mengekspos informasi ini, tetapi operasinya adalah Model-murni.

Operasi untuk ViewModel: ViewModel membutuhkan lebih dari sekedar INPC - ViewModel juga membutuhkan operasi yang spesifik untuk aplikasi Anda (bukan logika bisnis Anda), seperti menyimpan preferensi dan status pengguna, dll. Ini akan mengubah aplikasi. oleh aplikasi., bahkan saat menghubungkan "model" yang sama.

Cara yang baik untuk memikirkannya - Misalnya Anda ingin membuat 2 versi dari sistem pemesanan Anda. Yang pertama ada di WPF, dan yang kedua adalah antarmuka web.

Logika bersama yang menangani pesanan itu sendiri (mengirim email, masuk ke DB, dll) adalah Model. Aplikasi Anda mengekspos operasi dan data ini kepada pengguna, tetapi melakukannya dengan 2 cara.

Dalam aplikasi WPF, antarmuka pengguna (apa yang berinteraksi dengan pemirsa) adalah "tampilan" - dalam aplikasi web, ini pada dasarnya adalah kode yang (setidaknya pada akhirnya) diubah menjadi javascript + html + css pada klien.

ViewModel adalah sisa "perekat" yang diperlukan untuk menyesuaikan model Anda (operasi ini terkait dengan pemesanan) untuk membuatnya bekerja dengan teknologi tampilan / lapisan tertentu yang Anda gunakan.

VoteCoffee
sumber
Mungkin contoh sederhananya adalah pemutar musik. Model Anda akan berisi pustaka dan file suara aktif dan codec serta logika pemutar dan kode pemrosesan sinyal digital. Model tampilan akan berisi kontrol dan visualisasi dan browser perpustakaan Anda. ada banyak logika UI yang diperlukan untuk menampilkan semua informasi itu dan alangkah baiknya jika satu pemrogram dapat fokus pada pembuatan musik sambil membiarkan pemrogram lain untuk fokus pada pembuatan UI yang intuitif dan menyenangkan. Model tampilan dan model harus memungkinkan kedua pemrogram untuk menyetujui satu set antarmuka dan bekerja secara terpisah.
VoteCoffee
Contoh bagus lainnya adalah halaman web. Logika sisi server umumnya setara dengan model. Logika sisi klien umumnya setara dengan model tampilan. Saya akan dengan mudah membayangkan logika permainan akan menjadi milik server dan tidak dipercayakan kepada klien.
VoteCoffee
2

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.

Vladimir Dorokhov
sumber
Terima kasih banyak telah mereferensikan artikel bagus Josh Smith dan meliput Weak Events!
JJS
1

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.

Ibrahim Najjar
sumber
1

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 INotifyPropertyChangedpada 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.

HappyNomad
sumber
1

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.

Kola
sumber
0

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 CollectionChangedacara 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.

Mash
sumber
Jika berurusan dengan data hirarkis, Anda akan ingin melihat Demo 2 dari artikel MVVM saya .
HappyNomad
0

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,

public void DeleteItemExecute ()
{
    DesignObjectViewModel node = this.SelectedNode;    // Action is on selected item
    DocStructureManagement.DeleteNode(node.DesignObject); // Remove from application
    node.Remove();                                // Remove from view model
    Controller.UpdateDocument();                  // Signal document has changed
}

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.

Seni
sumber