Aracılığıyla paylaş


Xamarin.Forms Komut Arabirimi

Model-View-ViewModel (MVVM) mimarisinde veri bağlamaları, genellikle öğesinden INotifyPropertyChangedtüretilen bir sınıf olan ViewModel'deki özellikler ve genel olarak XAML dosyası olan Görünüm'deki özellikler arasında tanımlanır. Bazen bir uygulamanın, kullanıcının ViewModel'de bir şeyi etkileyen komutları başlatmasını gerektirerek bu özellik bağlamalarının ötesine geçme gereksinimleri vardır. Bu komutlar genellikle düğme tıklamaları veya parmak dokunuşları ile işaretlenir ve geleneksel olarak bir veya olayının işleyicisindeki ClickedButtonTappedTapGestureRecognizerarka planda kod dosyasında işlenir.

Komut arabirimi, MVVM mimarisine çok daha uygun komutları uygulamak için alternatif bir yaklaşım sağlar. ViewModel'in kendisi, Görünüm'deki belirli bir etkinliğe tepki olarak yürütülen, tıklama gibi Button yöntemler olan komutları içerebilir. Veri bağlamaları bu komutlarla Buttonarasında tanımlanır.

ile ViewModel arasında Button veri bağlamaya izin vermek için iki Button özelliği tanımlar:

Komut arabirimini kullanmak için, kaynağın ViewModel türünde ICommandbir özellik Button olduğu özelliğini hedefleyen Command bir veri bağlaması tanımlarsınız. ViewModel, düğmeye tıklandığında yürütülen bu ICommand özellikle ilişkilendirilmiş kodu içerir. Tümü ViewModel'deki aynı ICommand özelliğe bağlıysa, birden çok düğmeyi ayırt etmek için rastgele verilere ayarlayabilirsinizCommandParameter.

Command ve CommandParameter özellikleri de aşağıdaki sınıflar tarafından tanımlanır:

SearchBarSearchCommand türüne ICommand ve özelliğine sahip bir SearchCommandParameter özelliği tanımlar. özelliği RefreshCommandListView de türündedir ICommand.

Bu komutların tümü ViewModel içinde, Görünüm'deki belirli kullanıcı arabirimi nesnesine bağımlı olmayan bir şekilde işlenebilir.

ICommand Arabirimi

System.Windows.Input.ICommand Arabirim, öğesinin Xamarin.Formsbir parçası değildir. Bunun yerine System.Windows.Input ad alanında tanımlanır ve iki yöntemden ve bir olaydan oluşur:

public interface ICommand
{
    public void Execute (Object parameter);

    public bool CanExecute (Object parameter);

    public event EventHandler CanExecuteChanged;
}

Komut arabirimini kullanmak için ViewModel'iniz türünde ICommandözellikler içerir:

public ICommand MyCommand { private set; get; }

ViewModel ayrıca arabirimini uygulayan bir sınıfa ICommand da başvurmalıdır. Bu sınıf kısa süre içinde açıklanacaktır. Görünüm'de, öğesinin CommandButton özelliği bu özelliğe bağlıdır:

<Button Text="Execute command"
        Command="{Binding MyCommand}" />

Kullanıcı tuşuna bastığındaButtonButton, özelliğine Command bağlı nesnede ICommand yöntemini çağırırExecute. Komut arabiriminin en basit kısmıdır.

CanExecute yöntemi daha karmaşıktır. Bağlama özelliğinde CommandButtonilk kez tanımlandığında ve veri bağlaması bir şekilde değiştiğinde, Button nesnesindeki CanExecuteICommand yöntemini çağırır. döndürürse CanExecutefalse, Button kendisini devre dışı bırakır. Bu, belirli bir komutun şu anda kullanılamadığını veya geçersiz olduğunu gösterir.

ayrıca Button olayına CanExecuteChangedICommandbir işleyici ekler. Olay ViewModel'in içinden tetiklenir. Olay tetiklendiğinde yeniden çağrılar ButtonCanExecute yapılır. döndürürse ButtonCanExecutetrue kendisini etkinleştirir ve döndürürse CanExecutefalsekendisini devre dışı bırakır.

