Bagikan melalui


Menerapkan MVVM dengan Toolkit MVVM

Sekarang setelah Anda memiliki struktur proyek, Anda dapat mulai menerapkan pola MVVM dengan menggunakan Toolkit MVVM. Langkah ini melibatkan pembuatan ViewModels yang memanfaatkan fitur MVVM Toolkit, seperti ObservableObject untuk pemberitahuan perubahan properti dan RelayCommand untuk implementasi perintah.

Menginstal paket MVVM Toolkit NuGet

Anda perlu menginstal Toolkit MVVM di proyek WinUINotes dan WinUINotes.Bus .

Menggunakan Visual Studio

  1. Klik kanan pada proyek WinUINotes.Bus di Penjelajah Solusi.
  2. Pilih Kelola Paket NuGet.
  3. Cari CommunityToolkit.Mvvm dan instal versi stabil terbaru.
  4. Ulangi langkah-langkah ini untuk proyek WinUINotes .

Menggunakan .NET CLI

Atau, Anda dapat menggunakan .NET CLI untuk menginstal paket:

dotnet add WinUINotes.Bus package CommunityToolkit.Mvvm
dotnet add WinUINotes package CommunityToolkit.Mvvm

Keputusan desain untuk lapisan model

Saat Anda menerapkan MVVM, penting untuk memutuskan cara menyusun kelas model Anda sehubungan dengan ViewModels. Dalam tutorial ini, kelas model (Note dan AllNotes) bertanggung jawab atas representasi data, logika bisnis, dan memperbarui penyimpanan data. ViewModels menangani properti yang dapat diamati, mengubah pemberitahuan, dan perintah untuk interaksi UI.

Dalam implementasi yang lebih sederhana, Anda dapat menggunakan objek CLR (POCO) lama biasa untuk kelas model tanpa logika bisnis atau metode akses data. Dalam hal ini, ViewModels menangani semua operasi data melalui lapisan layanan. Namun, untuk tutorial ini, kelas model mencakup metode untuk memuat, menyimpan, dan menghapus catatan untuk memberikan pemisahan kekhawatiran yang lebih jelas dan menjaga ViewModels tetap fokus pada logika presentasi.

Memindahkan model Note

Pindahkan Note kelas ke proyek WinUINotes.Bus . Ini tetap menjadi kelas model sederhana dengan beberapa logika untuk representasi data dan manajemen status tetapi tanpa fitur Toolkit MVVM apa pun. ViewModels menangani properti yang dapat diamati dan mengubah pemberitahuan, bukan model itu sendiri.

  1. Di proyek WinUINotes.Bus , buat folder baru bernama Model.

  2. Pindahkan Note.cs file dari proyek WinUINotes ke folder WinUINotes.Bus/Models .

  3. Perbarui namespace agar sesuai dengan lokasi baru:

    namespace WinUINotes.Models
    {
        public class Note
        {
            // Existing code remains unchanged
            ...
        }
    }
    

Kelas Note ini adalah model data sederhana. Ini tidak memerlukan pemberitahuan perubahan karena ViewModels mengelola properti yang dapat diamati dan memberi tahu UI perubahan.

Pindahkan model AllNotes

Pindahkan AllNotes kelas ke proyek WinUINotes.Bus .

  1. Pindahkan AllNotes.cs file dari proyek WinUINotes ke folder WinUINotes.Bus/Models .

  2. Perbarui namespace agar sesuai dengan lokasi baru:

    namespace WinUINotes.Models
    {
        public class AllNotes
        {
            // Existing code remains unchanged
            ...
        }
    }
    

Seperti kelasnya Note , AllNotes adalah kelas model sederhana. ViewModel menangani perilaku yang dapat diamati dan mengelola kumpulan catatan.

Membuat AllNotesViewModel

  1. Di proyek WinUINotes.Bus , buat folder baru bernama ViewModels.

  2. Tambahkan file kelas baru bernama AllNotesViewModel.cs dengan konten berikut:

    using CommunityToolkit.Mvvm.ComponentModel;
    using CommunityToolkit.Mvvm.Input;
    using System.Collections.ObjectModel;
    using System.Threading.Tasks;
    using WinUINotes.Models;
    
    namespace WinUINotes.ViewModels
    {
        public partial class AllNotesViewModel : ObservableObject
        {
            private readonly AllNotes allNotes;
    
            [ObservableProperty]
            private ObservableCollection<Note> notes;
    
            public AllNotesViewModel()
            {
                allNotes = new AllNotes();
                notes = new ObservableCollection<Note>();
            }
    
            [RelayCommand]
            public async Task LoadAsync()
            {
                await allNotes.LoadNotes();
                Notes.Clear();
                foreach (var note in allNotes.Notes)
                {
                    Notes.Add(note);
                }
            }
        }
    }
    

