Bagikan melalui


Model-View-ViewModel (MVVM)

Tip

Konten ini adalah kutipan dari eBook, Pola Aplikasi Perusahaan Menggunakan .NET MAUI, tersedia di .NET Docs atau sebagai PDF gratis yang dapat diunduh yang dapat dibaca secara offline.

Pola Aplikasi Perusahaan Menggunakan thumbnail sampul .NET MAUI eBook.

Pengalaman pengembang .NET MAUI biasanya melibatkan pembuatan antarmuka pengguna di XAML, lalu menambahkan code-behind yang beroperasi pada antarmuka pengguna. Masalah pemeliharaan yang kompleks dapat muncul saat aplikasi dimodifikasi dan tumbuh dalam ukuran dan cakupan. Masalah ini termasuk kopling ketat antara kontrol UI dan logika bisnis, yang meningkatkan biaya membuat modifikasi UI, dan kesulitan pengujian unit kode tersebut.

Pola MVVM membantu memisahkan logika bisnis dan presentasi aplikasi dengan bersih dari antarmuka pengguna (UI). Mempertahankan pemisahan yang bersih antara logika aplikasi dan UI membantu mengatasi banyak masalah pengembangan dan membuat aplikasi lebih mudah diuji, dikelola, dan berkembang. Ini juga dapat secara signifikan meningkatkan peluang penggunaan kembali kode dan memungkinkan pengembang dan desainer UI untuk berkolaborasi lebih mudah saat mengembangkan bagian masing-masing aplikasi.

Pola MVVM

Ada tiga komponen inti dalam pola MVVM: model, tampilan, dan model tampilan. Masing-masing melayani tujuan yang berbeda. Diagram di bawah ini menunjukkan hubungan antara ketiga komponen.

Pola MVVM

Selain memahami tanggung jawab setiap komponen, penting juga untuk memahami bagaimana mereka berinteraksi. Pada tingkat tinggi, tampilan "tahu tentang" model tampilan, dan model tampilan "tahu tentang" model, tetapi model tidak menyadari model tampilan, dan model tampilan tidak menyadari tampilan. Oleh karena itu, model tampilan mengisolasi tampilan dari model, dan memungkinkan model berevolusi secara independen dari tampilan.

Manfaat menggunakan pola MVVM adalah sebagai berikut:

  • Jika implementasi model yang ada merangkum logika bisnis yang ada, mungkin sulit atau berisiko untuk mengubahnya. Dalam skenario ini, model tampilan bertindak sebagai adaptor untuk kelas model dan mencegah Anda membuat perubahan besar pada kode model.
  • Pengembang dapat membuat pengujian unit untuk model tampilan dan model, tanpa menggunakan tampilan. Pengujian unit untuk model tampilan dapat menjalankan fungsionalitas yang sama persis seperti yang digunakan oleh tampilan.
  • UI aplikasi dapat dirancang ulang tanpa menyentuh model tampilan dan kode model, asalkan tampilan diimplementasikan sepenuhnya di XAML atau C#. Oleh karena itu, versi baru tampilan harus berfungsi dengan model tampilan yang ada.
  • Desainer dan pengembang dapat bekerja secara independen dan bersamaan pada komponen mereka selama pengembangan. Desainer dapat fokus pada tampilan, sementara pengembang dapat mengerjakan model tampilan dan komponen model.

Kunci untuk menggunakan MVVM secara efektif terletak pada pemahaman cara memperhitungkan kode aplikasi ke dalam kelas yang benar dan bagaimana kelas berinteraksi. Bagian berikut membahas tanggung jawab masing-masing kelas dalam pola MVVM.

Tampilan

Tampilan bertanggung jawab untuk menentukan struktur, tata letak, dan tampilan apa yang dilihat pengguna di layar. Idealnya, setiap tampilan didefinisikan dalam XAML, dengan kode terbatas di belakang yang tidak berisi logika bisnis. Namun, dalam beberapa kasus, code-behind mungkin berisi logika UI yang mengimplementasikan perilaku visual yang sulit diekspresikan di XAML, seperti animasi.

Dalam aplikasi .NET MAUI , tampilan biasanya merupakan ContentPagekelas -turunan atau ContentView-turunan. Namun, tampilan juga dapat diwakili oleh templat data, yang menentukan elemen UI yang akan digunakan untuk mewakili objek secara visual saat ditampilkan. Templat data sebagai tampilan tidak memiliki kode di belakang, dan dirancang untuk mengikat ke jenis model tampilan tertentu.

