다음을 통해 공유


명령

Browse sample. 샘플 찾아보기

MVVM(Model-View-ViewModel) 패턴을 사용하는 .NET 다중 플랫폼 앱 UI(.NET MAUI) 앱에서는 일반적으로 파생되는 클래스 INotifyPropertyChanged인 viewmodel의 속성과 일반적으로 XAML 파일인 뷰의 속성 간에 데이터 바인딩이 정의됩니다. 경우에 따라 사용자가 viewmodel의 항목에 영향을 주는 명령을 시작하도록 요구하여 앱이 이러한 속성 바인딩을 넘어서야 하는 경우가 있습니다. 이러한 명령은 일반적으로 단추를 클릭하거나 손가락으로 탭하여 신호를 받으며, 대개 ButtonClicked 이벤트 또는 TapGestureRecognizerTapped 이벤트에 대한 처리기의 코드 숨김 파일에서 처리됩니다.

명령 인터페이스는 MVVM 아키텍처에 훨씬 더 적합한 명령을 구현하는 또 다른 방법을 제공합니다. viewmodel에는 클릭과 같은 뷰의 특정 활동에 대한 반응으로 실행되는 메서드인 명령이 Button 포함될 수 있습니다. 데이터 바인딩은 이러한 명령과 Button 간에 정의됩니다.

viewmodel과 viewmodel ButtonButton 간의 데이터 바인딩을 허용하려면 다음 두 가지 속성을 정의합니다.

명령 인터페이스를 사용하려면 소스가 형식의 viewmodel에 있는 속성인 위치의 Button 속성을 대상으로 Command 하는 데이터 바인딩을 정의합니다ICommand. viewmodel에는 단추를 클릭할 때 실행되는 해당 ICommand 속성과 연결된 코드가 포함되어 있습니다. 뷰모델의 CommandParameter 동일한 ICommand 속성에 모두 바인딩된 경우 속성을 임의의 데이터로 설정하여 여러 단추를 구분할 수 있습니다.

다른 많은 뷰도 정의하고 속성을 정의 Command 합니다 CommandParameter . 이러한 모든 명령은 뷰의 사용자 인터페이스 개체에 의존하지 않는 접근 방식을 사용하여 viewmodel 내에서 처리할 수 있습니다.

ICommands

인터페이스는 ICommand System.Windows.Input 네임스페이스에 정의되며 두 가지 메서드와 하나의 이벤트로 구성됩니다.

public interface ICommand
{
    public void Execute (Object parameter);
    public bool CanExecute (Object parameter);
    public event EventHandler CanExecuteChanged;
}

명령 인터페이스를 사용하려면 viewmodel에 다음과 같은 형식 ICommand의 속성이 포함되어야 합니다.

public ICommand MyCommand { private set; get; }

viewmodel은 인터페이스를 구현 ICommand 하는 클래스도 참조해야 합니다. 뷰 Command 에서 a Button 속성은 해당 속성에 바인딩됩니다.

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

사용자가 Button을 누르면 Button이 해당 Command 속성에 바인딩된 ICommand 개체에서 Execute 메서드를 호출합니다.

바인딩이 ButtonCommand 속성에 처음 정의되고 데이터 바인딩이 어떤 방식으로든 변경되면 ButtonICommand 개체의 CanExecute 메서드를 호출합니다. CanExecute에서 false가 반환되면 Button 자체를 사용하지 않도록 자동으로 설정됩니다. 이는 특정 명령이 현재 사용될 수 없거나 유효하지 않음을 나타냅니다.

또한 ButtonICommandCanExecuteChanged 이벤트에 대한 처리기를 연결합니다. 이 이벤트는 viewmodel 내에서 발생합니다. 해당 이벤트가 발생하면 다시 호출 CanExecute 됩니다Button. CanExecute에서 true가 반환되면 Button 자체를 사용하도록 설정되고, CanExecute에서 false가 반환되면 자체를 사용하지 않도록 설정됩니다.

