Příkazové Xamarin.Forms rozhraní
V architektuře Model-View-ViewModel (MVVM) jsou datové vazby definovány mezi vlastnostmi v Modelu ViewModel, což je obecně třída odvozená od INotifyPropertyChanged
a vlastnosti v Zobrazení, což je obecně soubor XAML. Někdy aplikace potřebuje, aby přesahovala tyto vazby vlastností tím, že vyžaduje, aby uživatel inicioval příkazy, které mají vliv na něco v modelu ViewModel. Tyto příkazy jsou obecně signalizovány kliknutím na tlačítko nebo prstem klepněte a tradičně se zpracovávají v souboru kódu v obslužné rutině pro Clicked
událost události Button
nebo Tapped
události TapGestureRecognizer
události .
Příkazové rozhraní poskytuje alternativní přístup k implementaci příkazů, které jsou pro architekturu MVVM mnohem vhodnější. Samotný Model ViewModel může obsahovat příkazy, které jsou metody spouštěné v reakci na konkrétní aktivitu v zobrazení, jako Button
je kliknutí. Datové vazby jsou definovány mezi těmito příkazy a Button
.
Chcete-li povolit datovou vazbu mezi modelem Button
a modelem ViewModel, Button
definuje dvě vlastnosti:
Command
typuSystem.Windows.Input.ICommand
CommandParameter
typuObject
Chcete-li použít příkazové rozhraní, definujete datovou vazbu, která cílí na Command
vlastnost Button
, kde zdroj je vlastnost v ViewModel typu ICommand
. ViewModel obsahuje kód přidružený k této ICommand
vlastnosti, která se spustí při kliknutí na tlačítko. Pokud jsou všechna svázaná se stejnou ICommand
vlastností v modelu ViewModel, můžete nastavit CommandParameter
na libovolná data a rozlišovat mezi více tlačítky.
Vlastnosti Command
jsou CommandParameter
také definovány následujícími třídami:
MenuItem
a tudíž ,ToolbarItem
který je odvozen odMenuItem
TextCell
a tudíž ,ImageCell
který je odvozen odTextCell
TapGestureRecognizer
SearchBar
SearchCommand
definuje vlastnost typu ICommand
a SearchCommandParameter
vlastnosti. ListView
Vlastnost RefreshCommand
je také typu ICommand
.
Všechny tyto příkazy lze zpracovat v modelu ViewModel způsobem, který nezávisí na konkrétním objektu uživatelského rozhraní v zobrazení.
ICommand – rozhraní
Rozhraní System.Windows.Input.ICommand
není součástí Xamarin.Forms. Místo toho se definuje v oboru názvů System.Windows.Input a skládá se ze dvou metod a jedné události:
public interface ICommand
{
public void Execute (Object parameter);
public bool CanExecute (Object parameter);
public event EventHandler CanExecuteChanged;
}
Chcete-li použít příkazové rozhraní, váš ViewModel obsahuje vlastnosti typu ICommand
:
public ICommand MyCommand { private set; get; }
Model ViewModel musí také odkazovat na třídu, která implementuje ICommand
rozhraní. Tato třída bude stručně popsána. V zobrazení je Command
vlastnost Button
vázána na tuto vlastnost:
<Button Text="Execute command"
Command="{Binding MyCommand}" />
Když uživatel stiskne Button
, Button
volá metodu Execute
v objektu ICommand
vázaném na jeho Command
vlastnost. To je nejjednodušší část příkazového rozhraní.
Metoda CanExecute
je složitější. Když je vazba poprvé definována na Command
vlastnosti , a když se datová vazba nějakým způsobem změní, Button
volá metodu CanExecute
v objektu ICommand
Button
. Pokud CanExecute
se vrátí false
, pak Button
se zakáže sám. To znamená, že konkrétní příkaz je momentálně nedostupný nebo neplatný.
Také Button
připojí obslužnou rutinu k CanExecuteChanged
události ICommand
. Událost se aktivuje z modelu ViewModel. Když se tato událost aktivuje, Button
volání CanExecute
se znovu spustí. Umožňuje Button
sám sebe, pokud CanExecute
vrátí true
a zakáže sám sebe, pokud CanExecute
vrátí false
.
Důležité
IsEnabled
Nepoužívejte vlastnostButton
, pokud používáte příkazové rozhraní.
Třída příkazů
Pokud váš ViewModel definuje vlastnost typu ICommand
, ViewModel musí také obsahovat nebo odkazovat na třídu, která implementuje ICommand
rozhraní. Tato třída musí obsahovat nebo odkazovat na Execute
metody a CanExecute
aktivovat CanExecuteChanged
událost vždy, když CanExecute
metoda může vrátit jinou hodnotu.
Takovou třídu můžete napsat sami nebo můžete použít třídu, kterou napsal někdo jiný. Vzhledem k tomu ICommand
, že je součástí Microsoft Windows, používá se už roky s aplikacemi windows MVVM. Použití třídy Systému Windows, která implementuje ICommand
, umožňuje sdílet modely ViewModels mezi aplikacemi a Xamarin.Forms aplikacemi systému Windows.
Pokud sdílení modelů ViewModels mezi Windows a Xamarin.Forms není problém, můžete k implementaci ICommand
rozhraní použít Command
třídu nebo Command<T>
modelXamarin.Forms. Tyto třídy umožňují určit těla Execute
a CanExecute
metody v konstruktorech tříd. Použijete-li Command<T>
CommandParameter
vlastnost k rozlišení mezi více zobrazeními vázanými na stejnou ICommand
vlastnost, a jednodušší Command
třídu, pokud to není požadavek.
Základní příkazování
Stránka Položka osoby v ukázkovém programu ukazuje některé jednoduché příkazy implementované v modelu ViewModel.
Definuje PersonViewModel
tři vlastnosti s názvem Name
, Age
a Skills
které definují osobu. Tato třída neobsahuje žádné ICommand
vlastnosti:
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));
}
}
Následující PersonCollectionViewModel
obrázek vytvoří nové objekty typu PersonViewModel
a umožní uživateli vyplnit data. Pro tento účel třída definuje vlastnosti IsEditing
typu bool
a PersonEdit
typu PersonViewModel
. Třída navíc definuje tři vlastnosti typu ICommand
a vlastnost s názvem Persons
typu 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));
}
}
Tento zkrácený výpis neobsahuje konstruktor třídy, což je místo, kde jsou definovány tři vlastnosti typu ICommand
, které se zobrazí krátce. Všimněte si, že změny tří vlastností typu ICommand
a Persons
vlastnost nemají za následek PropertyChanged
vyvolání událostí. Tyto vlastnosti jsou nastaveny při prvním vytvoření třídy a potom se nemění.
Než se podíváme na konstruktor PersonCollectionViewModel
třídy, pojďme se podívat na soubor XAML pro program Person Entry . Grid
Obsahuje s jeho BindingContext
vlastností nastavena na PersonCollectionViewModel
. Obsahuje Grid
s textem Button
New s jeho Command
vlastností vázanou na NewCommand
vlastnost v ViewModel, vstupní formulář s vlastnostmi vázanými na IsEditing
vlastnost, stejně jako vlastnosti PersonViewModel
, a dva další tlačítka svázané s SubmitCommand
a CancelCommand
vlastnosti ViewModel. Konečný ListView
zobrazí kolekci osob, které již byly zadány:
<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>
Takto to funguje: Uživatel nejprve stiskne tlačítko Nový . Tím se povolí formulář pro zadání, ale zakáže se tlačítko Nový . Uživatel pak zadá jméno, věk a dovednosti. Kdykoli během úprav může uživatel stisknout tlačítko Storno a začít znovu. Je-li zadáno jméno a platný věk, je tlačítko Odeslat povoleno. Stisknutím tohoto tlačítka Odeslat přenesete osobu do kolekce zobrazené pomocí ListView
tlačítka . Po stisknutí tlačítka Zrušit nebo Odeslat se formulář pro zadání vymaže a tlačítko Nový se znovu povolí.
Obrazovka iOS na levé straně zobrazuje rozložení před zadáním platného věku. Na obrazovce Androidu je tlačítko Odeslat povolené po nastavení věku:
Program nemá žádné možnosti pro úpravy existujících položek a při přechodu ze stránky neukládá položky.
Veškerá logika tlačítek Nový, Odeslat a Zrušit se zpracovává PersonCollectionViewModel
prostřednictvím definic , NewCommand
SubmitCommand
a CancelCommand
vlastností. Konstruktor těchto PersonCollectionViewModel
tří vlastností nastaví na objekty typu Command
.
Konstruktor Command
třídy umožňuje předat argumenty typu Action
a Func<bool>
odpovídající metodám.CanExecute
Execute
Nejjednodušší je definovat tyto akce a funkce jako funkce lambda přímo v konstruktoru Command
. Tady je definice objektu Command
NewCommand
pro vlastnost:
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();
}
···
}
Když uživatel klikne na tlačítko Nový , execute
funkce předaná konstruktoru Command
se spustí. Tím se vytvoří nový PersonViewModel
objekt, nastaví obslužnou rutinu pro událost tohoto objektu PropertyChanged
, nastaví IsEditing
na true
a zavolá metodu RefreshCanExecutes
definovanou za konstruktorem.
Kromě implementace ICommand
rozhraní třída Command
také definuje metodu s názvem ChangeCanExecute
. Model ViewModel by měl volat ChangeCanExecute
vlastnost ICommand
vždy, když se stane cokoli, co by mohlo změnit návratovou CanExecute
hodnotu metody. Volání ChangeCanExecute
způsobí, že Command
třída aktivuje metodu CanExecuteChanged
. Připojil Button
obslužnou rutinu pro tuto událost a reaguje opětovným voláním CanExecute
a pak se povolí na základě návratové hodnoty této metody.
execute
Když metoda NewCommand
volání RefreshCanExecutes
, NewCommand
vlastnost získá volání ChangeCanExecute
, a Button
volání canExecute
metody, která nyní vrátífalse
, protože IsEditing
vlastnost je nyní true
.
Obslužná rutina PropertyChanged
pro nový PersonViewModel
objekt volá metodu ChangeCanExecute
SubmitCommand
. Takto se implementuje tato vlastnost příkazu:
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;
});
···
}
···
}
Funkce canExecute
se SubmitCommand
volá při každé změně vlastnosti objektu, který PersonViewModel
se upravuje. Vrátí true
pouze v případě, že Name
je vlastnost alespoň jeden znak dlouhý a Age
je větší než 0. V té době se aktivuje tlačítko Odeslat .
Funkce execute
Submit odebere obslužnou rutinu změněnou vlastností z objektu PersonViewModel
, přidá objekt do Persons
kolekce a vrátí vše do počátečních podmínek.
Funkce execute
tlačítka Zrušit dělá vše, co tlačítko Odeslat dělá, s výjimkou přidání objektu do kolekce:
public class PersonCollectionViewModel : INotifyPropertyChanged
{
···
public PersonCollectionViewModel()
{
···
CancelCommand = new Command(
execute: () =>
{
PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
PersonEdit = null;
IsEditing = false;
RefreshCanExecutes();
},
canExecute: () =>
{
return IsEditing;
});
}
···
}
Metoda canExecute
se vrátí true
kdykoliv PersonViewModel
, kdy se upravuje.
Tyto techniky je možné přizpůsobit složitějším scénářům: Vlastnost in PersonCollectionViewModel
může být vázána na SelectedItem
vlastnost ListView
úprav existujících položek a tlačítko Odstranit lze přidat k odstranění těchto položek.
Není nutné definovat execute
a canExecute
metody jako funkce lambda. V modelu ViewModel je můžete psát jako běžné soukromé metody a odkazovat na ně v Command
konstruktorech. Tento přístup ale obvykle vede k mnoha metodám, na které se odkazuje pouze jednou v modelu ViewModel.
Použití parametrů příkazu
Někdy je vhodné, aby jedno nebo více tlačítek (nebo jiných objektů uživatelského rozhraní) sdílelo stejnou ICommand
vlastnost v modelu ViewModel. V tomto případě použijete CommandParameter
vlastnost k rozlišení mezi tlačítky.
Pro tyto sdílené ICommand
vlastnosti můžete i nadále používat Command
třídu. Třída definuje alternativní konstruktor , který přijímá execute
a canExecute
metody s parametry typu Object
. CommandParameter
Takto se tyto metody předávají.
Při použití CommandParameter
je však nejjednodušší použít obecnou Command<T>
třídu k určení typu objektu nastaveného na CommandParameter
. canExecute
Zadané execute
metody mají parametry tohoto typu.
Tato technika ukazuje, jak implementovat klávesnici pro zadávání desetinných čísel. Pro BindingContext
to Grid
je .DecimalKeypadViewModel
Vlastnost Entry
tohoto modelu ViewModel je vázána na Text
vlastnost Label
. Button
Všechny objekty jsou vázány na různé příkazy v Modelu ViewModel: ClearCommand
, BackspaceCommand
a 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>
Tlačítka 11 pro 10 číslic a desetinná čárka sdílejí vazbu s DigitCommand
. Rozlišení CommandParameter
mezi těmito tlačítky. Hodnota nastavená na CommandParameter
je obecně stejná jako text zobrazený tlačítkem s výjimkou desetinné čárky, která je pro účely srozumitelnosti zobrazena střední tečkou.
Tady je program v akci:
Všimněte si, že tlačítko desetinné čárky na všech třech snímcích obrazovky je zakázané, protože zadané číslo již obsahuje desetinnou čárku.
Definuje DecimalKeypadViewModel
vlastnost typu string
(což je jediná vlastnost, která aktivuje PropertyChanged
událost) a tři vlastnosti typuICommand
:Entry
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; }
}
Tlačítko odpovídající ho ClearCommand
je vždy povoleno a jednoduše nastaví položku zpět na "0":
public class DecimalKeypadViewModel : INotifyPropertyChanged
{
···
public DecimalKeypadViewModel()
{
ClearCommand = new Command(
execute: () =>
{
Entry = "0";
RefreshCanExecutes();
});
···
}
void RefreshCanExecutes()
{
((Command)BackspaceCommand).ChangeCanExecute();
((Command)DigitCommand).ChangeCanExecute();
}
···
}
Protože tlačítko je vždy povoleno, není nutné zadat canExecute
argument v konstruktoru Command
.
Logika zadávání čísel a zpětných mezer je trochu složitá, protože pokud nebyly zadány žádné číslice, pak Entry
vlastnost je řetězec "0". Pokud uživatel zadá více nul, bude stále obsahovat jenom jednu nulu Entry
. Pokud uživatel zadá jakoukoli jinou číslici, nahradí tato číslice nulu. Ale pokud uživatel zadá desetinnou čárku před jinou číslicí, pak Entry
je řetězec "0".
Tlačítko Backspace je povoleno pouze v případě, že délka položky je větší než 1 nebo pokud Entry
není rovna řetězci "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 pro funkci tlačítka execute
Backspace zajišťuje, že Entry
je alespoň řetězec "0".
Vlastnost DigitCommand
je vázána na 11 tlačítek, z nichž každý identifikuje sám se CommandParameter
vlastností. Může DigitCommand
být nastavena na instanci běžné Command
třídy, ale je jednodušší použít Command<T>
obecnou třídu. Při použití příkazového rozhraní s XAML CommandParameter
jsou vlastnosti obvykle řetězce a to je typ obecného argumentu. canExecute
Funkce execute
pak mají argumenty typustring
:
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("."));
});
}
···
}
Metoda execute
připojí řetězcový argument k Entry
vlastnosti. Pokud ale výsledek začíná nulou (ale ne nulou a desetinnou čárkou), musí být tato počáteční nula odebrána pomocí Substring
funkce.
Metoda canExecute
vrátí false
pouze v případě, že argument je desetinná čárka (označuje, že desetinná čárka je stisknuta) a Entry
již obsahuje desetinnou čárku.
Všechny metody volají execute
RefreshCanExecutes
, které pak volá ChangeCanExecute
obojí DigitCommand
a ClearCommand
. Tím zajistíte, že jsou tlačítka desetinné čárky a backspace povolená nebo zakázaná na základě aktuální sekvence zadaných číslic.
Asynchronní příkazování pro navigační nabídky
Příkazování je vhodné pro implementaci navigačních nabídek. Tady je část 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>
Při použití příkazů s XAML CommandParameter
jsou vlastnosti obvykle nastaveny na řetězce. V tomto případě se však používá rozšíření značek XAML tak, aby CommandParameter
byl typ System.Type
.
Každá Command
vlastnost je vázána na vlastnost s názvem NavigateCommand
. Tato vlastnost je definována v souboru kódu 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 nastaví NavigateCommand
vlastnost na metodu execute
, která vytvoří instanci parametru System.Type
a pak na ni přejde. PushAsync
Vzhledem k tomu, že volání vyžaduje await
operátor, execute
musí být metoda označena jako asynchronní. Toho dosáhnete pomocí klíčového async
slova před seznamem parametrů.
Konstruktor také nastaví BindingContext
stránku sama tak, aby vazby odkazy na NavigateCommand
v této třídě.
Pořadí kódu v tomto konstruktoru je rozdíl: Volání InitializeComponent
způsobí parsování XAML, ale v tomto okamžiku nelze přeložit vazbu na pojmenovanou NavigateCommand
vlastnost, protože BindingContext
je nastavena na null
. Pokud je nastavena BindingContext
v konstruktoru před NavigateCommand
nastavením, lze vazbu vyřešit, pokud BindingContext
je nastavena, ale v té době, NavigateCommand
je stále null
. BindingContext
Nastavení NavigateCommand
poté nebude mít na vazbu žádný vliv, protože změna NavigateCommand
neaktivuje PropertyChanged
událost a vazba neví, že NavigateCommand
je teď platná.
Nastavení obou NavigateCommand
i BindingContext
(v libovolném pořadí) před voláním InitializeComponent
bude fungovat, protože obě komponenty vazby jsou nastaveny, když analyzátor XAML narazí na definici vazby.
Datové vazby můžou být někdy složité, ale jak jste viděli v této sérii článků, jsou výkonné a všestranné a výrazně pomáhají uspořádat kód oddělením základní logiky od uživatelského rozhraní.