Önemli

Komut arabirimini IsEnabled kullanıyorsanız özelliğini Button kullanmayın.

Komut Sınıfı

ViewModel'iniz türünde ICommandbir özellik tanımladığında, ViewModel'in arabirimini uygulayan bir sınıfı da içermesi veya buna başvurması ICommand gerekir. Bu sınıfın ve CanExecute yöntemlerini içermesi Execute veya bunlara başvurması ve yöntemin CanExecuteChanged farklı bir değer döndürebileceği her durumda CanExecute olayı tetiklemesi gerekir.

Böyle bir sınıfı kendiniz yazabilir veya başka birinin yazdığı bir sınıfı kullanabilirsiniz. ICommand Microsoft Windows'un bir parçası olduğundan, yıllardır Windows MVVM uygulamalarıyla kullanılmıştır. Uygulayan ICommand bir Windows sınıfı kullanmak, ViewModel'lerinizi Windows uygulamaları ve Xamarin.Forms uygulamaları arasında paylaşmanıza olanak tanır.

ViewModel'leri Windows Xamarin.Forms arasında paylaşmak sorun değilse arabirimini uygulamak için içindeki Xamarin.Forms veya Command<T> sınıfını ICommand kullanabilirsinizCommand. Bu sınıflar, sınıf oluşturucularında ve CanExecute yöntemlerinin Execute gövdelerini belirtmenize olanak sağlar. Aynı özelliğe bağlı birden çok görünümü ayırt etmek için ICommand özelliğini kullandığınızda ve bu bir gereksinim olmadığında daha Command basit sınıfını kullandığınızda CommandParameter kullanınCommand<T>.

Temel Komut

Örnek programdaki Kişi Girişi sayfasında ViewModel'de uygulanan bazı basit komutlar gösterilir.

, PersonViewModel adlı NameAgeve Skills bir kişiyi tanımlayan üç özelliği tanımlar. Bu sınıf herhangi bir ICommand özellik içermiyor:

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));
    }
}

Aşağıda PersonCollectionViewModel gösterilen, türünde PersonViewModel yeni nesneler oluşturur ve kullanıcının verileri doldurmasına izin verir. Bu amaçla, sınıfı türü bool ve PersonEdit türündeki PersonViewModelözellikleri IsEditing tanımlar. Buna ek olarak, sınıfı türün üç özelliğini ve türünde ICommandIList<PersonViewModel>adlı Persons bir özelliği tanımlar:

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));
    }
}

Bu kısaltılmış liste, sınıfın oluşturucusunu içermez. Bu, türün ICommand üç özelliğinin tanımlandığı yerdir ve bu özellik kısa süre sonra gösterilir. türünün ICommand ve özelliğinin üç özelliğinde Persons yapılan değişikliklerin tetiklenen olaylarla sonuçlanmadığını PropertyChanged fark edin. Bu özelliklerin tümü sınıf ilk oluşturulduğunda ayarlanır ve bundan sonra değişmez.

Sınıfının oluşturucusunu PersonCollectionViewModel incelemeden önce, Kişi Girişi programı için XAML dosyasına bakalım. Bu, özelliği olarak ayarlanmış PersonCollectionViewModelbir GridBindingContext içerir. , Grid Özelliği ViewModel'deki özelliğine bağlı olan Yeni Command metnini, özelliğine NewCommand bağlı özelliklere IsEditing sahip bir giriş formunun yanı sıra , ve PersonViewModelViewModel'in özelliklerine SubmitCommand bağlı iki CancelCommand düğme daha içerirButton. Son olarak ListView , daha önce girilmiş olan kişilerin koleksiyonu görüntülenir:

<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>

Şu şekilde çalışır: Kullanıcı önce Yeni düğmesine basar. Bu, giriş formunu etkinleştirir ancak Yeni düğmesini devre dışı bırakır. Kullanıcı daha sonra bir ad, yaş ve beceri girer. Düzenleme sırasında kullanıcı, baştan başlamak için İptal düğmesine basabilir. Yalnızca bir ad ve geçerli bir yaş girildiğinde Gönder düğmesi etkinleştirilir. Bu Gönder düğmesine basıldığında, kişi tarafından görüntülenen koleksiyona ListViewaktarılır. İptal veya Gönder düğmesine basıldıktan sonra, giriş formu temizlenir ve Yeni düğmesi yeniden etkinleştirilir.

Soldaki iOS ekranında geçerli bir yaş girilmeden önce düzen gösterilir. Android ekranında , bir yaş ayarlandıktan sonra Gönder düğmesi etkinleştirilir:

Kişi Girişi

Programın var olan girişleri düzenlemek için herhangi bir özelliği yoktur ve sayfadan çıktığınızda girişleri kaydetmez.

Yeni, Gönder ve İptal düğmelerinin tüm mantığı , SubmitCommandve CancelCommand özelliklerinin tanımları NewCommandaracılığıyla işlenirPersonCollectionViewModel. oluşturucu bu PersonCollectionViewModel üç özelliği türündeki Commandnesnelere ayarlar.

sınıfının oluşturucusununCommand türü ve Func<bool> ve CanExecute yöntemlerine Execute karşılık gelen bağımsız değişkenleri Action geçirmenize olanak tanır. Bu eylemleri ve işlevleri doğrudan oluşturucuda lambda işlevleri olarak tanımlamak en kolayıdır Command . özelliği için nesnesinin Command tanımı aşağıdadır NewCommand :

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();
    }

    ···

}

Kullanıcı Yeni düğmesine tıkladığında, execute oluşturucuya Command geçirilen işlev yürütülür. Bu, yeni PersonViewModel bir nesne oluşturur, nesnenin PropertyChanged olayı üzerinde bir işleyici ayarlar, olarak trueayarlar IsEditing ve oluşturucudan sonra tanımlanan yöntemi çağırırRefreshCanExecutes.

sınıfı, Command arabirimini ICommand uygulamanın yanı sıra adlı ChangeCanExecutebir yöntem de tanımlar. Yöntemin dönüş değerini değiştirebilecek herhangi bir şey olduğunda ViewModel'iniz CanExecute bir ICommand özelliği çağırmalıdırChangeCanExecute. çağrısıChangeCanExecute, sınıfının yöntemini tetiklesine CanExecuteChanged neden olurCommand. Button, bu olay için bir işleyici eklemiş ve yeniden çağırarak CanExecute ve ardından bu yöntemin dönüş değerine göre kendisini etkinleştirerek yanıt verir.

yöntemi çağırdığındaRefreshCanExecutes, NewCommand özelliği öğesine ChangeCanExecutebir çağrı alır ve Button yöntemi çağırdığındacanExecute, artık özelliği şu anda trueolduğu için IsEditing yöntemini döndürürfalse.NewCommandexecute

PropertyChanged Yeni PersonViewModel nesnenin işleyicisi yöntemini SubmitCommandçağırırChangeCanExecute. Bu komut özelliği şu şekilde uygulanır:

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;
            });

        ···
    }

    ···

}

canExecute için işleviSubmitCommand, düzenlenmekte olan nesnede her özellik değiştiğinde PersonViewModel çağrılır. Yalnızca özelliği en az bir karakter uzunluğunda ve Age 0'dan büyük olduğunda Name döndürürtrue. Bu sırada Gönder düğmesi etkinleştirilir.

execute Submit işlevi, özelliği değiştirilmiş işleyicisini öğesinden PersonViewModelkaldırır, nesnesini koleksiyona Persons ekler ve her şeyi ilk koşullara döndürür.

İptal düğmesinin işlevi, nesneyi koleksiyona eklemek dışında Gönder düğmesinin yaptığı her şeyi yapar:execute

public class PersonCollectionViewModel : INotifyPropertyChanged
{

    ···

    public PersonCollectionViewModel()
    {

        ···

        CancelCommand = new Command(
            execute: () =>
            {
                PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
                PersonEdit = null;
                IsEditing = false;
                RefreshCanExecutes();
            },
            canExecute: () =>
            {
                return IsEditing;
            });
    }

    ···

}

yöntemicanExecute, düzenlenmekte olan herhangi bir PersonViewModel zamanda döndürürtrue.