Warning

명령 인터페이스를 사용하는 경우 ButtonIsEnabled 속성은 사용하지 마세요.

viewmodel이 형식 ICommand의 속성을 정의하는 경우 viewmodel은 인터페이스를 구현 ICommand 하는 클래스도 포함하거나 참조해야 합니다. 이 클래스는 ExecuteCanExecute 메서드를 포함하거나 참조해야 하며, CanExecute 메서드에서 다른 값을 반환할 수 있을 때마다 CanExecuteChanged 이벤트를 발생시킵니다. .NET MAUI에 Command 포함된 클래스 또는 Command<T> 클래스를 사용하여 인터페이스를 ICommand 구현할 수 있습니다. 이러한 클래스를 사용하면 클래스 생성자에서 ExecuteCanExecute 메서드의 본문을 지정할 수 있습니다.

속성을 사용하여 동일한 ICommand 속성에 CommandParameter 바인딩된 여러 뷰와 요구 사항이 아닌 경우 클래스를 Command 구분할 때 사용합니다Command<T>.

기본 명령

다음 예제에서는 viewmodel에서 구현된 기본 명령을 보여 줍니다.

클래스는 PersonViewModel 명명NameAge된 세 가지 속성을 정의하고 Skills 사람을 정의합니다.

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

아래 표시된 클래스는 PersonCollectionViewModel 형식 PersonViewModel 의 새 개체를 만들고 사용자가 데이터를 채울 수 있도록 합니다. 이 목적을 위해 클래스는 형식, 형식 boolPersonEdit형식의 PersonViewModel속성을 정의IsEditing합니다. 또한 이 클래스는 ICommand 형식의 세 가지 속성과 IList<PersonViewModel> 형식의 Persons 속성을 정의합니다.

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

이 예제에서는 세 ICommand 가지 속성을 변경하고 Persons 속성을 변경해도 PropertyChanged 이벤트가 발생하지 않습니다. 이러한 속성은 클래스를 처음 만들 때 설정되며 변경되지 않습니다.

다음 예제에서는 다음을 사용하는 XAML을 PersonCollectionViewModel보여 줍니다.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.PersonEntryPage"
             Title="Person Entry">
    <ContentPage.BindingContext>
        <local:PersonCollectionViewModel />
    </ContentPage.BindingContext>
    <Grid Margin="10">
        <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="Center" />
            <Button Text="Cancel"
                    Grid.Column="1"
                    Command="{Binding CancelCommand}"
                    VerticalOptions="Center" />
        </Grid>

        <!-- List of Persons -->
        <ListView Grid.Row="3"
                  ItemsSource="{Binding Persons}" />
    </Grid>
</ContentPage>

이 예제에서는 페이지의 BindingContext 속성이 .로 PersonCollectionViewModel설정됩니다. viewmodel Grid 의 속성에 바인딩된 NewCommand 속성이 Command 있는 New 텍스트, 속성에 바인딩된 IsEditing 속성이 있는 항목 폼 및 viewmodel의 PersonViewModel속성에 바인딩된 SubmitCommandCancelCommand 두 개의 단추가 포함됩니다.Button ListView 이미 입력한 사람의 컬렉션이 표시됩니다.

다음 스크린샷은 연령이 설정된 후 활성화된 제출 단추를 보여 줍니다.

Person Entry.

사용자가 먼저 새로 만들기 단추를 누르면 입력 폼이 활성화되지만 새로 만들기 단추를 사용하지 않도록 설정합니다. 그러면 사용자가 이름, 나이 및 기술을 입력합니다. 사용자는 편집하는 중에 언제라도 취소 단추를 눌러 다시 시작할 수 있습니다. 이름과 유효 기간이 입력된 경우에만 제출 단추가 활성화됩니다. 이 제출 단추를 누르면 사용자를 ListView에서 표시하는 컬렉션으로 전송합니다. 취소 또는 제출 단추를 누르면 입력 양식이 지워지고 새로 만들기 단추가 활성화됩니다.

새로 만들기, 제출취소 단추에 대한 모든 논리는 NewCommand, SubmitCommandCancelCommand 속성의 정의를 통해 PersonCollectionViewModel에서 처리됩니다. PersonCollectionViewModel의 생성자는 이러한 세 가지 속성을 Command 형식의 개체로 설정합니다.

클래스의 Command 생성자를 사용하면 형식 및 Func<bool> 메서드에 해당하는 형식 Action 의 인수를 ExecuteCanExecute 전달할 수 있습니다. 이 작업 및 함수는 생성자에서 Command 람다 함수로 정의할 수 있습니다.

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

사용자가 새로 만들기 단추를 클릭하면 Command 생성자에 전달된 execute 함수가 실행됩니다. 이렇게 하면 새 PersonViewModel 개체가 만들어지고, 해당 개체의 PropertyChanged 이벤트에 대한 처리기가 설정되고, IsEditingtrue로 설정되고, 생성자 뒤에 정의된 RefreshCanExecutes 메서드가 호출됩니다.

또한 Command 클래스는 ICommand 인터페이스를 구현하는 것 외에도 ChangeCanExecute라는 메서드를 정의합니다. viewmodel은 메서드의 ICommandCanExecute 반환 값을 변경할 수 있는 모든 일이 발생할 때마다 속성을 호출 ChangeCanExecute 해야 합니다. ChangeCanExecute에 대한 호출로 인해 Command 클래스에서 CanExecuteChanged 메서드를 실행합니다. Button은 해당 이벤트에 대한 처리기를 연결하고, CanExecute를 다시 호출한 후 해당 메서드의 반환 값에 따라 자체를 활성화하여 응답합니다.

NewCommandexecute 메서드에서 RefreshCanExecutes를 호출하면 NewCommand 속성이 ChangeCanExecute에 대한 호출을 가져오고, ButtoncanExecute 메서드를 호출하고, IsEditing 속성이 현재 true이므로 false를 반환합니다.

PropertyChangedPersonViewModel 개체의 처리기는 다음 메서드SubmitCommandChangeCanExecute 호출합니다.

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

SubmitCommand에 대한 canExecute 함수는 편집하는 PersonViewModel 개체에서 변경되는 속성이 있을 때마다 호출됩니다. Name 속성이 하나 이상의 문자이고 Age가 0보다 큰 경우에만 true를 반환합니다. 이때 제출 단추가 활성화됩니다.

Submit 함수는 execute 속성 변경 처리기를 컬렉션에서 PersonViewModel제거하고, 개체를 컬렉션에 Persons 추가하고, 모든 항목을 초기 상태로 반환합니다.

취소 단추에 대한 execute 함수는 제출 단추에서 개체를 컬렉션에 추가하는 것을 제외하고 수행하는 모든 작업을 수행합니다.

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

PersonViewModel이 편집될 때마다 canExecute 메서드에서 true를 반환합니다.

참고 항목

executecanExecute 메서드를 람다 함수로 정의할 필요는 없습니다. viewmodel에서 프라이빗 메서드로 작성하고 생성자에서 Command 참조할 수 있습니다. 그러나 이 방법을 사용하면 viewmodel에서 한 번만 참조되는 많은 메서드가 발생할 수 있습니다.

명령 매개 변수 사용

viewmodel에서 동일한 ICommand 속성을 공유하는 하나 이상의 단추 또는 다른 사용자 인터페이스 개체에 편리한 경우도 있습니다. 이 경우 이 속성을 사용하여 CommandParameter 단추를 구분할 수 있습니다.

