Příkazové Xamarin.Forms rozhraní

Download Sample Stažení ukázky

V architektuře Model-View-ViewModel (MVVM) jsou datové vazby definovány mezi vlastnostmi v Modelu ViewModel, což je obecně třída odvozená od INotifyPropertyChangeda 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 TapGestureRecognizerudá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:

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:

SearchBarSearchCommand 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 ICommandButton. 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 Pro zadávání osob v programu Ukázky datových vazeb ukazuje některé jednoduché příkazy implementované v modelu ViewModel.

Definuje PersonViewModel tři vlastnosti s názvem Name, Agea 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 ButtonNew 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í ListViewtlačí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:

Person Entry

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 , NewCommandSubmitCommanda 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.CanExecuteExecute Nejjednodušší je definovat tyto akce a funkce jako funkce lambda přímo v konstruktoru Command . Tady je definice objektu CommandNewCommand 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 truea 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 ChangeCanExecuteSubmitCommand. 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 executeSubmit 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í CommandParameterje 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, BackspaceCommanda 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="&#x21E6;"
                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="&#x00B7;"
                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:

Decimal Keyboard

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 executeBackspace 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í executeRefreshCanExecutes, 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, jako je například samotný program Ukázky datových vazeb. 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ředNavigateCommand 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í.