Meningkatkan aplikasi Anda dengan konsep MVVM
Seri tutorial ini dirancang untuk melanjutkan tutorial Membuat aplikasi .NET MAUI, yang membuat aplikasi pengambilan catatan. Di bagian seri ini, Anda akan mempelajari cara:
- Terapkan pola model-view-viewmodel (MVVM).
- Gunakan gaya tambahan string kueri untuk meneruskan data selama navigasi.
Kami sangat menyarankan agar Anda terlebih dahulu mengikuti tutorial Membuat aplikasi .NET MAUI, karena kode yang dibuat dalam tutorial tersebut adalah dasar untuk tutorial ini. Jika Anda kehilangan kode, atau ingin memulai dari awal, unduh proyek ini.
Memahami MVVM
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 model-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, dirawat, dan berkembang. Ini juga dapat secara signifikan meningkatkan peluang penggunaan kembali kode dan memungkinkan pengembang dan desainer UI untuk berkolaborasi dengan lebih mudah saat mengembangkan bagian masing-masing aplikasi.
Pola
Ada tiga komponen inti dalam pola MVVM: model, tampilan, dan model tampilan. Masing-masing melayani tujuan yang berbeda. Diagram berikut menunjukkan hubungan antara ketiga komponen.
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.
Kunci untuk menggunakan MVVM secara efektif terletak pada pemahaman cara memperhitungkan kode aplikasi ke dalam kelas yang benar dan bagaimana kelas berinteraksi.
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.
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.
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.
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.
Penting
Marshal .NET MAUI mengikat pembaruan ke utas UI. Saat menggunakan MVVM, ini memungkinkan Anda memperbarui properti viewmodel terikat data dari utas apa pun, dengan mesin pengikatan .NET MAUI yang membawa pembaruan ke utas UI.
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.
Memperbarui model
Di bagian pertama tutorial ini, Anda akan menerapkan pola model-view-viewmodel (MVVM). Untuk memulai, buka solusi Notes.sln di Visual Studio.
Membersihkan model
Dalam tutorial sebelumnya, jenis model bertindak sebagai model (data) dan sebagai model tampilan (persiapan data), yang dipetakan langsung ke tampilan. Tabel berikut ini menjelaskan model:
File kode | Deskripsi |
---|---|
Model/About.cs | Model.About Berisi bidang baca-saja yang menjelaskan aplikasi itu sendiri, seperti judul dan versi aplikasi. |
Model/Note.cs | Model.Note Mewakili catatan. |
Model/AllNotes.cs | Model.AllNotes Memuat semua catatan pada perangkat ke dalam koleksi. |
Memikirkan aplikasi itu sendiri, hanya ada satu bagian data yang digunakan oleh aplikasi, yaitu Note
. Catatan dimuat dari perangkat, disimpan ke perangkat, dan diedit melalui UI aplikasi. Benar-benar tidak perlu untuk About
model dan AllNotes
. Hapus model ini dari proyek:
- Temukan panel Penjelajah Solusi Visual Studio.
- Klik kanan pada file Model\About.cs dan pilih Hapus. Tekan OK untuk menghapus file.
- Klik kanan pada file Model\AllNotes.cs dan pilih Hapus. Tekan OK untuk menghapus file.
Satu-satunya file model yang tersisa adalah file Models\Note.cs .
Memperbarui model
Model berisi Note
:
- Pengidentifikasi unik, yang merupakan nama file catatan seperti yang disimpan di perangkat.
- Teks catatan.
- Tanggal untuk menunjukkan kapan catatan dibuat atau terakhir diperbarui.
Saat ini, memuat dan menyimpan model dilakukan melalui tampilan, dan dalam beberapa kasus, oleh jenis model lain yang baru saja Anda hapus. Kode yang Anda miliki untuk jenisnya Note
harus sebagai berikut:
namespace Notes.Models;
internal class Note
{
public string Filename { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
}
Model Note
ini akan diperluas untuk menangani pemuatan, penyimpanan, dan penghapusan catatan.
Di panel Penjelajah Solusi Visual Studio, klik dua kali model\Note.cs.
Di editor kode, tambahkan dua metode berikut ke
Note
kelas . Metode ini berbasis instans dan menangani penyimpanan atau penghapusan catatan saat ini ke atau dari perangkat, masing-masing:public void Save() => File.WriteAllText(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename), Text); public void Delete() => File.Delete(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename));
Aplikasi ini perlu memuat catatan dengan dua cara, memuat catatan individual dari file dan memuat semua catatan di perangkat. Kode untuk menangani pemuatan dapat menjadi
static
anggota, tidak memerlukan instans kelas untuk dijalankan.Tambahkan kode berikut ke kelas untuk memuat catatan berdasarkan nama file:
public static Note Load(string filename) { filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename); if (!File.Exists(filename)) throw new FileNotFoundException("Unable to find file on local storage.", filename); return new() { Filename = Path.GetFileName(filename), Text = File.ReadAllText(filename), Date = File.GetLastWriteTime(filename) }; }
Kode ini mengambil nama file sebagai parameter, membangun jalur ke tempat catatan disimpan di perangkat, dan mencoba memuat file jika ada.
Cara kedua untuk memuat catatan adalah dengan menghitung semua catatan pada perangkat dan memuatnya ke dalam koleksi.
Tambahkan kode berikut ke kelas :
public static IEnumerable<Note> LoadAll() { // Get the folder where the notes are stored. string appDataPath = FileSystem.AppDataDirectory; // Use Linq extensions to load the *.notes.txt files. return Directory // Select the file names from the directory .EnumerateFiles(appDataPath, "*.notes.txt") // Each file name is used to load a note .Select(filename => Note.Load(Path.GetFileName(filename))) // With the final collection of notes, order them by date .OrderByDescending(note => note.Date); }
Kode ini mengembalikan kumpulan jenis model yang dapat dijumlahkan
Note
dengan mengambil file pada perangkat yang cocok dengan pola file catatan: *.notes.txt. Setiap nama file diteruskan keLoad
metode , memuat catatan individual. Akhirnya, kumpulan catatan diurutkan berdasarkan tanggal setiap catatan dan dikembalikan ke pemanggil.Terakhir, tambahkan konstruktor ke kelas yang mengatur nilai default untuk properti, termasuk nama file acak:
public Note() { Filename = $"{Path.GetRandomFileName()}.notes.txt"; Date = DateTime.Now; Text = ""; }
Kode Note
kelas akan terlihat seperti berikut:
namespace Notes.Models;
internal class Note
{
public string Filename { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
public Note()
{
Filename = $"{Path.GetRandomFileName()}.notes.txt";
Date = DateTime.Now;
Text = "";
}
public void Save() =>
File.WriteAllText(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename), Text);
public void Delete() =>
File.Delete(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename));
public static Note Load(string filename)
{
filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename);
if (!File.Exists(filename))
throw new FileNotFoundException("Unable to find file on local storage.", filename);
return
new()
{
Filename = Path.GetFileName(filename),
Text = File.ReadAllText(filename),
Date = File.GetLastWriteTime(filename)
};
}
public static IEnumerable<Note> LoadAll()
{
// Get the folder where the notes are stored.
string appDataPath = FileSystem.AppDataDirectory;
// Use Linq extensions to load the *.notes.txt files.
return Directory
// Select the file names from the directory
.EnumerateFiles(appDataPath, "*.notes.txt")
// Each file name is used to load a note
.Select(filename => Note.Load(Path.GetFileName(filename)))
// With the final collection of notes, order them by date
.OrderByDescending(note => note.Date);
}
}
Sekarang setelah Note
model selesai, model tampilan dapat dibuat.
Membuat viewmodel Tentang
Sebelum menambahkan model tampilan ke proyek, tambahkan referensi ke MVVM Community Toolkit. Pustaka ini tersedia di NuGet, dan menyediakan jenis dan sistem yang membantu menerapkan pola MVVM.
Di panel Penjelajah Solusi Visual Studio, klik kanan proyek >Catatan Kelola Paket NuGet.
Pilih tab Telusuri.
Cari communitytoolkit mvvm dan pilih
CommunityToolkit.Mvvm
paket, yang seharusnya menjadi hasil pertama.Pastikan setidaknya versi 8 dipilih. Tutorial ini ditulis menggunakan versi 8.0.0.
Selanjutnya, pilih Instal dan terima perintah apa pun yang ditampilkan.
Sekarang Anda siap untuk mulai memperbarui proyek dengan menambahkan model tampilan.
Memisahkan dengan model tampilan
Hubungan view-to-viewmodel sangat bergantung pada sistem pengikatan yang disediakan oleh .NET Multi-platform App UI (.NET MAUI). Aplikasi ini sudah menggunakan pengikatan dalam tampilan untuk menampilkan daftar catatan dan untuk menyajikan teks dan tanggal satu catatan. Logika aplikasi saat ini disediakan oleh kode tampilan di belakang dan langsung terkait dengan tampilan. Misalnya, saat pengguna mengedit catatan dan menekan tombol Simpan , Clicked
peristiwa untuk tombol dinaikkan. Kemudian, code-behind untuk penanganan aktivitas menyimpan teks catatan ke file dan menavigasi ke layar sebelumnya.
Memiliki logika aplikasi di kode-belakang tampilan dapat menjadi masalah saat tampilan berubah. Misalnya jika tombol diganti dengan kontrol input yang berbeda, atau nama kontrol diubah, penanganan aktivitas mungkin menjadi tidak valid. Terlepas dari bagaimana tampilan dirancang, tujuan tampilan adalah untuk memanggil semacam logika aplikasi dan menyajikan informasi kepada pengguna. Untuk aplikasi ini, tombol Save
menyimpan catatan lalu menavigasi kembali ke layar sebelumnya.
Viewmodel memberi aplikasi tempat tertentu untuk menempatkan logika aplikasi terlepas dari bagaimana UI dirancang atau bagaimana data dimuat atau disimpan. Viewmodel adalah lem yang mewakili dan berinteraksi dengan model data atas nama tampilan.
Model tampilan disimpan dalam folder ViewModels .
- Temukan panel Penjelajah Solusi Visual Studio.
- Klik kanan pada proyek Catatan dan pilih Tambahkan>Folder Baru. Beri nama folder ViewModels.
- Klik kanan pada folder> ViewModels Tambahkan>Kelas dan beri nama AboutViewModel.cs.
- Ulangi langkah sebelumnya dan buat dua model tampilan lagi:
- NoteViewModel.cs
- NotesViewModel.cs
Pengaturan Anda akan terlihat seperti gambar berikut:
Tentang tampilan viewmodel dan Tentang
Tampilan Tentang menampilkan beberapa data di layar dan secara opsional menavigasi ke situs web dengan informasi lebih lanjut. Karena tampilan ini tidak memiliki data apa pun untuk diubah, seperti dengan kontrol entri teks atau memilih item dari daftar, ini adalah kandidat yang baik untuk menunjukkan penambahan viewmodel. Untuk viewmodel Tentang, tidak ada model dukungan.
Buat viewmodel Tentang:
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada ViewModels\AboutViewModel.cs.
Tempelkan dalam kode berikut:
using CommunityToolkit.Mvvm.Input; using System.Windows.Input; namespace Notes.ViewModels; internal class AboutViewModel { public string Title => AppInfo.Name; public string Version => AppInfo.VersionString; public string MoreInfoUrl => "https://aka.ms/maui"; public string Message => "This app is written in XAML and C# with .NET MAUI."; public ICommand ShowMoreInfoCommand { get; } public AboutViewModel() { ShowMoreInfoCommand = new AsyncRelayCommand(ShowMoreInfo); } async Task ShowMoreInfo() => await Launcher.Default.OpenAsync(MoreInfoUrl); }
Cuplikan kode sebelumnya berisi beberapa properti yang mewakili informasi tentang aplikasi, seperti nama dan versi. Cuplikan ini persis sama dengan model Tentang yang Anda hapus sebelumnya. Namun, viewmodel ini berisi konsep baru, ShowMoreInfoCommand
properti perintah.
Perintah adalah tindakan yang dapat diikat yang memanggil kode, dan merupakan tempat yang bagus untuk menempatkan logika aplikasi. Dalam contoh ini, ShowMoreInfoCommand
menunjuk ke ShowMoreInfo
metode , yang membuka browser web ke halaman tertentu. Anda akan mempelajari selengkapnya tentang sistem perintah di bagian berikutnya.
Tentang tampilan
Tampilan Tentang perlu diubah sedikit untuk menghubungkannya ke viewmodel yang dibuat di bagian sebelumnya. Dalam file Views\AboutPage.xaml, terapkan perubahan berikut:
xmlns:models
Perbarui namespace XML kexmlns:viewModels
dan targetkanNotes.ViewModels
namespace .NET.ContentPage.BindingContext
Ubah properti menjadi instansAbout
baru viewmodel.- Hapus penanganan aktivitas tombol
Clicked
dan gunakanCommand
properti .
Perbarui tampilan Tentang:
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada Views\AboutPage.xaml.
Tempelkan dalam kode berikut:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewModels="clr-namespace:Notes.ViewModels" x:Class="Notes.Views.AboutPage"> <ContentPage.BindingContext> <viewModels:AboutViewModel /> </ContentPage.BindingContext> <VerticalStackLayout Spacing="10" Margin="10"> <HorizontalStackLayout Spacing="10"> <Image Source="dotnet_bot.png" SemanticProperties.Description="The dot net bot waving hello!" HeightRequest="64" /> <Label FontSize="22" FontAttributes="Bold" Text="{Binding Title}" VerticalOptions="End" /> <Label FontSize="22" Text="{Binding Version}" VerticalOptions="End" /> </HorizontalStackLayout> <Label Text="{Binding Message}" /> <Button Text="Learn more..." Command="{Binding ShowMoreInfoCommand}" /> </VerticalStackLayout> </ContentPage>
Cuplikan kode sebelumnya menyoroti baris yang telah berubah dalam versi tampilan ini.
Perhatikan bahwa tombol menggunakan Command
properti . Banyak kontrol memiliki Command
properti yang dipanggil saat pengguna berinteraksi dengan kontrol. Ketika digunakan dengan tombol, perintah dipanggil ketika pengguna menekan tombol , mirip dengan bagaimana Clicked
penanganan aktivitas dipanggil, kecuali bahwa Anda dapat mengikat Command
ke properti di viewmodel.
Dalam tampilan ini, ketika pengguna menekan tombol , dipanggil Command
. Command
terikat ke ShowMoreInfoCommand
properti di viewmodel, dan ketika dipanggil, menjalankan kode dalam ShowMoreInfo
metode , yang membuka browser web ke halaman tertentu.
Bersihkan Tentang kode di belakang
Tombol ShowMoreInfo
tidak menggunakan penanganan aktivitas, sehingga LearnMore_Clicked
kode harus dihapus dari file Views\AboutPage.xaml.cs . Hapus kode tersebut, kelas hanya boleh berisi konstruktor:
Di panel Penjelajah Solusi Visual Studio, klik dua kali tampilan\AboutPage.xaml.cs.
Tip
Anda mungkin perlu memperluas Views\AboutPage.xaml untuk menampilkan file.
Ganti kode dengan cuplikan berikut:
namespace Notes.Views; public partial class AboutPage : ContentPage { public AboutPage() { InitializeComponent(); } }
Membuat viewmodel Catatan
Tujuan memperbarui tampilan Catatan adalah untuk memindahkan fungsionalitas sebanyak mungkin dari kode XAML di belakang dan meletakkannya di viewmodel Catatan.
Catatan viewmodel
Berdasarkan apa yang diperlukan tampilan Catatan, viewmodel Catatan perlu menyediakan item berikut:
- Teks catatan.
- Tanggal/waktu catatan dibuat atau terakhir diperbarui.
- Perintah yang menyimpan catatan.
- Perintah yang menghapus catatan.
Buat viewmodel Catatan:
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada ViewModels\NoteViewModel.cs.
Ganti kode dalam file ini dengan cuplikan berikut:
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.ComponentModel; using System.Windows.Input; namespace Notes.ViewModels; internal class NoteViewModel : ObservableObject, IQueryAttributable { private Models.Note _note; }
Kode ini adalah viewmodel kosong
Note
tempat Anda akan menambahkan properti dan perintah untuk mendukungNote
tampilan. Perhatikan bahwaCommunityToolkit.Mvvm.ComponentModel
namespace sedang diimpor. Namespace layanan ini menyediakan yangObservableObject
digunakan sebagai kelas dasar. Anda akan mempelajari lebihObservableObject
lanjut di langkah berikutnya. NamespaceCommunityToolkit.Mvvm.Input
layanan juga diimpor. Namespace ini menyediakan beberapa jenis perintah yang memanggil metode secara asinkron.Model
Models.Note
sedang disimpan sebagai bidang privat. Properti dan metode kelas ini akan menggunakan bidang ini.Tambahkan properti berikut ke kelas :
public string Text { get => _note.Text; set { if (_note.Text != value) { _note.Text = value; OnPropertyChanged(); } } } public DateTime Date => _note.Date; public string Identifier => _note.Filename;
Properti
Date
danIdentifier
adalah properti sederhana yang hanya mengambil nilai yang sesuai dari model.Tip
Untuk properti,
=>
sintaks membuat properti get-only di mana pernyataan di sebelah kanan=>
harus dievaluasi ke nilai yang akan dikembalikan.Properti
Text
pertama-tama memeriksa apakah nilai yang ditetapkan adalah nilai yang berbeda. Jika nilainya berbeda, nilai tersebut diteruskan ke properti model, dan metode dipanggilOnPropertyChanged
.Metode
OnPropertyChanged
ini disediakan olehObservableObject
kelas dasar. Metode ini menggunakan nama kode panggilan, dalam hal ini, nama properti Teks, dan menaikkanObservableObject.PropertyChanged
peristiwa. Kejadian ini memberikan nama properti kepada pelanggan peristiwa apa pun. Sistem pengikatan yang disediakan oleh .NET MAUI mengenali peristiwa ini, dan memperbarui pengikatan terkait di UI. Untuk viewmodel Catatan, saatText
properti berubah, peristiwa dinaikkan, dan elemen UI apa pun yang terikat keText
properti diberi tahu bahwa properti berubah.Tambahkan properti perintah berikut ke kelas , yang merupakan perintah yang dapat diikat tampilan:
public ICommand SaveCommand { get; private set; } public ICommand DeleteCommand { get; private set; }
Tambahkan konstruktor berikut ke kelas :
public NoteViewModel() { _note = new Models.Note(); SaveCommand = new AsyncRelayCommand(Save); DeleteCommand = new AsyncRelayCommand(Delete); } public NoteViewModel(Models.Note note) { _note = note; SaveCommand = new AsyncRelayCommand(Save); DeleteCommand = new AsyncRelayCommand(Delete); }
Kedua konstruktor ini digunakan untuk membuat viewmodel dengan model backing baru, yang merupakan catatan kosong, atau untuk membuat viewmodel yang menggunakan instans model yang ditentukan.
Konstruktor juga menyiapkan perintah untuk viewmodel. Selanjutnya, tambahkan kode untuk perintah ini.
Save
Tambahkan metode danDelete
:private async Task Save() { _note.Date = DateTime.Now; _note.Save(); await Shell.Current.GoToAsync($"..?saved={_note.Filename}"); } private async Task Delete() { _note.Delete(); await Shell.Current.GoToAsync($"..?deleted={_note.Filename}"); }
Metode ini dipanggil oleh perintah terkait. Mereka melakukan tindakan terkait pada model dan membuat aplikasi menavigasi ke halaman sebelumnya. Parameter string kueri ditambahkan ke
..
jalur navigasi, menunjukkan tindakan mana yang diambil dan pengidentifikasi unik catatan.Selanjutnya, tambahkan
ApplyQueryAttributes
metode ke kelas , yang memenuhi persyaratan IQueryAttributable antarmuka:void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) { if (query.ContainsKey("load")) { _note = Models.Note.Load(query["load"].ToString()); RefreshProperties(); } }
Saat halaman, atau konteks pengikatan halaman, mengimplementasikan antarmuka ini, parameter string kueri yang digunakan dalam navigasi diteruskan ke
ApplyQueryAttributes
metode . Viewmodel ini digunakan sebagai konteks pengikatan untuk tampilan Catatan. Saat tampilan Catatan dinavigasi, konteks pengikatan tampilan (viewmodel ini) diteruskan ke parameter string kueri yang digunakan selama navigasi.Kode ini memeriksa apakah
load
kunci disediakan dalamquery
kamus. Jika kunci ini ditemukan, nilainya harus menjadi pengidentifikasi (nama file) catatan yang akan dimuat. Catatan tersebut dimuat dan ditetapkan sebagai objek model yang mendasar dari instans viewmodel ini.Terakhir, tambahkan dua metode pembantu ini ke kelas :
public void Reload() { _note = Models.Note.Load(_note.Filename); RefreshProperties(); } private void RefreshProperties() { OnPropertyChanged(nameof(Text)); OnPropertyChanged(nameof(Date)); }
Metode
Reload
ini adalah metode pembantu yang me-refresh objek model pencadangan, memuat ulang dari penyimpanan perangkatMetode ini
RefreshProperties
adalah metode pembantuText
lain untuk memastikan bahwa setiap pelanggan yang terikat ke objek ini diberi tahu bahwa properti danDate
telah berubah. Karena model yang mendasar (_note
bidang) diubah saat catatan dimuat selama navigasi,Text
properti danDate
tidak benar-benar diatur ke nilai baru. Karena properti ini tidak diatur secara langsung, pengikatan apa pun yang dilampirkan ke properti tersebut tidak akan diberi tahu karenaOnPropertyChanged
tidak dipanggil untuk setiap properti.RefreshProperties
memastikan pengikatan ke properti ini disegarkan.
Kode untuk kelas akan terlihat seperti cuplikan berikut:
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Windows.Input;
namespace Notes.ViewModels;
internal class NoteViewModel : ObservableObject, IQueryAttributable
{
private Models.Note _note;
public string Text
{
get => _note.Text;
set
{
if (_note.Text != value)
{
_note.Text = value;
OnPropertyChanged();
}
}
}
public DateTime Date => _note.Date;
public string Identifier => _note.Filename;
public ICommand SaveCommand { get; private set; }
public ICommand DeleteCommand { get; private set; }
public NoteViewModel()
{
_note = new Models.Note();
SaveCommand = new AsyncRelayCommand(Save);
DeleteCommand = new AsyncRelayCommand(Delete);
}
public NoteViewModel(Models.Note note)
{
_note = note;
SaveCommand = new AsyncRelayCommand(Save);
DeleteCommand = new AsyncRelayCommand(Delete);
}
private async Task Save()
{
_note.Date = DateTime.Now;
_note.Save();
await Shell.Current.GoToAsync($"..?saved={_note.Filename}");
}
private async Task Delete()
{
_note.Delete();
await Shell.Current.GoToAsync($"..?deleted={_note.Filename}");
}
void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query.ContainsKey("load"))
{
_note = Models.Note.Load(query["load"].ToString());
RefreshProperties();
}
}
public void Reload()
{
_note = Models.Note.Load(_note.Filename);
RefreshProperties();
}
private void RefreshProperties()
{
OnPropertyChanged(nameof(Text));
OnPropertyChanged(nameof(Date));
}
}
Tampilan catatan
Sekarang setelah viewmodel dibuat, perbarui tampilan Catatan. Dalam file Views\NotePage.xaml, terapkan perubahan berikut:
xmlns:viewModels
Tambahkan namespace XML yang menargetkanNotes.ViewModels
namespace .NET.BindingContext
Tambahkan ke halaman.- Hapus penanganan aktivitas tombol
Clicked
hapus dan simpan dan ganti dengan perintah.
Perbarui tampilan Catatan:
- Di panel Penjelajah Solusi Visual Studio, klik dua kali pada Views\NotePage.xaml untuk membuka editor XAML.
- Tempelkan dalam kode berikut:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:Notes.ViewModels"
x:Class="Notes.Views.NotePage"
Title="Note">
<ContentPage.BindingContext>
<viewModels:NoteViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout Spacing="10" Margin="5">
<Editor x:Name="TextEditor"
Placeholder="Enter your note"
Text="{Binding Text}"
HeightRequest="100" />
<Grid ColumnDefinitions="*,*" ColumnSpacing="4">
<Button Text="Save"
Command="{Binding SaveCommand}"/>
<Button Grid.Column="1"
Text="Delete"
Command="{Binding DeleteCommand}"/>
</Grid>
</VerticalStackLayout>
</ContentPage>
Sebelumnya, tampilan ini tidak mendeklarasikan konteks pengikatan, karena disediakan oleh kode di belakang halaman itu sendiri. Mengatur konteks pengikatan langsung di XAML menyediakan dua hal:
Pada run-time, saat halaman dinavigasi, halaman akan menampilkan catatan kosong. Ini karena konstruktor tanpa parameter untuk konteks pengikatan, viewmodel, dipanggil. Jika Anda ingat dengan benar, konstruktor tanpa parameter untuk viewmodel Catatan membuat catatan kosong.
Intellisense di editor XAML menunjukkan properti yang tersedia segera setelah Anda mulai mengetik
{Binding
sintaksis. Sintaks juga divalidasi dan memberi tahu Anda tentang nilai yang tidak valid. Coba ubah sintaks pengikatan untuk keSaveCommand
Save123Command
. Jika Anda mengarahkan kursor mouse ke teks, Anda akan melihat bahwa tipsalat ditampilkan yang memberi tahu Anda bahwa Save123Command tidak ditemukan. Pemberitahuan ini tidak dianggap sebagai kesalahan karena pengikatan bersifat dinamis, ini benar-benar peringatan kecil yang dapat membantu Anda melihat ketika Anda mengetik properti yang salah.Jika Anda mengubah SaveCommand ke nilai yang berbeda, pulihkan sekarang.
Membersihkan kode Catatan di belakang
Sekarang setelah interaksi dengan tampilan berubah dari penanganan aktivitas menjadi perintah, buka file Views\NotePage.xaml.cs dan ganti semua kode dengan kelas yang hanya berisi konstruktor:
Di panel Penjelajah Solusi Visual Studio, klik dua kali tampilan\NotePage.xaml.cs.
Tip
Anda mungkin perlu memperluas Views\NotePage.xaml untuk menampilkan file.
Ganti kode dengan cuplikan berikut:
namespace Notes.Views; public partial class NotePage : ContentPage { public NotePage() { InitializeComponent(); } }
Membuat viewmodel Catatan
Pasangan tampilan-viewmodel akhir adalah tampilan Catatanmodel dan AllNotes. Namun, tampilan saat ini mengikat langsung ke model, yang dihapus pada awal tutorial ini. Tujuan dalam memperbarui tampilan AllNotes adalah untuk memindahkan fungsionalitas sebanyak mungkin dari kode XAML-belakang dan meletakkannya di viewmodel. Sekali lagi, manfaatnya adalah tampilan dapat mengubah desainnya dengan sedikit efek pada kode Anda.
Viewmodel catatan
Berdasarkan apa yang akan ditampilkan tampilan AllNotes dan interaksi apa yang akan dilakukan pengguna, viewmodel Catatan harus menyediakan item berikut:
- Kumpulan catatan.
- Perintah untuk menangani navigasi ke catatan.
- Perintah untuk membuat catatan baru.
- Perbarui daftar catatan saat dibuat, dihapus, atau diubah.
Buat viewmodel Catatan:
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada ViewModels\NotesViewModel.cs.
Ganti kode dalam file ini dengan kode berikut:
using CommunityToolkit.Mvvm.Input; using System.Collections.ObjectModel; using System.Windows.Input; namespace Notes.ViewModels; internal class NotesViewModel: IQueryAttributable { }
Kode ini kosong di
NotesViewModel
mana Anda akan menambahkan properti dan perintah untuk mendukungAllNotes
tampilan.NotesViewModel
Dalam kode kelas, tambahkan properti berikut:public ObservableCollection<ViewModels.NoteViewModel> AllNotes { get; } public ICommand NewCommand { get; } public ICommand SelectNoteCommand { get; }
Properti
AllNotes
adalah yangObservableCollection
menyimpan semua catatan yang dimuat dari perangkat. Dua perintah akan digunakan oleh tampilan untuk memicu tindakan membuat catatan atau memilih catatan yang sudah ada.Tambahkan konstruktor tanpa parameter ke kelas , yang menginisialisasi perintah dan memuat catatan dari model:
public NotesViewModel() { AllNotes = new ObservableCollection<ViewModels.NoteViewModel>(Models.Note.LoadAll().Select(n => new NoteViewModel(n))); NewCommand = new AsyncRelayCommand(NewNoteAsync); SelectNoteCommand = new AsyncRelayCommand<ViewModels.NoteViewModel>(SelectNoteAsync); }
Perhatikan bahwa
AllNotes
koleksi menggunakanModels.Note.LoadAll
metode untuk mengisi koleksi yang dapat diamati dengan catatan. Metode mengembalikanLoadAll
catatan sebagaiModels.Note
jenis, tetapi koleksi yang dapat diamati adalah kumpulan jenisViewModels.NoteViewModel
. Kode ini menggunakanSelect
ekstensi Linq untuk membuat instans viewmodel dari model catatan yang dikembalikan dariLoadAll
.Buat metode yang ditargetkan oleh perintah:
private async Task NewNoteAsync() { await Shell.Current.GoToAsync(nameof(Views.NotePage)); } private async Task SelectNoteAsync(ViewModels.NoteViewModel note) { if (note != null) await Shell.Current.GoToAsync($"{nameof(Views.NotePage)}?load={note.Identifier}"); }
Perhatikan bahwa
NewNoteAsync
metode tidak mengambil parameter saatSelectNoteAsync
tidak. Perintah dapat secara opsional memiliki satu parameter yang disediakan saat perintah dipanggil. Untuk metode ,SelectNoteAsync
parameter mewakili catatan yang sedang dipilih.Terakhir, terapkan
IQueryAttributable.ApplyQueryAttributes
metode :void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) { if (query.ContainsKey("deleted")) { string noteId = query["deleted"].ToString(); NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault(); // If note exists, delete it if (matchedNote != null) AllNotes.Remove(matchedNote); } else if (query.ContainsKey("saved")) { string noteId = query["saved"].ToString(); NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault(); // If note is found, update it if (matchedNote != null) matchedNote.Reload(); // If note isn't found, it's new; add it. else AllNotes.Add(new NoteViewModel(Note.Load(noteId))); } }
Viewmodel Catatan yang dibuat di langkah tutorial sebelumnya, menggunakan navigasi saat catatan disimpan atau dihapus. Viewmodel dinavigasi kembali ke tampilan AllNotes, yang dikaitkan dengan viewmodel ini. Kode ini mendeteksi apakah string kueri berisi
deleted
kunci atausaved
. Nilai kunci adalah pengidentifikasi unik catatan.Jika catatan dihapus, catatan tersebut dicocokkan dalam
AllNotes
koleksi oleh pengidentifikasi yang disediakan, dan dihapus.Ada dua kemungkinan alasan catatan disimpan. Catatan baru saja dibuat atau catatan yang sudah ada diubah. Jika catatan sudah ada dalam
AllNotes
koleksi, ini adalah catatan yang diperbarui. Dalam hal ini, instans catatan dalam koleksi hanya perlu di-refresh. Jika catatan hilang dari koleksi, ini adalah catatan baru dan harus ditambahkan ke koleksi.
Kode untuk kelas akan terlihat seperti cuplikan berikut:
using CommunityToolkit.Mvvm.Input;
using Notes.Models;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace Notes.ViewModels;
internal class NotesViewModel : IQueryAttributable
{
public ObservableCollection<ViewModels.NoteViewModel> AllNotes { get; }
public ICommand NewCommand { get; }
public ICommand SelectNoteCommand { get; }
public NotesViewModel()
{
AllNotes = new ObservableCollection<ViewModels.NoteViewModel>(Models.Note.LoadAll().Select(n => new NoteViewModel(n)));
NewCommand = new AsyncRelayCommand(NewNoteAsync);
SelectNoteCommand = new AsyncRelayCommand<ViewModels.NoteViewModel>(SelectNoteAsync);
}
private async Task NewNoteAsync()
{
await Shell.Current.GoToAsync(nameof(Views.NotePage));
}
private async Task SelectNoteAsync(ViewModels.NoteViewModel note)
{
if (note != null)
await Shell.Current.GoToAsync($"{nameof(Views.NotePage)}?load={note.Identifier}");
}
void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query.ContainsKey("deleted"))
{
string noteId = query["deleted"].ToString();
NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
// If note exists, delete it
if (matchedNote != null)
AllNotes.Remove(matchedNote);
}
else if (query.ContainsKey("saved"))
{
string noteId = query["saved"].ToString();
NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
// If note is found, update it
if (matchedNote != null)
matchedNote.Reload();
// If note isn't found, it's new; add it.
else
AllNotes.Add(new NoteViewModel(Note.Load(noteId)));
}
}
}
Tampilan AllNotes
Sekarang setelah viewmodel dibuat, perbarui tampilan AllNotes untuk menunjuk ke properti viewmodel. Dalam file Views\AllNotesPage.xaml, terapkan perubahan berikut:
xmlns:viewModels
Tambahkan namespace XML yang menargetkanNotes.ViewModels
namespace .NET.BindingContext
Tambahkan ke halaman.- Hapus peristiwa tombol
Clicked
toolbar dan gunakanCommand
properti . CollectionView
Ubah untuk mengikat keItemSource
AllNotes
.CollectionView
Ubah perintah untuk menggunakan untuk bereaksi terhadap ketika item yang dipilih berubah.
Perbarui tampilan AllNotes:
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada Views\AllNotesPage.xaml.
Tempelkan dalam kode berikut:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewModels="clr-namespace:Notes.ViewModels" x:Class="Notes.Views.AllNotesPage" Title="Your Notes"> <ContentPage.BindingContext> <viewModels:NotesViewModel /> </ContentPage.BindingContext> <!-- Add an item to the toolbar --> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" Command="{Binding NewCommand}" IconImageSource="{FontImage Glyph='+', Color=Black, Size=22}" /> </ContentPage.ToolbarItems> <!-- Display notes in a list --> <CollectionView x:Name="notesCollection" ItemsSource="{Binding AllNotes}" Margin="20" SelectionMode="Single" SelectionChangedCommand="{Binding SelectNoteCommand}" SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}"> <!-- Designate how the collection of items are laid out --> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Vertical" ItemSpacing="10" /> </CollectionView.ItemsLayout> <!-- Define the appearance of each item in the list --> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout> <Label Text="{Binding Text}" FontSize="22"/> <Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage>
Toolbar tidak lagi menggunakan Clicked
peristiwa dan sebaliknya menggunakan perintah.
Mendukung CollectionView
perintah dengan SelectionChangedCommand
properti dan SelectionChangedCommandParameter
. Dalam XAML yang diperbarui, SelectionChangedCommand
properti terikat ke viewmodel SelectNoteCommand
, yang berarti perintah dipanggil saat item yang dipilih berubah. Ketika perintah dipanggil, SelectionChangedCommandParameter
nilai properti diteruskan ke perintah .
Lihat pengikatan yang digunakan untuk CollectionView
:
<CollectionView x:Name="notesCollection"
ItemsSource="{Binding AllNotes}"
Margin="20"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectNoteCommand}"
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
Properti SelectionChangedCommandParameter
menggunakan Source={RelativeSource Self}
pengikatan. Mereferensikan Self
objek saat ini, yaitu CollectionView
. Perhatikan bahwa jalur pengikatan adalah SelectedItem
properti . Ketika perintah dipanggil dengan mengubah item yang dipilih, SelectNoteCommand
perintah dipanggil dan item yang dipilih diteruskan ke perintah sebagai parameter.
Membersihkan kode AllNotes di belakang
Sekarang setelah interaksi dengan tampilan berubah dari penanganan aktivitas menjadi perintah, buka file Views\AllNotesPage.xaml.cs dan ganti semua kode dengan kelas yang hanya berisi konstruktor:
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada Views\AllNotesPage.xaml.cs.
Tip
Anda mungkin perlu memperluas Views\AllNotesPage.xaml untuk menampilkan file.
Ganti kode dengan cuplikan berikut:
namespace Notes.Views; public partial class AllNotesPage : ContentPage { public AllNotesPage() { InitializeComponent(); } }
Menjalankan aplikasi
Anda sekarang dapat menjalankan aplikasi, dan semuanya berfungsi. Namun, ada dua masalah dengan perilaku aplikasi:
- Jika Anda memilih catatan, yang membuka editor, tekan Simpan, lalu coba pilih catatan yang sama, catatan tersebut tidak berfungsi.
- Setiap kali catatan diubah atau ditambahkan, daftar catatan tidak diurutkan ulang untuk menampilkan catatan terbaru di bagian atas.
Kedua masalah ini diperbaiki pada langkah tutorial berikutnya.
Memperbaiki perilaku aplikasi
Sekarang setelah kode aplikasi dapat mengkompilasi dan menjalankan, Anda mungkin telah memperhatikan bahwa ada dua kelemahan dengan bagaimana aplikasi berperilaku. Aplikasi ini tidak memungkinkan Anda memilih kembali catatan yang sudah dipilih, dan daftar catatan tidak diurutkan ulang setelah catatan dibuat atau diubah.
Mendapatkan catatan ke bagian atas daftar
Pertama, perbaiki masalah pengurusan ulang dengan daftar catatan. Dalam file ViewModels\NotesViewModel.cs, AllNotes
koleksi berisi semua catatan yang akan disajikan kepada pengguna. Sayangnya, kelemahan untuk menggunakan ObservableCollection
adalah harus diurutkan secara manual. Untuk mendapatkan item baru atau yang diperbarui ke bagian atas daftar, lakukan langkah-langkah berikut:
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada ViewModels\NotesViewModel.cs.
Dalam metode ,
ApplyQueryAttributes
lihat logika untuk kunci string kueri yang disimpan .matchedNote
Jika tidaknull
, catatan sedang diperbarui.AllNotes.Move
Gunakan metode untuk memindahkanmatchedNote
ke indeks 0, yang merupakan bagian atas daftar.string noteId = query["saved"].ToString(); NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault(); // If note is found, update it if (matchedNote != null) { matchedNote.Reload(); AllNotes.Move(AllNotes.IndexOf(matchedNote), 0); }
Metode ini
AllNotes.Move
mengambil dua parameter untuk memindahkan posisi objek dalam koleksi. Parameter pertama adalah indeks objek yang akan dipindahkan, dan parameter kedua adalah indeks tempat memindahkan objek. Metode mengambilAllNotes.IndexOf
indeks catatan.matchedNote
Ketika adalahnull
, catatan baru dan sedang ditambahkan ke daftar. Alih-alih menambahkannya, yang menambahkan catatan ke akhir daftar, sisipkan catatan di indeks 0, yang merupakan bagian atas daftar. Ubah metode menjadiAllNotes.Add
AllNotes.Insert
.string noteId = query["saved"].ToString(); NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault(); // If note is found, update it if (matchedNote != null) { matchedNote.Reload(); AllNotes.Move(AllNotes.IndexOf(matchedNote), 0); } // If note isn't found, it's new; add it. else AllNotes.Insert(0, new NoteViewModel(Models.Note.Load(noteId)));
Metode ApplyQueryAttributes
ini akan terlihat seperti cuplikan kode berikut:
void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query.ContainsKey("deleted"))
{
string noteId = query["deleted"].ToString();
NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
// If note exists, delete it
if (matchedNote != null)
AllNotes.Remove(matchedNote);
}
else if (query.ContainsKey("saved"))
{
string noteId = query["saved"].ToString();
NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
// If note is found, update it
if (matchedNote != null)
{
matchedNote.Reload();
AllNotes.Move(AllNotes.IndexOf(matchedNote), 0);
}
// If note isn't found, it's new; add it.
else
AllNotes.Insert(0, new NoteViewModel(Models.Note.Load(noteId)));
}
}
Perbolehkan memilih catatan dua kali
Dalam tampilan AllNotes, CollectionView
mencantumkan semua catatan, tetapi tidak memperbolehkan Anda memilih catatan yang sama dua kali. Ada dua cara item tetap dipilih: ketika pengguna mengubah catatan yang ada, dan ketika pengguna secara paksa menavigasi mundur. Kasus di mana pengguna menyimpan catatan diperbaiki dengan perubahan kode di bagian sebelumnya yang menggunakan AllNotes.Move
, sehingga Anda tidak perlu khawatir tentang kasus tersebut.
Masalah yang harus Anda selesaikan sekarang terkait dengan navigasi. Tidak peduli bagaimana tampilan Allnotes dinavigasi, NavigatedTo
peristiwa dinaikkan untuk halaman tersebut. Kejadian ini adalah tempat yang sempurna untuk membatalkan pilihan item yang dipilih secara paksa di CollectionView
.
Namun, dengan pola MVVM yang diterapkan di sini, viewmodel tidak dapat memicu sesuatu langsung pada tampilan, seperti menghapus item yang dipilih setelah catatan disimpan. Jadi bagaimana anda mendapatkan itu terjadi? Implementasi pola MVVM yang baik meminimalkan kode di belakang dalam tampilan. Ada beberapa cara berbeda untuk menyelesaikan masalah ini untuk mendukung pola pemisahan MVVM. Namun, tidak apa-apa juga untuk menempatkan kode di kode di belakang tampilan, terutama ketika langsung terikat dengan tampilan. MVVM memiliki banyak desain dan konsep hebat yang membantu Anda mengkompartmentalisasi aplikasi, meningkatkan pemeliharaan, dan memudahkan Anda untuk menambahkan fitur baru. Namun, dalam beberapa kasus, Anda mungkin menemukan bahwa MVVM mendorong overengineering.
Jangan overengineer solusi untuk masalah ini, dan hanya menggunakan NavigatedTo
peristiwa untuk menghapus item yang dipilih dari CollectionView
.
Di panel Penjelajah Solusi Visual Studio, klik dua kali pada Views\AllNotesPage.xaml.
Di XAML untuk
<ContentPage>
, tambahkanNavigatedTo
peristiwa:<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewModels="clr-namespace:Notes.ViewModels" x:Class="Notes.Views.AllNotesPage" Title="Your Notes" NavigatedTo="ContentPage_NavigatedTo"> <ContentPage.BindingContext> <viewModels:NotesViewModel /> </ContentPage.BindingContext>
Anda dapat menambahkan penanganan aktivitas default dengan mengklik kanan pada nama metode peristiwa,
ContentPage_NavigatedTo
, dan memilih Buka Definisi. Tindakan ini membuka Views\AllNotesPage.xaml.cs di editor kode.Ganti kode penanganan aktivitas dengan cuplikan berikut:
private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e) { notesCollection.SelectedItem = null; }
Dalam XAML,
CollectionView
diberikan namanotesCollection
. Kode ini menggunakan nama tersebutCollectionView
untuk mengakses , dan diaturSelectedItem
kenull
. Item terpilih dibersihkan setiap kali halaman dinavigasi.
Sekarang, jalankan aplikasi Anda. Cobalah untuk menavigasi ke catatan, tekan tombol kembali, dan pilih catatan yang sama untuk kedua kalinya. Perilaku aplikasi diperbaiki!
Jelajahi kode untuk tutorial ini.. Jika Anda ingin mengunduh salinan proyek yang telah selesai untuk membandingkan kode Anda dengan, unduh proyek ini.
Selamat!
Aplikasi Anda sekarang menggunakan pola MVVM!
Langkah berikutnya
Tautan berikut memberikan informasi lebih lanjut yang terkait dengan beberapa konsep yang Anda pelajari dalam tutorial ini:
Memiliki masalah dengan bagian ini? Jika iya, berikan umpan balik kepada kami agar kami dapat meningkatkan bagian ini.