Tip

Hindari mengaktifkan dan menonaktifkan elemen UI di code-behind.

Pastikan bahwa model tampilan bertanggung jawab untuk menentukan perubahan status logis yang memengaruhi beberapa aspek tampilan, seperti apakah perintah tersedia, atau indikasi bahwa operasi tertunda. Oleh karena itu, aktifkan dan nonaktifkan elemen UI dengan mengikat untuk melihat properti model, daripada mengaktifkan dan menonaktifkannya di code-behind.

Ada beberapa opsi untuk menjalankan kode pada model tampilan sebagai respons terhadap interaksi pada tampilan, seperti klik tombol atau pilihan item. Jika kontrol mendukung perintah, properti Perintah kontrol dapat terikat data ke properti ICommand pada model tampilan. Ketika perintah kontrol dipanggil, kode dalam model tampilan akan dijalankan. Selain perintah, perilaku dapat dilampirkan ke objek dalam tampilan dan dapat mendengarkan perintah yang akan dipanggil atau peristiwa yang akan dimunculkan. Sebagai respons, perilaku kemudian dapat memanggil ICommand pada model tampilan atau metode pada model tampilan.

ViewModel

Model tampilan mengimplementasikan properti dan perintah tempat tampilan dapat mengikat data, dan memberi tahu tampilan perubahan status apa pun melalui peristiwa pemberitahuan perubahan. Properti dan perintah yang disediakan model tampilan menentukan fungsionalitas yang akan ditawarkan oleh UI, tetapi tampilan menentukan bagaimana fungsionalitas tersebut akan ditampilkan.

Tip

Jaga antarmuka pengguna tetap responsif dengan operasi asinkron.

Aplikasi multi-platform harus menjaga utas UI tidak diblokir untuk meningkatkan persepsi performa pengguna. Oleh karena itu, dalam model tampilan, gunakan metode asinkron untuk operasi I/O dan ajukan peristiwa untuk memberi tahu tampilan perubahan properti secara asinkron.

Model tampilan juga bertanggung jawab untuk mengoordinasikan interaksi tampilan dengan kelas model apa pun yang diperlukan. Biasanya ada hubungan satu-ke-banyak antara model tampilan dan kelas model. Model tampilan mungkin memilih untuk mengekspos kelas model langsung ke tampilan sehingga kontrol dalam tampilan dapat mengikat data langsung ke kelas tersebut. Dalam hal ini, kelas model harus dirancang untuk mendukung pengikatan data dan mengubah peristiwa pemberitahuan.

Setiap model tampilan menyediakan data dari model dalam bentuk yang dapat dikonsumsi dengan mudah oleh tampilan. Untuk mencapai hal ini, model tampilan terkadang melakukan konversi data. Menempatkan konversi data ini dalam model tampilan adalah ide yang baik karena menyediakan properti yang dapat diikat tampilan. Misalnya, model tampilan mungkin menggabungkan nilai dua properti untuk mempermudah tampilan.

Tip

Mempusatkan konversi data dalam lapisan konversi.

Anda juga dapat menggunakan pengonversi sebagai lapisan konversi data terpisah yang berada di antara model tampilan dan tampilan. Ini mungkin diperlukan, misalnya, ketika data memerlukan pemformatan khusus yang tidak disediakan model tampilan.

Agar model tampilan berpartisipasi dalam pengikatan data dua arah dengan tampilan, propertinya harus menaikkan PropertyChanged acara. Lihat model memenuhi persyaratan ini dengan mengimplementasikan INotifyPropertyChanged antarmuka, dan meningkatkan PropertyChanged peristiwa saat properti diubah.

Untuk koleksi, view-friendly ObservableCollection<T> disediakan. Koleksi ini mengimplementasikan pemberitahuan pengumpulan yang diubah, menghilangkan pengembang dari harus menerapkan INotifyCollectionChanged antarmuka pada koleksi.

Model

Kelas model adalah kelas non-visual yang merangkum data aplikasi. Oleh karena itu, model dapat dianggap sebagai mewakili model domain aplikasi, yang biasanya mencakup model data bersama dengan logika bisnis dan validasi. Contoh objek model termasuk objek transfer data (DTO), Objek CLR Lama Biasa (POCO), dan objek entitas dan proksi yang dihasilkan.

Kelas model biasanya digunakan bersama dengan layanan atau repositori yang merangkum akses dan penembolokan data.

Menyambungkan model tampilan ke tampilan

Model tampilan dapat disambungkan ke tampilan dengan menggunakan kemampuan pengikatan data .NET MAUI. Ada banyak pendekatan yang dapat digunakan untuk membuat tampilan dan melihat model dan mengaitkannya saat runtime. Pendekatan ini termasuk dalam dua kategori, yang dikenal sebagai melihat komposisi pertama, dan melihat komposisi pertama model. Memilih antara tampilan komposisi pertama dan melihat komposisi pertama model adalah masalah preferensi dan kompleksitas. Namun, semua pendekatan memiliki tujuan yang sama, yaitu agar tampilan memiliki model tampilan yang ditetapkan ke properti BindingContext-nya.

Dengan komposisi tampilan pertama, aplikasi secara konseptual terdiri dari tampilan yang terhubung ke model tampilan yang mereka andalkan. Manfaat utama dari pendekatan ini adalah memudahkan untuk membangun aplikasi yang dapat diuji unit yang digabungkan secara longgar karena model tampilan tidak memiliki ketergantungan pada tampilan itu sendiri. Juga mudah untuk memahami struktur aplikasi dengan mengikuti struktur visualnya, daripada harus melacak eksekusi kode untuk memahami bagaimana kelas dibuat dan dikaitkan. Selain itu, lihat konstruksi pertama yang selaras dengan sistem navigasi Microsoft Maui yang bertanggung jawab untuk membuat halaman ketika navigasi terjadi, yang membuat model tampilan terlebih dahulu kompleks dan tidak selaras dengan platform.

Dengan komposisi pertama model tampilan, aplikasi secara konseptual terdiri dari model tampilan, dengan layanan yang bertanggung jawab untuk menemukan tampilan untuk model tampilan. Lihat komposisi pertama model terasa lebih alami untuk beberapa pengembang, karena pembuatan tampilan dapat diabstraksi, memungkinkan mereka untuk fokus pada struktur non-UI logis aplikasi. Selain itu, ini memungkinkan model tampilan dibuat oleh model tampilan lain. Namun, pendekatan ini seringkali rumit, dan menjadi sulit untuk memahami bagaimana berbagai bagian aplikasi dibuat dan terkait.

Tip

Menjaga model tampilan dan tampilan tetap independen.

Pengikatan tampilan ke properti di sumber data harus menjadi dependensi utama tampilan pada model tampilan yang sesuai. Secara khusus, jangan mereferensikan jenis tampilan, seperti Tombol dan ListView, dari model tampilan. Dengan mengikuti prinsip-prinsip yang diuraikan di sini, model tampilan dapat diuji dalam isolasi, oleh karena itu mengurangi kemungkinan cacat perangkat lunak dengan membatasi cakupan.

Bagian berikut membahas pendekatan utama untuk menyambungkan model tampilan ke tampilan.

Membuat model tampilan secara deklaratif

Pendekatan paling sederhana adalah agar tampilan dapat secara deklaratif membuat instans model tampilan yang sesuai di XAML. Saat tampilan dibangun, objek model tampilan yang sesuai juga akan dibangun. Pendekatan ini ditunjukkan dalam contoh kode berikut:

<ContentPage xmlns:local="clr-namespace:eShop">
    <ContentPage.BindingContext>
        <local:LoginViewModel />
    </ContentPage.BindingContext>
    <!-- Omitted for brevity... -->
</ContentPage>

ContentPage Saat dibuat, instans LoginViewModel secara otomatis dibangun dan ditetapkan sebagai tampilan BindingContext.

Konstruksi deklaratif dan penetapan model tampilan ini dengan tampilan memiliki keuntungan bahwa itu sederhana, tetapi memiliki kerugian bahwa ia memerlukan konstruktor default (parameter-kurang) dalam model tampilan.

Membuat model tampilan secara terprogram

Tampilan dapat memiliki kode dalam file code-behind, yang mengakibatkan model tampilan ditetapkan ke propertinya BindingContext . Ini sering dicapai dalam konstruktor tampilan, seperti yang ditunjukkan dalam contoh kode berikut:

public LoginView()
{
    InitializeComponent();
    BindingContext = new LoginViewModel(navigationService);
}