Pengelola AllNotesViewModel mengelola kumpulan catatan yang ditampilkan di UI:

  • [ObservableProperty]: Bidang notes secara otomatis menghasilkan properti publik Notes dengan pemberitahuan perubahan. Saat koleksi Notes berubah, UI akan diperbarui secara otomatis.
  • allNotes model: Bidang privat ini menyimpan instans AllNotes model, yang menangani operasi data aktual.
  • [RelayCommand]: Atribut ini menghasilkan LoadCommand properti dari LoadAsync() metode , memungkinkan UI untuk memicu operasi pemuatan melalui pengikatan data.
  • LoadAsync() metode: Metode ini memuat catatan dari model, menghapus koleksi yang dapat diamati saat ini, dan mengisinya dengan catatan yang dimuat. Pola ini memastikan koleksi terikat UI tetap disinkronkan dengan data yang mendasar.

Pemisahan antara allNotes model (operasi data) dan Notes koleksi yang dapat diamati (pengikatan UI) adalah pola MVVM kunci yang menjaga tanggung jawab terpisah, dan memastikan Tampilan tetap sinkron dengan data dari ViewModel.

Pelajari selengkapnya di dokumen:

Membuat NoteViewModel

  1. Di folder ViewModels , tambahkan file kelas baru bernama NoteViewModel.cs:

    using CommunityToolkit.Mvvm.ComponentModel;
    using CommunityToolkit.Mvvm.Input;
    using System;
    using System.Threading.Tasks;
    using WinUINotes.Models;
    
    namespace WinUINotes.ViewModels
    {
        public partial class NoteViewModel : ObservableObject
        {
            private Note note;
    
            [ObservableProperty]
            [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
            [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
            private string filename = string.Empty;
    
            [ObservableProperty]
            [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
            private string text = string.Empty;
    
            [ObservableProperty]
            private DateTime date = DateTime.Now;
    
            public NoteViewModel()
            {
                this.note = new Note();
                this.Filename = note.Filename;
            }
    
            public void InitializeForExistingNote(Note note)
            {
                this.note = note;
                this.Filename = note.Filename;
                this.Text = note.Text;
                this.Date = note.Date;
            }
    
            [RelayCommand(CanExecute = nameof(CanSave))]
            private async Task Save()
            {
                note.Filename = this.Filename;
                note.Text = this.Text;
                note.Date = this.Date;
                await note.SaveAsync();
    
                // Check if the DeleteCommand can now execute
                // (it can if the file now exists)
                DeleteCommand.NotifyCanExecuteChanged();
            }
    
            private bool CanSave()
            {
                return note is not null
                    && !string.IsNullOrWhiteSpace(this.Text)
                    && !string.IsNullOrWhiteSpace(this.Filename);
            }
    
            [RelayCommand(CanExecute = nameof(CanDelete))]
            private async Task Delete()
            {
                await note.DeleteAsync();
                note = new Note();
            }
    
            private bool CanDelete()
            {
                // Note: This is to illustrate how commands can be
                // enabled or disabled.
                // In a real application, you shouldn't perform
                // file operations in your CanExecute logic.
                return note is not null
                    && !string.IsNullOrWhiteSpace(this.Filename)
                    && this.note.NoteFileExists();
            }
        }
    }
    

Ini NoteViewModel menunjukkan beberapa fitur utama dari Toolkit MVVM:

  • [ObservableProperty]: Bidang filename, text, dan date secara otomatis menghasilkan properti publik (Filename, Text, Date) dengan dukungan pemberitahuan perubahan.
  • [NotifyCanExecuteChangedFor]: Atribut ini memastikan bahwa ketika Filename atau Text berubah, perintah terkait mengevaluasi kembali apakah dapat dijalankan. Misalnya, saat Anda mengetik teks, tombol Simpan secara otomatis mengaktifkan atau menonaktifkan berdasarkan logika validasi.
  • [RelayCommand(CanExecute = nameof(CanSave))]: Atribut ini menghasilkan properti SaveCommand yang terikat ke metode validasi CanSave(). Perintah hanya diaktifkan ketika keduanya Text dan Filename memiliki nilai.
  • InitializeForExistingNote(): Metode ini memuat data catatan yang ada ke properti ViewModel, yang kemudian memperbarui UI melalui pengikatan data.
  • Simpan logika: Metode tersebut memperbarui model dasar Note dengan nilai properti saat ini dan memanggil SaveAsync() pada model. Setelah disimpan, ini memberi tahu DeleteCommand bahwa file harus dievaluasi ulang (karena file sekarang ada dan dapat dihapus).
  • Menghapus logika: Metode Delete() memanggil DeleteAsync() model catatan dan membuat catatan kosong baru.

Kemudian dalam tutorial ini, Anda mengintegrasikan layanan file untuk menangani operasi file aktual dan menggunakan kelas MVVM Toolkit WeakReferenceMessenger untuk memberi tahu bagian lain dari aplikasi ketika catatan dihapus sambil tetap digabungkan secara longgar.

Pelajari selengkapnya di dokumen:

Memperbarui tampilan untuk menggunakan ViewModels

Sekarang Anda perlu memperbarui halaman XAML anda untuk mengikat ke ViewModels baru.

Perbarui tampilan AllNotesPage

  1. Di AllNotesPage.xaml, perbarui pengikatan ItemsSourceItemsView untuk menggunakan properti ViewModel Notes :

    <ItemsView ItemsSource="{x:Bind viewModel.Notes}"
    ...
    
  2. AllNotesPage.xaml.cs Perbarui file agar terlihat seperti ini:

    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Navigation;
    using WinUINotes.ViewModels;
    
    namespace WinUINotes.Views
    {
        public sealed partial class AllNotesPage : Page
        {
            private AllNotesViewModel? viewModel;
    
            public AllNotesPage()
            {
                this.InitializeComponent();
                viewModel = new AllNotesViewModel();
            }
    
            private void NewNoteButton_Click(object sender, RoutedEventArgs e)
            {
                Frame.Navigate(typeof(NotePage));
            }
    
            private void ItemsView_ItemInvoked(ItemsView sender, ItemsViewItemInvokedEventArgs args)
            {
                Frame.Navigate(typeof(NotePage), args.InvokedItem);
            }
    
            protected override async void OnNavigatedTo(NavigationEventArgs e)
            {
                base.OnNavigatedTo(e);
    
                if (viewModel is not null)
                {
                    await viewModel.LoadAsync();
                }
            }
        }
    }
    

Dalam file code-behind ini, konstruktor langsung membuat instans AllNotesViewModel. Metode OnNavigatedTo() memanggil LoadAsync() metode pada ViewModel saat halaman dinavigasi. Metode ini memuat catatan dari penyimpanan dan memperbarui koleksi yang dapat diamati. Pola ini memastikan data selalu di-refresh saat pengguna menavigasi ke halaman semua catatan.

Kemudian dalam tutorial ini, Anda merefaktor kode ini untuk menggunakan injeksi dependensi, yang memungkinkan ViewModel disuntikkan ke konstruktor halaman alih-alih dibuat secara langsung. Pendekatan ini meningkatkan kemampuan pengujian dan memudahkan pengelolaan siklus hidup ViewModel.

Memperbarui tampilan NotePage

  1. Di NotePage.xaml, perbarui pengikatan TextBox untuk Text dan Header untuk menggunakan properti ViewModel. Perbarui tombol StackPanel untuk mengikat perintah alih-alih menggunakan Click peristiwa:

    ...
    <TextBox x:Name="NoteEditor"
             Text="{x:Bind noteVm.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             AcceptsReturn="True"
             TextWrapping="Wrap"
             PlaceholderText="Enter your note"
             Header="{x:Bind noteVm.Date.ToString()}"
             ScrollViewer.VerticalScrollBarVisibility="Auto"
             MaxWidth="400"
             Grid.Column="1"/>
    
    <StackPanel Orientation="Horizontal"
                HorizontalAlignment="Right"
                Spacing="4"
                Grid.Row="1" Grid.Column="1">
        <Button Content="Save" Command="{x:Bind noteVm.SaveCommand}"/>
        <Button Content="Delete" Command="{x:Bind noteVm.DeleteCommand}"/>
    </StackPanel>
    ...
    

    Anda juga mengatur UpdateSourceTrigger pada pengikatan TextBox.Text untuk memastikan perubahan dikirim ke ViewModel saat pengguna mengetik. Pengaturan ini memungkinkan tombol Save untuk mengaktifkan atau menonaktifkan secara real-time berdasarkan input.

  2. Di NotePage.xaml.cs, perbarui kode untuk menggunakan NoteViewModel:

    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Navigation;
    using WinUINotes.Models;
    using WinUINotes.ViewModels;
    
    namespace WinUINotes.Views
    {
        public sealed partial class NotePage : Page
        {
            private NoteViewModel? noteVm;
    
            public NotePage()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                base.OnNavigatedTo(e);
                noteVm = new NoteViewModel();
    
                if (e.Parameter is Note note && noteVm is not null)
                {
                    noteVm.InitializeForExistingNote(note);
                }
            }
        }
    }
    

    Peristiwa Click untuk Save dan Delete dihapus karena tombol sekarang terikat langsung pada perintah di ViewModel. NoteViewModel diinstansiasi dalam metode OnNavigatedTo(). Note Jika sebuah parameter diteruskan, itu akan menginisialisasi ViewModel dengan data catatan yang ada.

Pelajari selengkapnya di dokumen: