Xamarin.Forms Antarmuka Perintah
Dalam arsitektur Model-View-ViewModel (MVVM), pengikatan data ditentukan antara properti di ViewModel, yang umumnya merupakan kelas yang berasal dari INotifyPropertyChanged
, dan properti dalam Tampilan, yang umumnya merupakan file XAML. Terkadang aplikasi memiliki kebutuhan yang melampaui pengikatan properti ini dengan mengharuskan pengguna untuk memulai perintah yang memengaruhi sesuatu di ViewModel. Perintah ini umumnya disinyalir oleh klik tombol atau ketukan jari, dan secara tradisional diproses dalam file code-behind dalam handler untuk Clicked
peristiwa Button
atau Tapped
peristiwa TapGestureRecognizer
.
Antarmuka perintah menyediakan pendekatan alternatif untuk menerapkan perintah yang jauh lebih cocok untuk arsitektur MVVM. ViewModel itu sendiri dapat berisi perintah, yang merupakan metode yang dijalankan sebagai reaksi terhadap aktivitas tertentu dalam Tampilan seperti Button
klik. Pengikatan data didefinisikan antara perintah ini dan Button
.
Untuk mengizinkan pengikatan data antara dan Button
ViewModel, Button
menentukan dua properti:
Command
dari jenisSystem.Windows.Input.ICommand
CommandParameter
dari jenisObject
Untuk menggunakan antarmuka perintah, Anda menentukan pengikatan data yang menargetkan Command
properti Button
tempat sumber adalah properti di ViewModel jenis ICommand
. ViewModel berisi kode yang ICommand
terkait dengan properti yang dijalankan saat tombol diklik. Anda dapat mengatur CommandParameter
ke data arbitrer untuk membedakan antara beberapa tombol jika semuanya terikat ke properti yang sama ICommand
di ViewModel.
Properti Command
dan CommandParameter
juga didefinisikan oleh kelas-kelas berikut:
MenuItem
dan oleh karena itu,ToolbarItem
, yang berasal dariMenuItem
TextCell
dan oleh karena itu,ImageCell
, yang berasal dariTextCell
TapGestureRecognizer
SearchBar
SearchCommand
menentukan properti jenis ICommand
dan SearchCommandParameter
properti. Properti RefreshCommand
juga ListView
berjenis ICommand
.
Semua perintah ini dapat ditangani dalam ViewModel dengan cara yang tidak bergantung pada objek antarmuka pengguna tertentu dalam Tampilan.
Antarmuka ICommand
Antarmuka System.Windows.Input.ICommand
bukan bagian Xamarin.Formsdari . Ini didefinisikan sebagai gantinya di namespace System.Windows.Input , dan terdiri dari dua metode dan satu peristiwa:
public interface ICommand
{
public void Execute (Object parameter);
public bool CanExecute (Object parameter);
public event EventHandler CanExecuteChanged;
}
Untuk menggunakan antarmuka perintah, ViewModel Anda berisi properti jenis ICommand
:
public ICommand MyCommand { private set; get; }
ViewModel juga harus mereferensikan kelas yang mengimplementasikan ICommand
antarmuka. Kelas ini akan segera dijelaskan. Di Tampilan, Command
properti terikat Button
ke properti tersebut:
<Button Text="Execute command"
Command="{Binding MyCommand}" />
Ketika pengguna menekan Button
, pengguna memanggil Execute
metode dalam objek yang ICommand
terikat ke propertinya Command
Button
. Itulah bagian paling sederhana dari antarmuka perintah.
Metode CanExecute
ini lebih kompleks. Ketika pengikatan pertama kali didefinisikan pada Command
properti Button
, dan ketika pengikatan data berubah dalam beberapa cara, Button
memanggil CanExecute
metode dalam ICommand
objek. Jika CanExecute
mengembalikan false
, maka menonaktifkan Button
dirinya sendiri. Ini menunjukkan bahwa perintah tertentu saat ini tidak tersedia atau tidak valid.
juga Button
melampirkan handler pada CanExecuteChanged
peristiwa ICommand
. Peristiwa ini diaktifkan dari dalam ViewModel. Ketika peristiwa itu dipicu, Button
panggilan CanExecute
lagi. mengaktifkan Button
dirinya sendiri jika CanExecute
mengembalikan true
dan menonaktifkan dirinya sendiri jika CanExecute
mengembalikan false
.
Penting
Jangan gunakan IsEnabled
properti Button
jika Anda menggunakan antarmuka perintah.
Kelas Perintah
Saat ViewModel Anda menentukan properti jenis ICommand
, ViewModel juga harus berisi atau mereferensikan kelas yang mengimplementasikan ICommand
antarmuka. Kelas ini harus berisi atau mereferensikan Execute
metode dan CanExecute
, dan menembakkan CanExecuteChanged
peristiwa setiap kali CanExecute
metode mungkin mengembalikan nilai yang berbeda.
Anda dapat menulis kelas seperti itu sendiri, atau Anda dapat menggunakan kelas yang ditulis orang lain. Karena ICommand
merupakan bagian dari Microsoft Windows, ini telah digunakan selama bertahun-tahun dengan aplikasi Windows MVVM. Menggunakan kelas Windows yang mengimplementasikan ICommand
memungkinkan Anda berbagi ViewModel antara aplikasi dan Xamarin.Forms aplikasi Windows.
Jika berbagi ViewModels antara Windows dan Xamarin.Forms bukan masalah, maka Anda dapat menggunakan kelas atau Command<T>
yang Command
disertakan untuk Xamarin.Forms mengimplementasikan ICommand
antarmuka. Kelas-kelas ini memungkinkan Anda menentukan isi Execute
metode dan CanExecute
di konstruktor kelas. Gunakan Command<T>
saat Anda menggunakan CommandParameter
properti untuk membedakan antara beberapa tampilan yang terikat ke properti yang sama ICommand
, dan kelas yang lebih Command
sederhana ketika itu bukan persyaratan.
Perintah Dasar
Halaman Entri Orang dalam program sampel menunjukkan beberapa perintah sederhana yang diterapkan dalam ViewModel.
PersonViewModel
menentukan tiga properti bernama Name
, Age
, dan Skills
yang menentukan seseorang. Kelas ini tidak berisi properti apa punICommand
:
public class PersonViewModel : INotifyPropertyChanged
{
string name;
double age;
string skills;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
set { SetProperty(ref name, value); }
get { return name; }
}
public double Age
{
set { SetProperty(ref age, value); }
get { return age; }
}
public string Skills
{
set { SetProperty(ref skills, value); }
get { return skills; }
}
public override string ToString()
{
return Name + ", age " + Age;
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Yang PersonCollectionViewModel
ditunjukkan di bawah ini membuat objek baru jenis PersonViewModel
dan memungkinkan pengguna untuk mengisi data. Untuk tujuan itu, kelas mendefinisikan properti IsEditing
jenis bool
dan PersonEdit
jenis PersonViewModel
. Selain itu, kelas mendefinisikan tiga properti jenis ICommand
dan properti bernama Persons
jenis IList<PersonViewModel>
:
public class PersonCollectionViewModel : INotifyPropertyChanged
{
PersonViewModel personEdit;
bool isEditing;
public event PropertyChangedEventHandler PropertyChanged;
···
public bool IsEditing
{
private set { SetProperty(ref isEditing, value); }
get { return isEditing; }
}
public PersonViewModel PersonEdit
{
set { SetProperty(ref personEdit, value); }
get { return personEdit; }
}
public ICommand NewCommand { private set; get; }
public ICommand SubmitCommand { private set; get; }
public ICommand CancelCommand { private set; get; }
public IList<PersonViewModel> Persons { get; } = new ObservableCollection<PersonViewModel>();
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Daftar singkatan ini tidak termasuk konstruktor kelas, yang merupakan tempat tiga properti jenis ICommand
didefinisikan, yang akan segera ditampilkan. Perhatikan bahwa perubahan pada tiga properti jenis ICommand
dan Persons
properti tidak mengakibatkan peristiwa diaktifkan PropertyChanged
. Properti ini semua diatur ketika kelas pertama kali dibuat dan tidak berubah setelahnya.
Sebelum memeriksa konstruktor PersonCollectionViewModel
kelas , mari kita lihat file XAML untuk program Entri Orang. Ini berisi dengan propertinya BindingContext
yang Grid
diatur ke PersonCollectionViewModel
. Grid
berisi Button
dengan teks Baru dengan properti terikat Command
ke NewCommand
properti di ViewModel, formulir entri dengan properti yang terikat ke IsEditing
properti, serta properti PersonViewModel
, dan dua tombol lagi yang terikat ke SubmitCommand
properti dan CancelCommand
ViewModel. ListView
Final menampilkan kumpulan orang yang sudah dimasukkan:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.PersonEntryPage"
Title="Person Entry">
<Grid Margin="10">
<Grid.BindingContext>
<local:PersonCollectionViewModel />
</Grid.BindingContext>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- New Button -->
<Button Text="New"
Grid.Row="0"
Command="{Binding NewCommand}"
HorizontalOptions="Start" />
<!-- Entry Form -->
<Grid Grid.Row="1"
IsEnabled="{Binding IsEditing}">
<Grid BindingContext="{Binding PersonEdit}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="Name: " Grid.Row="0" Grid.Column="0" />
<Entry Text="{Binding Name}"
Grid.Row="0" Grid.Column="1" />
<Label Text="Age: " Grid.Row="1" Grid.Column="0" />
<StackLayout Orientation="Horizontal"
Grid.Row="1" Grid.Column="1">
<Stepper Value="{Binding Age}"
Maximum="100" />
<Label Text="{Binding Age, StringFormat='{0} years old'}"
VerticalOptions="Center" />
</StackLayout>
<Label Text="Skills: " Grid.Row="2" Grid.Column="0" />
<Entry Text="{Binding Skills}"
Grid.Row="2" Grid.Column="1" />
</Grid>
</Grid>
<!-- Submit and Cancel Buttons -->
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Submit"
Grid.Column="0"
Command="{Binding SubmitCommand}"
VerticalOptions="CenterAndExpand" />
<Button Text="Cancel"
Grid.Column="1"
Command="{Binding CancelCommand}"
VerticalOptions="CenterAndExpand" />
</Grid>
<!-- List of Persons -->
<ListView Grid.Row="3"
ItemsSource="{Binding Persons}" />
</Grid>
</ContentPage>
Berikut cara kerjanya: Pengguna terlebih dahulu menekan tombol Baru . Ini memungkinkan formulir entri tetapi menonaktifkan tombol Baru . Pengguna kemudian memasukkan nama, usia, dan keterampilan. Kapan saja selama pengeditan, pengguna dapat menekan tombol Batal untuk memulai kembali. Hanya ketika nama dan usia yang valid telah dimasukkan adalah tombol Kirim diaktifkan. Menekan tombol Kirim ini akan mentransfer orang ke koleksi yang ditampilkan oleh ListView
. Setelah tombol Batalkan atau Kirim ditekan, formulir entri dikosongkan dan tombol Baru diaktifkan lagi.
Layar iOS di sebelah kiri menampilkan tata letak sebelum usia yang valid dimasukkan. Layar Android menampilkan tombol Kirim diaktifkan setelah usia ditetapkan:
Program ini tidak memiliki fasilitas untuk mengedit entri yang ada, dan tidak menyimpan entri ketika Anda menavigasi jauh dari halaman.
Semua logika untuk tombol Baru, Kirim, dan Batal ditangani PersonCollectionViewModel
melalui definisi NewCommand
properti , , SubmitCommand
dan CancelCommand
. Konstruktor dari PersonCollectionViewModel
set ketiga properti ini ke objek jenis Command
.
Konstruktor Command
kelas memungkinkan Anda untuk meneruskan argumen jenis Action
dan Func<bool>
sesuai dengan Execute
metode dan CanExecute
. Paling mudah untuk menentukan tindakan dan fungsi ini sebagai fungsi lambda tepat di Command
konstruktor. Berikut adalah definisi Command
objek untuk NewCommand
properti :
public class PersonCollectionViewModel : INotifyPropertyChanged
{
···
public PersonCollectionViewModel()
{
NewCommand = new Command(
execute: () =>
{
PersonEdit = new PersonViewModel();
PersonEdit.PropertyChanged += OnPersonEditPropertyChanged;
IsEditing = true;
RefreshCanExecutes();
},
canExecute: () =>
{
return !IsEditing;
});
···
}
void OnPersonEditPropertyChanged(object sender, PropertyChangedEventArgs args)
{
(SubmitCommand as Command).ChangeCanExecute();
}
void RefreshCanExecutes()
{
(NewCommand as Command).ChangeCanExecute();
(SubmitCommand as Command).ChangeCanExecute();
(CancelCommand as Command).ChangeCanExecute();
}
···
}
Ketika pengguna mengklik tombol Baru , fungsi yang execute
diteruskan ke Command
konstruktor dijalankan. Ini membuat objek baru PersonViewModel
, mengatur handler pada peristiwa objek tersebut PropertyChanged
, diatur IsEditing
ke true
, dan memanggil metode yang RefreshCanExecutes
ditentukan setelah konstruktor.
Selain mengimplementasikan ICommand
antarmuka, Command
kelas juga mendefinisikan metode bernama ChangeCanExecute
. ViewModel Anda harus memanggil ChangeCanExecute
ICommand
properti setiap kali terjadi sesuatu yang mungkin mengubah nilai CanExecute
pengembalian metode. Panggilan untuk ChangeCanExecute
menyebabkan Command
kelas menembakkan CanExecuteChanged
metode . telah melampirkan handler untuk peristiwa tersebut Button
dan merespons dengan memanggil CanExecute
lagi, lalu mengaktifkan dirinya sendiri berdasarkan nilai pengembalian metode tersebut.
execute
Ketika metode NewCommand
panggilan RefreshCanExecutes
, NewCommand
properti mendapatkan panggilan ke ChangeCanExecute
, dan Button
memanggil canExecute
metode , yang sekarang kembali false
karena IsEditing
properti sekarang true
.
Handler PropertyChanged
untuk objek baru PersonViewModel
memanggil ChangeCanExecute
metode SubmitCommand
. Berikut adalah cara properti perintah tersebut diimplementasikan:
public class PersonCollectionViewModel : INotifyPropertyChanged
{
···
public PersonCollectionViewModel()
{
···
SubmitCommand = new Command(
execute: () =>
{
Persons.Add(PersonEdit);
PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
PersonEdit = null;
IsEditing = false;
RefreshCanExecutes();
},
canExecute: () =>
{
return PersonEdit != null &&
PersonEdit.Name != null &&
PersonEdit.Name.Length > 1 &&
PersonEdit.Age > 0;
});
···
}
···
}
Fungsi canExecute
untuk SubmitCommand
dipanggil setiap kali ada properti yang diubah dalam objek yang PersonViewModel
sedang diedit. Ini hanya mengembalikan true
ketika Name
properti setidaknya memiliki panjang satu karakter, dan Age
lebih besar dari 0. Pada saat itu, tombol Kirim menjadi diaktifkan.
Fungsi execute
untuk Kirim menghapus handler yang diubah properti dari PersonViewModel
, menambahkan objek ke Persons
koleksi, dan mengembalikan semuanya ke kondisi awal.
Fungsi execute
untuk tombol Batal melakukan semua yang dilakukan tombol Kirim kecuali menambahkan objek ke koleksi:
public class PersonCollectionViewModel : INotifyPropertyChanged
{
···
public PersonCollectionViewModel()
{
···
CancelCommand = new Command(
execute: () =>
{
PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
PersonEdit = null;
IsEditing = false;
RefreshCanExecutes();
},
canExecute: () =>
{
return IsEditing;
});
}
···
}
Metode mengembalikan canExecute
true
kapan saja yang PersonViewModel
sedang diedit.
Teknik ini dapat disesuaikan dengan skenario yang lebih kompleks: Properti di PersonCollectionViewModel
dapat terikat ke SelectedItem
properti ListView
untuk mengedit item yang ada, dan tombol Hapus dapat ditambahkan untuk menghapus item tersebut.
Tidak perlu mendefinisikan execute
metode dan canExecute
sebagai fungsi lambda. Anda dapat menulisnya sebagai metode privat reguler di ViewModel dan mereferensikannya di Command
konstruktor. Namun, pendekatan ini cenderung menghasilkan banyak metode yang hanya direferensikan sekali di ViewModel.
Menggunakan Parameter Perintah
Terkadang nyaman untuk satu atau beberapa tombol (atau objek antarmuka pengguna lainnya) untuk berbagi properti yang sama ICommand
di ViewModel. Dalam hal ini, Anda menggunakan CommandParameter
properti untuk membedakan antara tombol.
Anda dapat terus menggunakan Command
kelas untuk properti bersama ICommand
ini. Kelas mendefinisikan konstruktor alternatif yang menerima execute
dan canExecute
metode dengan parameter jenis Object
. Ini adalah bagaimana diteruskan CommandParameter
ke metode ini.
Namun, saat menggunakan CommandParameter
, paling mudah menggunakan kelas generik Command<T>
untuk menentukan jenis objek yang diatur ke CommandParameter
. Metode dan canExecute
yang Anda tentukan memiliki parameter jenis tersebutexecute
.
Halaman Keyboard Desimal mengilustrasikan teknik ini dengan menunjukkan cara mengimplementasikan keypad untuk memasukkan angka desimal. untuk BindingContext
Grid
adalah .DecimalKeypadViewModel
Properti Entry
ViewModel ini terikat ke Text
properti dari Label
. Button
Semua objek terikat ke berbagai perintah di ViewModel: ClearCommand
, , BackspaceCommand
dan DigitCommand
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.DecimalKeypadPage"
Title="Decimal Keyboard">
<Grid WidthRequest="240"
HeightRequest="480"
ColumnSpacing="2"
RowSpacing="2"
HorizontalOptions="Center"
VerticalOptions="Center">
<Grid.BindingContext>
<local:DecimalKeypadViewModel />
</Grid.BindingContext>
<Grid.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Setter Property="FontSize" Value="32" />
<Setter Property="BorderWidth" Value="1" />
<Setter Property="BorderColor" Value="Black" />
</Style>
</ResourceDictionary>
</Grid.Resources>
<Label Text="{Binding Entry}"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
FontSize="32"
LineBreakMode="HeadTruncation"
VerticalTextAlignment="Center"
HorizontalTextAlignment="End" />
<Button Text="CLEAR"
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
Command="{Binding ClearCommand}" />
<Button Text="⇦"
Grid.Row="1" Grid.Column="2"
Command="{Binding BackspaceCommand}" />
<Button Text="7"
Grid.Row="2" Grid.Column="0"
Command="{Binding DigitCommand}"
CommandParameter="7" />
<Button Text="8"
Grid.Row="2" Grid.Column="1"
Command="{Binding DigitCommand}"
CommandParameter="8" />
<Button Text="9"
Grid.Row="2" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="9" />
<Button Text="4"
Grid.Row="3" Grid.Column="0"
Command="{Binding DigitCommand}"
CommandParameter="4" />
<Button Text="5"
Grid.Row="3" Grid.Column="1"
Command="{Binding DigitCommand}"
CommandParameter="5" />
<Button Text="6"
Grid.Row="3" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="6" />
<Button Text="1"
Grid.Row="4" Grid.Column="0"
Command="{Binding DigitCommand}"
CommandParameter="1" />
<Button Text="2"
Grid.Row="4" Grid.Column="1"
Command="{Binding DigitCommand}"
CommandParameter="2" />
<Button Text="3"
Grid.Row="4" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="3" />
<Button Text="0"
Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"
Command="{Binding DigitCommand}"
CommandParameter="0" />
<Button Text="·"
Grid.Row="5" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="." />
</Grid>
</ContentPage>
11 tombol untuk 10 digit dan titik desimal berbagi pengikatan ke DigitCommand
. Pembeda CommandParameter
antara tombol-tombol ini. Nilai yang diatur ke CommandParameter
umumnya sama dengan teks yang ditampilkan oleh tombol kecuali untuk titik desimal, yang untuk tujuan kejelasan ditampilkan dengan karakter titik tengah.
Berikut adalah program yang sedang berjalan:
Perhatikan bahwa tombol untuk titik desimal di ketiga cuplikan layar dinonaktifkan karena angka yang dimasukkan sudah berisi titik desimal.
DecimalKeypadViewModel
menentukan Entry
properti jenis string
(yang merupakan satu-satunya properti yang memicu PropertyChanged
peristiwa) dan tiga properti jenis ICommand
:
public class DecimalKeypadViewModel : INotifyPropertyChanged
{
string entry = "0";
public event PropertyChangedEventHandler PropertyChanged;
···
public string Entry
{
private set
{
if (entry != value)
{
entry = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Entry"));
}
}
get
{
return entry;
}
}
public ICommand ClearCommand { private set; get; }
public ICommand BackspaceCommand { private set; get; }
public ICommand DigitCommand { private set; get; }
}
Tombol yang ClearCommand
sesuai dengan selalu diaktifkan dan hanya mengatur entri kembali ke "0":
public class DecimalKeypadViewModel : INotifyPropertyChanged
{
···
public DecimalKeypadViewModel()
{
ClearCommand = new Command(
execute: () =>
{
Entry = "0";
RefreshCanExecutes();
});
···
}
void RefreshCanExecutes()
{
((Command)BackspaceCommand).ChangeCanExecute();
((Command)DigitCommand).ChangeCanExecute();
}
···
}
Karena tombol selalu diaktifkan, tidak perlu menentukan canExecute
argumen di Command
konstruktor.
Logika untuk memasukkan angka dan backspacing sedikit rumit karena jika tidak ada digit yang dimasukkan, maka Entry
properti adalah string "0". Jika pengguna mengetikan lebih banyak nol, maka masih Entry
berisi hanya satu nol. Jika pengguna mengetik digit lain, digit tersebut menggantikan nol. Tetapi jika pengguna mengetikan titik desimal sebelum digit lain, maka Entry
adalah string "0.".
Tombol Backspace diaktifkan hanya ketika panjang entri lebih besar dari 1, atau jika Entry
tidak sama dengan string "0":
public class DecimalKeypadViewModel : INotifyPropertyChanged
{
···
public DecimalKeypadViewModel()
{
···
BackspaceCommand = new Command(
execute: () =>
{
Entry = Entry.Substring(0, Entry.Length - 1);
if (Entry == "")
{
Entry = "0";
}
RefreshCanExecutes();
},
canExecute: () =>
{
return Entry.Length > 1 || Entry != "0";
});
···
}
···
}
Logika untuk execute
fungsi untuk tombol Backspace memastikan bahwa Entry
setidaknya adalah string "0".
Properti DigitCommand
terikat ke 11 tombol, yang masing-masing mengidentifikasi dirinya dengan CommandParameter
properti . DigitCommand
dapat diatur ke instans kelas regulerCommand
, tetapi lebih mudah untuk menggunakan Command<T>
kelas generik. Saat menggunakan antarmuka perintah dengan XAML, CommandParameter
properti biasanya berupa string, dan itulah jenis argumen generik. Fungsi execute
dan canExecute
kemudian memiliki argumen jenis string
:
public class DecimalKeypadViewModel : INotifyPropertyChanged
{
···
public DecimalKeypadViewModel()
{
···
DigitCommand = new Command<string>(
execute: (string arg) =>
{
Entry += arg;
if (Entry.StartsWith("0") && !Entry.StartsWith("0."))
{
Entry = Entry.Substring(1);
}
RefreshCanExecutes();
},
canExecute: (string arg) =>
{
return !(arg == "." && Entry.Contains("."));
});
}
···
}
Metode menambahkan execute
argumen string ke Entry
properti . Namun, jika hasilnya dimulai dengan nol (tetapi bukan nol dan titik desimal) maka nol awal tersebut harus dihapus menggunakan Substring
fungsi .
Metode canExecute
ini hanya mengembalikan false
jika argumen adalah titik desimal (menunjukkan bahwa titik desimal sedang ditekan) dan Entry
sudah berisi titik desimal.
execute
Semua metode memanggil RefreshCanExecutes
, yang kemudian memanggil ChangeCanExecute
untuk dan DigitCommand
ClearCommand
. Ini memastikan bahwa tombol titik desimal dan backspace diaktifkan atau dinonaktifkan berdasarkan urutan digit yang dimasukkan saat ini.
Perintah Asinkron untuk Menu Navigasi
Perintah nyaman untuk menerapkan menu navigasi. Berikut adalah bagian dari MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.MainPage"
Title="Data Binding Demos"
Padding="10">
<TableView Intent="Menu">
<TableRoot>
<TableSection Title="Basic Bindings">
<TextCell Text="Basic Code Binding"
Detail="Define a data-binding in code"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:BasicCodeBindingPage}" />
<TextCell Text="Basic XAML Binding"
Detail="Define a data-binding in XAML"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:BasicXamlBindingPage}" />
<TextCell Text="Alternative Code Binding"
Detail="Define a data-binding in code without a BindingContext"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:AlternativeCodeBindingPage}" />
···
</TableSection>
</TableRoot>
</TableView>
</ContentPage>
Saat menggunakan perintah dengan XAML, CommandParameter
properti biasanya diatur ke string. Namun, dalam hal ini, ekstensi markup XAML digunakan sehingga CommandParameter
berjenis System.Type
.
Setiap Command
properti terikat ke properti bernama NavigateCommand
. Properti tersebut didefinisikan dalam file code-behind, MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
NavigateCommand = new Command<Type>(
async (Type pageType) =>
{
Page page = (Page)Activator.CreateInstance(pageType);
await Navigation.PushAsync(page);
});
BindingContext = this;
}
public ICommand NavigateCommand { private set; get; }
}
Konstruktor mengatur NavigateCommand
properti ke execute
metode yang membuat instans System.Type
parameter dan kemudian menavigasi ke dalamnya. PushAsync
Karena panggilan memerlukan await
operator, execute
metode harus ditandai sebagai asinkron. Ini dicapai dengan async
kata kunci sebelum daftar parameter.
Konstruktor juga mengatur BindingContext
halaman ke dirinya sendiri sehingga pengikatan mereferensikan NavigateCommand
di kelas ini.
Urutan kode dalam konstruktor ini membuat perbedaan: InitializeComponent
Panggilan menyebabkan XAML diurai, tetapi pada saat itu pengikatan ke properti bernama NavigateCommand
tidak dapat diselesaikan karena BindingContext
diatur ke null
. BindingContext
Jika diatur dalam konstruktor sebelumNavigateCommand
diatur, maka pengikatan dapat diselesaikan ketika BindingContext
diatur, tetapi pada saat itu, NavigateCommand
masih null
. Pengaturan NavigateCommand
setelah BindingContext
tidak akan berpengaruh pada pengikatan karena perubahan untuk NavigateCommand
tidak mengaktifkan PropertyChanged
peristiwa, dan pengikatan tidak tahu bahwa NavigateCommand
sekarang valid.
Mengatur dan NavigateCommand
BindingContext
(dalam urutan apa pun) sebelum panggilan ke InitializeComponent
akan berfungsi karena kedua komponen pengikatan diatur ketika pengurai XAML menemukan definisi pengikatan.
Pengikatan data terkadang bisa rumit, tetapi seperti yang telah Anda lihat dalam rangkaian artikel ini, mereka kuat dan serbaguna, dan sangat membantu mengatur kode Anda dengan memisahkan logika yang mendasar dari antarmuka pengguna.