Konstruksi terprogram dan penetapan model tampilan dalam kode tampilan di belakang memiliki keuntungan bahwa itu sederhana. Namun, kerugian utama dari pendekatan ini adalah bahwa tampilan perlu menyediakan model tampilan dengan dependensi yang diperlukan. Menggunakan kontainer injeksi dependensi dapat membantu mempertahankan kopling longgar antara tampilan dan model tampilan. Untuk informasi selengkapnya, lihat Injeksi dependensi.

Memperbarui tampilan sebagai respons terhadap perubahan dalam model atau model tampilan yang mendasar

Semua melihat model dan kelas model yang dapat diakses oleh tampilan harus mengimplementasikan [INotifyPropertyChanged antarmuka. Menerapkan antarmuka ini dalam model tampilan atau kelas model memungkinkan kelas untuk memberikan pemberitahuan perubahan ke kontrol terikat data apa pun dalam tampilan saat nilai properti yang mendasar berubah.

Aplikasi harus dirancang untuk penggunaan pemberitahuan perubahan properti yang benar, dengan memenuhi persyaratan berikut:

  • Selalu menaikkan PropertyChanged peristiwa jika nilai properti publik berubah. Jangan berasumsi bahwa menaikkan PropertyChanged peristiwa dapat diabaikan karena pengetahuan tentang bagaimana pengikatan XAML terjadi.
  • Selalu menaikkan PropertyChanged peristiwa untuk properti terhitung yang nilainya digunakan oleh properti lain dalam model tampilan atau model.
  • Selalu tingkatkan PropertyChanged peristiwa di akhir metode yang membuat perubahan properti, atau ketika objek diketahui dalam keadaan aman. Meningkatkan peristiwa mengganggu operasi dengan memanggil handler peristiwa secara sinkron. Jika ini terjadi di tengah operasi, mungkin mengekspos objek ke fungsi panggilan balik ketika berada dalam status tidak aman dan diperbarui sebagian. Selain itu, ada kemungkinan perubahan berkala dipicu oleh PropertyChanged peristiwa. Perubahan berjencar umumnya mengharuskan pembaruan selesai sebelum perubahan berjencar aman untuk dijalankan.
  • Jangan pernah menaikkan PropertyChanged peristiwa jika properti tidak berubah. Ini berarti Anda harus membandingkan nilai lama dan baru sebelum menaikkan PropertyChanged acara.
  • Jangan pernah menaikkan PropertyChanged peristiwa selama konstruktor model tampilan jika Anda menginisialisasi properti. Kontrol terikat data dalam tampilan tidak akan berlangganan untuk menerima pemberitahuan perubahan pada saat ini.
  • Jangan pernah menaikkan lebih dari satu PropertyChanged peristiwa dengan argumen nama properti yang sama dalam satu pemanggilan sinkron dari metode publik kelas. Misalnya, mengingat NumberOfItems properti yang penyimpanan backing-nya adalah _numberOfItems bidang , jika metode naik _numberOfItems lima puluh kali selama eksekusi perulangan, itu hanya boleh menaikkan pemberitahuan perubahan properti pada NumberOfItems properti sekali, setelah semua pekerjaan selesai. Untuk metode asinkron, ajukan PropertyChanged peristiwa untuk nama properti tertentu di setiap segmen sinkron dari rantai kelanjutan asinkron.

Cara sederhana untuk menyediakan fungsionalitas ini adalah dengan membuat ekstensi BindableObject kelas. Dalam contoh ini, ExtendedBindableObject kelas menyediakan pemberitahuan perubahan, yang ditunjukkan dalam contoh kode berikut:

public abstract class ExtendedBindableObject : BindableObject
{
    public void RaisePropertyChanged<T>(Expression<Func<T>> property)
    {
        var name = GetMemberInfo(property).Name;
        OnPropertyChanged(name);
    }

    private MemberInfo GetMemberInfo(Expression expression)
    {
        // Omitted for brevity ...
    }
}

Kelas .NET MAUIBindableObject mengimplementasikan INotifyPropertyChanged antarmuka, dan menyediakan OnPropertyChanged metode . Kelas ExtendedBindableObject menyediakan RaisePropertyChanged metode untuk memanggil pemberitahuan perubahan properti, dan dalam melakukannya menggunakan fungsionalitas yang disediakan oleh BindableObject kelas.

Lihat kelas model kemudian dapat berasal dari ExtendedBindableObject kelas . Oleh karena itu, setiap kelas model tampilan menggunakan RaisePropertyChanged metode di ExtendedBindableObject kelas untuk memberikan pemberitahuan perubahan properti. Contoh kode berikut menunjukkan bagaimana aplikasi multi-platform eShop memanggil pemberitahuan perubahan properti dengan menggunakan ekspresi lambda:

public bool IsLogin
{
    get => _isLogin;
    set
    {
        _isLogin = value;
        RaisePropertyChanged(() => IsLogin);
    }
}

Menggunakan ekspresi lambda dengan cara ini melibatkan biaya performa yang kecil karena ekspresi lambda harus dievaluasi untuk setiap panggilan. Meskipun biaya performa kecil dan biasanya tidak akan berdampak pada aplikasi, biaya dapat bertambah ketika ada banyak pemberitahuan perubahan. Namun, manfaat dari pendekatan ini adalah menyediakan keamanan jenis waktu kompilasi dan dukungan refaktor saat mengganti nama properti.

Kerangka Kerja MVVM

Pola MVVM didirikan dengan baik di .NET, dan komunitas telah menciptakan banyak kerangka kerja yang membantu memudahkan pengembangan ini. Setiap kerangka kerja menyediakan serangkaian fitur yang berbeda, tetapi standar bagi mereka untuk menyediakan model tampilan umum dengan implementasi INotifyPropertyChanged antarmuka. Fitur tambahan kerangka kerja MVVM termasuk perintah kustom, pembantu navigasi, komponen injeksi dependensi/pencari layanan, dan integrasi platform UI. Meskipun tidak perlu menggunakan kerangka kerja ini, mereka dapat mempercepat dan menstandarkan pengembangan Anda. Aplikasi multi-platform eShop menggunakan Toolkit MVVM Komunitas .NET. Saat memilih kerangka kerja, Anda harus mempertimbangkan kebutuhan aplikasi dan kekuatan tim Anda. Daftar di bawah ini mencakup beberapa kerangka kerja MVVM yang lebih umum untuk .NET MAUI.

Interaksi UI menggunakan perintah dan perilaku

Di aplikasi multi-platform, tindakan biasanya dipanggil sebagai respons terhadap tindakan pengguna, seperti klik tombol, yang dapat diimplementasikan dengan membuat penanganan aktivitas di file code-behind. Namun, dalam pola MVVM, tanggung jawab untuk menerapkan tindakan terletak pada model tampilan, dan menempatkan kode di kode-belakang harus dihindari.

Perintah menyediakan cara mudah untuk mewakili tindakan yang dapat terikat ke kontrol di UI. Mereka merangkum kode yang mengimplementasikan tindakan dan membantu membuatnya dipisahkan dari representasi visualnya dalam tampilan. Dengan cara ini, model tampilan Anda menjadi lebih portabel ke platform baru, karena tidak memiliki dependensi langsung pada peristiwa yang disediakan oleh kerangka kerja UI platform. .NET MAUI menyertakan kontrol yang dapat secara deklaratif terhubung ke perintah, dan kontrol ini akan memanggil perintah ketika pengguna berinteraksi dengan kontrol.

Perilaku juga memungkinkan kontrol terhubung secara deklaratif ke perintah. Namun, perilaku dapat digunakan untuk memanggil tindakan yang terkait dengan berbagai peristiwa yang diangkat oleh kontrol. Oleh karena itu, perilaku membahas banyak skenario yang sama dengan kontrol berkemampuan perintah, sambil memberikan tingkat fleksibilitas dan kontrol yang lebih besar. Selain itu, perilaku juga dapat digunakan untuk mengaitkan objek perintah atau metode dengan kontrol yang tidak dirancang khusus untuk berinteraksi dengan perintah.

Menerapkan perintah

Lihat model biasanya mengekspos properti publik, untuk mengikat dari tampilan, yang mengimplementasikan ICommand antarmuka. Banyak kontrol dan gerakan .NET MAUI menyediakan Command properti, yang dapat berupa data yang terikat ke objek yang ICommand disediakan oleh model tampilan. Kontrol tombol adalah salah satu kontrol yang paling umum digunakan, menyediakan properti perintah yang dijalankan saat tombol diklik.

Catatan

Meskipun dimungkinkan untuk mengekspos implementasi aktual antarmuka ICommand yang digunakan model tampilan Anda (misalnya, Command<T> atau RelayCommand), disarankan untuk mengekspos perintah Anda secara publik sebagai ICommand. Dengan cara ini, jika Anda perlu mengubah implementasi di kemudian hari, itu dapat dengan mudah ditukar.

Antarmuka ICommand mendefinisikan Execute metode, yang merangkum operasi itu sendiri, CanExecute metode, yang menunjukkan apakah perintah dapat dipanggil, dan CanExecuteChanged peristiwa yang terjadi ketika perubahan terjadi yang memengaruhi apakah perintah harus dijalankan. Dalam kebanyakan kasus, kami hanya akan menyediakan Execute metode untuk perintah kami. Untuk gambaran umum yang lebih rinci tentang ICommand, lihat dokumentasi Perintah untuk .NET MAUI.

Disediakan dengan .NET MAUI adalah Command kelas dan Command<T> yang mengimplementasikan ICommand antarmuka, di mana T adalah jenis argumen ke Execute dan CanExecute. Command dan Command<T> merupakan implementasi dasar yang menyediakan serangkaian fungsionalitas minimal yang diperlukan untuk ICommand antarmuka.

Catatan

Banyak kerangka kerja MVVM menawarkan implementasi antarmuka yang ICommand lebih kaya fitur.

Konstruktor Command atau Command<T> memerlukan objek panggilan balik Tindakan yang dipanggil saat ICommand.Execute metode dipanggil. Metode CanExecute ini adalah parameter konstruktor opsional, dan merupakan Func yang mengembalikan bool.

Aplikasi multi-platform eShop menggunakan RelayCommand dan AsyncRelayCommand. Manfaat utama untuk aplikasi modern adalah menyediakan fungsionalitas yang AsyncRelayCommand lebih baik untuk operasi asinkron.

Kode berikut menunjukkan bagaimana Command instans, yang mewakili perintah register, dibangun dengan menentukan delegasi ke metode model tampilan Register:

public ICommand RegisterCommand { get; }

Perintah diekspos ke tampilan melalui properti yang mengembalikan referensi ke ICommand. Execute Ketika metode dipanggil pada Command objek, metode hanya meneruskan panggilan ke metode dalam model tampilan melalui delegasi yang ditentukan dalam Command konstruktor. Metode asinkron dapat dipanggil oleh perintah dengan menggunakan asinkron dan menunggu kata kunci saat menentukan delegasi perintah Execute . Ini menunjukkan bahwa panggilan balik adalah Task dan harus ditunggu. Misalnya, kode berikut menunjukkan bagaimana ICommand instans, yang mewakili perintah masuk, dibangun dengan menentukan delegasi ke SignInAsync metode model tampilan:

public ICommand SignInCommand { get; }
...
SignInCommand = new AsyncRelayCommand(async () => await SignInAsync());

Parameter dapat diteruskan ke Execute tindakan dan CanExecute dengan menggunakan AsyncRelayCommand<T> kelas untuk membuat instans perintah. Misalnya, kode berikut menunjukkan bagaimana AsyncRelayCommand<T> instans digunakan untuk menunjukkan bahwa NavigateAsync metode akan memerlukan argumen string jenis:

public ICommand NavigateCommand { get; }

...
NavigateCommand = new AsyncRelayCommand<string>(NavigateAsync);

Di kedua RelayCommand kelas dan RelayCommand<T> , delegasi ke CanExecute metode di setiap konstruktor bersifat opsional. Jika delegasi tidak ditentukan, Command akan mengembalikan true untuk CanExecute. Namun, model tampilan dapat menunjukkan perubahan status perintah CanExecute dengan memanggil ChangeCanExecute metode pada Command objek . Hal ini menyebabkan CanExecuteChanged peristiwa dinaikkan. Kontrol UI apa pun yang terikat ke perintah kemudian akan memperbarui status yang diaktifkan untuk mencerminkan ketersediaan perintah terikat data.

Memanggil perintah dari tampilan

Contoh kode berikut menunjukkan bagaimana Grid dalam LoginView pengikatan ke RegisterCommand di LoginViewModel kelas dengan menggunakan TapGestureRecognizer instans:

<Grid Grid.Column="1" HorizontalOptions="Center">
    <Label Text="REGISTER" TextColor="Gray"/>
    <Grid.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="1" />
    </Grid.GestureRecognizers>
</Grid>

Parameter perintah juga dapat ditentukan secara opsional menggunakan CommandParameter properti . Jenis argumen yang diharapkan ditentukan dalam Execute metode target dan CanExecute . TapGestureRecognizer akan secara otomatis memanggil perintah target saat pengguna berinteraksi dengan kontrol terlampir. CommandParameter, jika disediakan, akan diteruskan sebagai argumen ke delegasi Jalankan perintah.

Menerapkan perilaku

Perilaku memungkinkan fungsionalitas ditambahkan ke kontrol UI tanpa harus mensublasifikasikannya. Sebaliknya, fungsionalitas diimplementasikan dalam kelas perilaku dan dilampirkan ke kontrol seolah-olah itu adalah bagian dari kontrol itu sendiri. Perilaku memungkinkan Anda menerapkan kode yang biasanya harus Anda tulis sebagai kode di belakang, karena berinteraksi langsung dengan API kontrol, sededimikian rupa sehingga dapat dilampirkan secara ringkas ke kontrol, dan dikemas untuk digunakan kembali di lebih dari satu tampilan atau aplikasi. Dalam konteks MVVM, perilaku adalah pendekatan yang berguna untuk menghubungkan kontrol ke perintah.

Perilaku yang dilampirkan ke kontrol melalui properti terlampir dikenal sebagai perilaku terlampir. Perilaku kemudian dapat menggunakan API yang diekspos dari elemen yang dilampirkan untuk menambahkan fungsionalitas ke kontrol tersebut, atau kontrol lainnya, di pohon visual tampilan.

Perilaku .NET MAUI adalah kelas yang berasal dari Behavior kelas atau Behavior<T> , di mana T adalah jenis kontrol yang harus diterapkan perilaku. Kelas-kelas ini menyediakan OnAttachedTo metode dan OnDetachingFrom , yang harus ditimpa untuk memberikan logika yang akan dijalankan ketika perilaku dilampirkan dan dilepas dari kontrol.

Di aplikasi multi-platform eShop, BindableBehavior<T> kelas berasal dari Behavior<T> kelas . Tujuan kelas BindableBehavior<T> adalah untuk menyediakan kelas dasar untuk perilaku .NET MAUI yang mengharuskan BindingContext perilaku diatur ke kontrol terlampir.

Kelas BindableBehavior<T> ini menyediakan metode yang dapat OnAttachedTo diambil alih yang mengatur BindingContext perilaku, dan metode yang dapat OnDetachingFrom diambil alih yang membersihkan BindingContext.

Aplikasi multi-platform eShop mencakup kelas EventToCommandBehavior yang disediakan oleh MAUI toolkit Komunitas. EventToCommandBehavior menjalankan perintah sebagai respons terhadap peristiwa yang terjadi. Kelas ini berasal dari BaseBehavior<View> kelas sehingga perilaku dapat mengikat dan menjalankan yang ICommand ditentukan oleh Command properti ketika perilaku dikonsumsi. Contoh kode berikut menunjukkan EventToCommandBehavior kelas :

/// <summary>
/// The <see cref="EventToCommandBehavior"/> is a behavior that allows the user to invoke a <see cref="ICommand"/> through an event. It is designed to associate Commands to events exposed by controls that were not designed to support Commands. It allows you to map any arbitrary event on a control to a Command.
/// </summary>
public class EventToCommandBehavior : BaseBehavior<VisualElement>
{
    // Omitted for brevity...

    /// <inheritdoc/>
    protected override void OnAttachedTo(VisualElement bindable)
    {
        base.OnAttachedTo(bindable);
        RegisterEvent();
    }

    /// <inheritdoc/>
    protected override void OnDetachingFrom(VisualElement bindable)
    {
        UnregisterEvent();
        base.OnDetachingFrom(bindable);
    }

    static void OnEventNamePropertyChanged(BindableObject bindable, object oldValue, object newValue)
        => ((EventToCommandBehavior)bindable).RegisterEvent();

    void RegisterEvent()
    {
        UnregisterEvent();

        var eventName = EventName;
        if (View is null || string.IsNullOrWhiteSpace(eventName))
        {
            return;
        }

        eventInfo = View.GetType()?.GetRuntimeEvent(eventName) ??
            throw new ArgumentException($"{nameof(EventToCommandBehavior)}: Couldn't resolve the event.", nameof(EventName));

        ArgumentNullException.ThrowIfNull(eventInfo.EventHandlerType);
        ArgumentNullException.ThrowIfNull(eventHandlerMethodInfo);

        eventHandler = eventHandlerMethodInfo.CreateDelegate(eventInfo.EventHandlerType, this) ??
            throw new ArgumentException($"{nameof(EventToCommandBehavior)}: Couldn't create event handler.", nameof(EventName));

        eventInfo.AddEventHandler(View, eventHandler);
    }

    void UnregisterEvent()
    {
        if (eventInfo is not null && eventHandler is not null)
        {
            eventInfo.RemoveEventHandler(View, eventHandler);
        }

        eventInfo = null;
        eventHandler = null;
    }

    /// <summary>
    /// Virtual method that executes when a Command is invoked
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="eventArgs"></param>
    [Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
    protected virtual void OnTriggerHandled(object? sender = null, object? eventArgs = null)
    {
        var parameter = CommandParameter
            ?? EventArgsConverter?.Convert(eventArgs, typeof(object), null, null);

        var command = Command;
        if (command?.CanExecute(parameter) ?? false)
        {
            command.Execute(parameter);
        }
    }
}

Metode OnAttachedTo dan OnDetachingFrom digunakan untuk mendaftarkan dan membatalkan pendaftaran penanganan aktivitas untuk peristiwa yang ditentukan dalam EventName properti. Kemudian, ketika peristiwa diaktifkan, OnTriggerHandled metode dipanggil, yang menjalankan perintah.

Keuntungan menggunakan EventToCommandBehavior untuk menjalankan perintah saat peristiwa diaktifkan, adalah bahwa perintah dapat dikaitkan dengan kontrol yang tidak dirancang untuk berinteraksi dengan perintah. Selain itu, ini memindahkan kode penanganan peristiwa untuk melihat model, tempat kode dapat diuji unit.

Memanggil perilaku dari tampilan

EventToCommandBehavior sangat berguna untuk melampirkan perintah ke kontrol yang tidak mendukung perintah. Misalnya, LoginView menggunakan EventToCommandBehavior untuk menjalankan ValidateCommand saat pengguna mengubah nilai kata sandi mereka, seperti yang ditunjukkan dalam kode berikut:

<Entry
    IsPassword="True"
    Text="{Binding Password.Value, Mode=TwoWay}">
    <!-- Omitted for brevity... -->
    <Entry.Behaviors>
        <mct:EventToCommandBehavior
            EventName="TextChanged"
            Command="{Binding ValidateCommand}" />
    </Entry.Behaviors>
    <!-- Omitted for brevity... -->
</Entry>

Pada runtime, EventToCommandBehavior akan merespons interaksi dengan Entry. Ketika pengguna mengetik ke Entry bidang , TextChanged peristiwa akan diaktifkan, yang akan menjalankan ValidateCommand di LoginViewModel. Secara default, argumen peristiwa untuk peristiwa diteruskan ke perintah . Jika diperlukan, EventArgsConverter properti dapat digunakan untuk mengonversi EventArgs yang disediakan oleh peristiwa menjadi nilai yang diharapkan perintah sebagai input.

Untuk informasi selengkapnya tentang perilaku, lihat Perilaku di Pusat Pengembang .NET MAUI .

Ringkasan

Pola Model-View-ViewModel (MVVM) membantu memisahkan logika bisnis dan presentasi aplikasi dengan bersih dari antarmuka pengguna (UI). Mempertahankan pemisahan yang bersih antara logika aplikasi dan UI membantu mengatasi banyak masalah pengembangan dan membuat aplikasi lebih mudah diuji, dikelola, dan berkembang. Ini juga dapat secara signifikan meningkatkan peluang penggunaan kembali kode dan memungkinkan pengembang dan desainer UI untuk berkolaborasi lebih mudah saat mengembangkan bagian masing-masing aplikasi.

Menggunakan pola MVVM, UI aplikasi dan presentasi dan logika bisnis yang mendasarinya dipisahkan menjadi tiga kelas terpisah: tampilan, yang merangkum logika UI dan UI; model tampilan, yang merangkum logika dan status presentasi; dan model, yang merangkum logika dan data bisnis aplikasi.