Apa itu ViewModelLocator dan apa kelebihan / kekurangannya dibandingkan dengan DataTemplates?

112

Dapatkah seseorang memberi saya ringkasan singkat tentang apa itu ViewModelLocator, cara kerjanya, dan apa pro / kontra penggunaannya dibandingkan dengan DataTemplates?

Saya telah mencoba mencari info di Google tetapi tampaknya ada banyak implementasi yang berbeda dan tidak ada daftar yang tepat tentang apa itu dan pro / kontra menggunakannya.

Rachel
sumber

Jawaban:

204

Intro

Dalam MVVM, praktik yang biasa dilakukan adalah meminta Views menemukan ViewModels mereka dengan menyelesaikannya dari penampung injeksi ketergantungan (DI). Ini terjadi secara otomatis ketika container diminta untuk menyediakan (menyelesaikan) sebuah instance dari kelas View. Penampung memasukkan ViewModel ke dalam Tampilan dengan memanggil konstruktor Tampilan yang menerima parameter ViewModel; skema ini disebut inversion of control (IoC).

Manfaat DI

Manfaat utama di sini adalah bahwa penampung dapat dikonfigurasi pada waktu proses dengan petunjuk tentang cara menyelesaikan jenis yang kami minta darinya. Hal ini memungkinkan testabilitas yang lebih besar dengan menginstruksikannya untuk menyelesaikan jenis (Views dan ViewModels) yang kita gunakan saat aplikasi kita benar-benar berjalan, tetapi menginstruksikannya secara berbeda saat menjalankan pengujian unit untuk aplikasi tersebut. Dalam kasus terakhir, aplikasi bahkan tidak akan memiliki UI (tidak berjalan; hanya pengujiannya) sehingga penampung akan menyelesaikan tiruan sebagai ganti jenis "normal" yang digunakan saat aplikasi berjalan.

Masalah yang berasal dari DI

Sejauh ini kita telah melihat bahwa pendekatan DI memungkinkan kemudahan pengujian untuk aplikasi dengan menambahkan lapisan abstraksi selama pembuatan komponen aplikasi. Ada satu masalah dengan pendekatan ini: pendekatan ini tidak cocok dengan desainer visual seperti Microsoft Expression Blend.

Masalahnya adalah bahwa dalam proses aplikasi normal dan pengujian unit, seseorang harus menyiapkan container dengan instruksi tentang tipe apa yang harus diselesaikan; Selain itu, seseorang harus meminta penampung untuk menyelesaikan Tampilan sehingga ViewModels dapat dimasukkan ke dalamnya.

Namun, dalam waktu desain tidak ada kode kami yang berjalan . Perancang mencoba menggunakan refleksi untuk membuat contoh Tampilan kami, yang berarti:

  • Jika konstruktor Tampilan memerlukan contoh ViewModel, perancang tidak akan dapat membuat contoh Tampilan sama sekali - ini akan menghasilkan kesalahan dengan cara yang terkontrol
  • Jika View memiliki konstruktor tanpa parameter, View akan dipakai, tetapi DataContextakan nullbegitu kita mendapatkan tampilan "kosong" di desainer - yang tidak terlalu berguna

Masuk ke ViewModelLocator

ViewModelLocator adalah abstraksi tambahan yang digunakan seperti ini:

  • Tampilan itu sendiri membuat instance ViewModelLocator sebagai bagian dari sumber dayanya dan menghubungkan DataContext-nya ke properti ViewModel pelacak
  • Locator entah bagaimana mendeteksi jika kita dalam mode desain
  • Jika tidak dalam mode desain, locator mengembalikan ViewModel yang diselesaikannya dari kontainer DI, seperti dijelaskan di atas
  • Jika dalam mode desain, pencari lokasi mengembalikan ViewModel "tiruan" yang tetap menggunakan logikanya sendiri (ingat: tidak ada kontainer dalam waktu desain!); ViewModel ini biasanya sudah terisi sebelumnya dengan data dummy

Tentu saja ini berarti bahwa View harus memiliki konstruktor tanpa parameter untuk memulai (jika tidak, desainer tidak akan dapat membuatnya).

Ringkasan

ViewModelLocator adalah idiom yang memungkinkan Anda menyimpan manfaat DI dalam aplikasi MVVM Anda sekaligus memungkinkan kode Anda berfungsi baik dengan desainer visual. Ini terkadang disebut "daya campuran" dari aplikasi Anda (mengacu pada Campuran Ekspresi).

Setelah mencerna hal di atas, lihat contoh praktis di sini .

Terakhir, menggunakan template data bukanlah alternatif untuk menggunakan ViewModelLocator, tetapi alternatif untuk menggunakan pasangan View / ViewModel eksplisit untuk bagian UI Anda. Seringkali Anda mungkin menemukan bahwa tidak perlu menentukan Tampilan untuk ViewModel karena Anda bisa menggunakan templat data sebagai gantinya.

Jon
sumber
4
1 untuk penjelasan yang bagus. Bisakah Anda memperluas View dan Sumber dayanya lebih jauh? Yang Anda maksud dengan Sumber daya adalah properti View? Atau? Apakah Anda memiliki hubungan dengan contoh konkret untuk pola ini?
Metro Smurf
@ MetroSmurf: Tautan Anda ada di bagian Ringkasan.
Jon
1
Terima kasih. Apakah ada batasan untuk menggunakan ViewModelLocator? Saya memiliki beberapa kekhawatiran tentang fakta bahwa itu mereferensikan sumber daya statis - dapatkah ViewModels dibuat secara dinamis saat runtime? Dan apakah ada banyak kode tambahan yang terlibat dalam menghubungkannya?
Rachel
@Rachel: Tautan yang sama di Ringkasan harus menjawab pertanyaan-pertanyaan ini dengan contoh praktis.
Jon
2
Jawaban yang sangat menyesatkan. Tujuan utama View Model Locator bukanlah untuk memberikan data tiruan kepada desainer. Anda dapat dengan mudah melakukan ini dengan menentukan d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}". Tujuan dari Locator adalah untuk benar-benar mengaktifkan DI pada Views, karena WPF sangat buruk dalam menyediakannya. Contoh: Anda memiliki Jendela Utama yang membuka beberapa Jendela Dialog. Untuk menyelesaikan DI di Jendela Dialog dengan cara biasa, Anda harus meneruskannya sebagai dependensi di Jendela Utama! Ini dihindari dengan View Locator.
hyankov
10

Contoh implementasi jawaban @ Jon

Saya memiliki kelas pencari model tampilan. Setiap properti akan menjadi turunan dari model tampilan yang akan saya alokasikan pada tampilan saya. Saya dapat memeriksa apakah kode tersebut berjalan dalam mode desain atau tidak menggunakan DesignerProperties.GetIsInDesignMode. Ini memungkinkan saya untuk menggunakan model tiruan selama waktu desain dan objek nyata saat saya menjalankan aplikasi.

public class ViewModelLocator
{
    private DependencyObject dummy = new DependencyObject();

    public IMainViewModel MainViewModel
    {
        get
        {
            if (IsInDesignMode())
            {
                return new MockMainViewModel();
            }

            return MyIoC.Container.GetExportedValue<IMainViewModel>();
        }
    }

    // returns true if editing .xaml file in VS for example
    private bool IsInDesignMode()
    {
        return DesignerProperties.GetIsInDesignMode(dummy);
    }
}

Dan untuk menggunakannya, saya dapat menambahkan pencari lokasi saya ke App.xamlsumber daya:

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

<Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

Dan kemudian untuk memasang tampilan Anda (mis: MainView.xaml) ke viewmodel Anda:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
BrunoLM
sumber
apakah ada perbedaan dalam menggunakan thisselain dummy?
Sebastian Xawery Wiśniowiecki
5

Saya tidak mengerti mengapa jawaban lain dari pertanyaan ini melingkupi Desainer.

Tujuan dari View Model Locator adalah untuk memungkinkan View Anda membuat contoh ini (ya, View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

bukan hanya ini:

public void MyWindowViewModel()
{
}

dengan menyatakan ini:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"

Di mana ViewModelLocatorkelas, yang mereferensikan IoC dan begitulah cara memecahkan MainWindowModelproperti yang dieksposnya.

Ini tidak ada hubungannya dengan menyediakan model tampilan Mock ke tampilan Anda. Jika Anda menginginkan itu, lakukan saja

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"

View Model Locator adalah pembungkus di sekitar beberapa (ada) wadah Pembalikan Kontrol, seperti Unity misalnya.

Mengacu pada:

hyankov
sumber
Pencari model tampilan tidak memerlukan penampung, pengguna memutuskan bagaimana model tampilan diselesaikan melalui konfigurasi dan Anda dapat menggunakan penampung atau hanya membuat tipe baru sendiri. Jadi, Anda dapat melakukan lokasi model tampilan berbasis konvensi, misalnya daripada mendaftarkan semua tampilan dan model tampilan Anda di beberapa penampung sebelumnya.
Chris Bordeman
Anda benar ketika mengatakan " Saya tidak mengerti mengapa jawaban lain [...] melingkupi Perancang ", tetapi tujuan pencari lokasi adalah untuk menghilangkan pengetahuan apa pun tentang bagaimana model tampilan dibuat dibuat, membuat tampilan tidak tergantung pada contoh ini, menyerahkannya ke pelacak. Pencari lokasi akan dapat memberikan rasa yang berbeda dari model tampilan, mungkin beberapa yang khusus ditambahkan melalui plugin yang akan dikelola oleh pelacak (dan yang khusus untuk waktu desain). Tampilan akan bersih dari semua proses penentuan versi yang benar dari model tampilan, yang memang bagus untuk SoC.
menit