Bu teknikler daha karmaşık senaryolara uyarlanabilir: içindeki PersonCollectionViewModel bir özellik mevcut öğeleri düzenlemek için öğesinin ListView özelliğine SelectedItem bağlanabilir ve bu öğeleri silmek için bir Sil düğmesi eklenebilir.

ve canExecute yöntemlerini lambda işlevleri olarak tanımlamak execute gerekmez. Bunları ViewModel'de normal özel yöntemler olarak yazabilir ve oluşturucularda Command bunlara başvurabilirsiniz. Ancak bu yaklaşım, ViewModel'de yalnızca bir kez başvuruda bulunan birçok yöntemle sonuçlanır.

Komut Parametrelerini Kullanma

Bazen bir veya daha fazla düğmenin (veya diğer kullanıcı arabirimi nesnelerinin) ViewModel'de aynı ICommand özelliği paylaşması kullanışlı olabilir. Bu durumda, düğmeleri ayırt etmek için özelliğini kullanırsınız CommandParameter .

Bu paylaşılan ICommand özellikler için sınıfını Command kullanmaya devam edebilirsiniz. sınıfı, türündeki Objectparametrelere sahip ve canExecute yöntemlerini kabul execute eden alternatif bir oluşturucu tanımlar. bu yöntemlere bu şekilde CommandParameter geçirilir.

Ancak kullanırkenCommandParameter, olarak ayarlanan CommandParameternesnenin türünü belirtmek için genel Command<T> sınıfı kullanmak en kolayıdır. execute Belirttiğiniz ve canExecute yöntemleri bu tür parametrelere sahiptir.

Ondalık Klavye sayfasında, ondalık sayıları girmek için tuş takımının nasıl uygulandığı gösterilerek bu teknik gösterilir. BindingContext için Grid bir DecimalKeypadViewModelşeklindedir. Entry Bu ViewModel'in özelliği, özelliğine TextLabelbağlıdır. Button Tüm nesneler ViewModel'deki çeşitli komutlara bağlıdır: ClearCommand, BackspaceCommandve 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>

10 basamak için 11 düğmesi ve ondalık noktası ile DigitCommandbir bağlama paylaşır. bu CommandParameter düğmeler arasında ayrımlar. olarak ayarlanan CommandParameter değer, açıklık amacıyla orta nokta karakteriyle görüntülenen ondalık nokta dışında düğme tarafından görüntülenen metinle genel olarak aynıdır.

İşte program şu şekildedir:

Ondalık Klavye

Girilen sayı zaten bir ondalık nokta içerdiğinden, üç ekran görüntüsündeki ondalık nokta düğmesinin devre dışı olduğuna dikkat edin.

türündeki DecimalKeypadViewModelstring bir Entry özelliği (bir PropertyChanged olayı tetikleyen tek özelliktir) ve türündeki ICommandüç özelliği tanımlar:

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; }
}

düğmesine karşılık gelen ClearCommand düğme her zaman etkindir ve girişi "0" olarak ayarlar:

public class DecimalKeypadViewModel : INotifyPropertyChanged
{

    ···

    public DecimalKeypadViewModel()
    {
        ClearCommand = new Command(
            execute: () =>
            {
                Entry = "0";
                RefreshCanExecutes();
            });

        ···

    }

    void RefreshCanExecutes()
    {
        ((Command)BackspaceCommand).ChangeCanExecute();
        ((Command)DigitCommand).ChangeCanExecute();
    }

    ···

}

Düğme her zaman etkinleştirildiğinden, oluşturucuda Command bir canExecute bağımsız değişken belirtmek gerekmez.

Sayı ve geri saydamlık girme mantığı biraz karmaşıktır çünkü basamak girilmemişse özelliği Entry "0" dizesidir. Kullanıcı daha fazla sıfır içeriyorsa, Entry yine de yalnızca bir sıfır içerir. Kullanıcı başka bir basamak yazarken, bu basamak sıfırın yerini alır. Ancak kullanıcı başka bir basamak öncesinde bir ondalık nokta yazdıysa , Entry "0" dizesidir.

Geri Al düğmesi yalnızca girdinin uzunluğu 1'den büyükse veya "0" dizesine eşit değilse Entry etkinleştirilir:

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";
            });

        ···

    }

    ···

}

Geri Al düğmesinin execute işlevinin mantığı, işlevinin Entry en az "0" dizesi olmasını sağlar.

DigitCommand özelliği, her biri kendisini özelliğiyle tanımlayan 11 düğmeye CommandParameter bağlıdır. DigitCommand normal Command sınıfın bir örneğine ayarlanabilir, ancak genel sınıfı kullanmak Command<T> daha kolaydır. Komut arabirimini XAML ile kullanırken, CommandParameter özellikler genellikle dizelerdir ve bu genel bağımsız değişkenin türüdür. ve executecanExecute işlevleri daha sonra türünde stringbağımsız değişkenlere sahiptir:

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("."));
            });
    }

    ···

}

yöntemi, execute dize bağımsız değişkenini özelliğine Entry ekler. Ancak, sonuç sıfırla (sıfır ve ondalık noktayla değil) başlıyorsa, ilk sıfır işlevi kullanılarak Substring kaldırılmalıdır.

canExecute yöntemi yalnızca bağımsız değişken ondalık noktaysa (ondalık ayırıcıya basıldığını gösterir) ve Entry zaten bir ondalık nokta içeriyorsa döndürürfalse.

execute Tüm yöntemler, hem hem de ClearCommandDigitCommand için çağrı yapan öğesini çağırırRefreshCanExecutesChangeCanExecute. Bu, girilen basamakların geçerli sırasına göre ondalık nokta ve geri al düğmelerinin etkinleştirilmesini veya devre dışı bırakılmasını sağlar.

Gezinti Menüleri için Zaman Uyumsuz Komut Oluşturma

Komut, gezinti menülerini uygulamak için kullanışlıdır. MainPage.xaml dosyasının bir bölümü aşağıdadır:

<?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>

XAML ile komut kullanılırken, CommandParameter özellikler genellikle dizelere ayarlanır. Ancak bu durumda, türünde System.Typeolması için CommandParameter bir XAML işaretleme uzantısı kullanılır.

Her Command özellik adlı NavigateCommandbir özelliğe bağlıdır. Bu özellik arka planda kod dosyasında tanımlanır 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; }
}

Oluşturucu, özelliğini parametresini NavigateCommand başlatan bir execute yönteme System.Type ayarlar ve ardından bu yönteme gider. PushAsync Çağrısı bir await işleç gerektirdiğinden execute yöntemi zaman uyumsuz olarak işaretlenmelidir. Bu, parametre listesinden önce anahtar async sözcüğüyle gerçekleştirilir.

Oluşturucu, bağlamaların bu sınıfta öğesine başvurması NavigateCommand için sayfanın kendisini de ayarlarBindingContext.

Bu oluşturucudaki kodun sırası bir fark yaratır: InitializeComponent Çağrı XAML'nin ayrıştırılmasına neden olur, ancak o sırada adlı NavigateCommand bir özelliğe bağlama çözümlenemez çünkü BindingContext olarak ayarlanır null. BindingContext oluşturucuda ayarlanmadan önceNavigateCommand ayarlanırsa, bağlama ayarlandığında çözümlenebilirBindingContext, ancak o zaman NavigateCommand hala nullolur. sonra ayarının NavigateCommand bağlama üzerinde hiçbir etkisi olmaz çünkü bir değişiklik NavigateCommand bir PropertyChanged olayı tetiklemez ve bağlama bunun geçerli olduğunu NavigateCommand bilmez.BindingContext

XAML ayrıştırıcısı bağlama tanımıyla karşılaştığında bağlamanın her iki bileşeni de ayarlandığından, çağrısından InitializeComponent önce hem hem BindingContext de NavigateCommand (herhangi bir sırada) ayarı çalışır.

Veri bağlamaları bazen karmaşık olabilir, ancak bu makale dizisinde gördüğünüz gibi güçlü ve çok yönlüdür ve temel alınan mantığı kullanıcı arabiriminden ayırarak kodunuzu düzenlemeye büyük ölçüde yardımcı olur.