Command 클래스는 이러한 공유 ICommand 속성에 대해 계속 사용할 수 있습니다. 클래스는 형식Object의 매개 변수를 사용하여 수락하고 canExecute 메서드를 허용하는 대체 생성자를 정의합니다execute. 이는 CommandParameter가 이러한 메서드에 전달되는 방법입니다. 그러나 지정 하는 CommandParameter경우 제네릭 Command<T> 클래스를 사용 하 여 개체 집합의 형식을 지정 하는 CommandParameter것이 가장 쉽습니다. 지정한 executecanExecute 메서드에는 해당 형식의 매개 변수가 있습니다.

다음 예제에서는 소수 자릿수를 입력하기 위한 키보드를 보여 줍니다.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.DecimalKeypadPage"
             Title="Decimal Keyboard">
    <ContentPage.BindingContext>
        <local:DecimalKeypadViewModel />
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style TargetType="Button">
            <Setter Property="FontSize" Value="32" />
            <Setter Property="BorderWidth" Value="1" />
            <Setter Property="BorderColor" Value="Black" />
        </Style>
    </ContentPage.Resources>

    <Grid WidthRequest="240"
          HeightRequest="480"
          ColumnDefinitions="80, 80, 80"
          RowDefinitions="Auto, Auto, Auto, Auto, Auto, Auto"
          ColumnSpacing="2"
          RowSpacing="2"
          HorizontalOptions="Center"
          VerticalOptions="Center">
        <Label Text="{Binding Entry}"
               Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
               Margin="0,0,10,0"
               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>

이 예제에서 페이지의 BindingContext 형식은 .입니다 DecimalKeypadViewModel. 이 Entry viewmodel의 속성은 의 속성LabelText 바인딩됩니다. 모든 개체는 Button viewmodel의 ClearCommandBackspaceCommandDigitCommand명령에 바인딩됩니다. 10개 숫자 및 소수점에 대한 11개 단추는 DigitCommand에 대한 바인딩을 공유합니다. CommandParameter는 이러한 단추를 구분합니다. 설정된 CommandParameter 값은 일반적으로 10진수 점을 제외하고 단추에 표시되는 텍스트와 동일하며, 이는 선명도를 위해 가운데 점 문자로 표시됩니다.

Decimal keyboard.

형식 DecimalKeypadViewModel 의 속성과 형식 string 의 세 가지 속성을 ICommand정의합니다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; }
}

이 단추에 ClearCommand 해당하는 단추는 항상 사용하도록 설정되며 항목을 다시 "0"으로 설정합니다.

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

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

단추가 항상 활성화되므로 Command 생성자에서 canExecute 인수를 지정할 필요가 없습니다.

항목의 길이가 1보다 크거나 Entry가 "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";
            });
        ···
    }
    ···
}

백스페이스 단추에 대한 execute 함수의 논리는 Entry가 적어도 "0" 문자열임을 보장합니다.

DigitCommand 속성은 각각 CommandParameter 속성으로 식별되는 11개의 단추에 바인딩됩니다. 클래스 DigitCommand 의 인스턴스로 설정됩니다 Command<T> . XAML CommandParameter 에서 명령 인터페이스를 사용하는 경우 속성은 일반적으로 제네릭 인수의 형식인 문자열입니다. 그러면 executecanExecute 함수에 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("."));
            });
    }
    ···
}

execute 메서드는 문자열 인수를 Entry 속성에 추가합니다. 그러나 결과가 0으로 시작하는 경우(0 및 소수점이 아님) Substring 함수를 사용하여 초기 0을 제거해야 합니다. 인수가 소수점이고(소수점을 눌렀는지 나타냄) Entry에 이미 소수점이 포함되어 있는 경우에만 canExecute 메서드에서 false를 반환합니다. 모든 execute 메서드는 RefreshCanExecutes를 호출한 다음, DigitCommandClearCommand 모두에 대해 ChangeCanExecute를 호출합니다. 이렇게 하면 입력된 숫자의 현재 순서에 따라 소수점 및 백스페이스 단추가 활성화 또는 비활성화되도록 보장합니다.