Używanie poleceń w modelu viewmodel

Ukończone

Pokazano, jak pobierać dane z modelu widoków do interfejsu użytkownika i jak można użyć powiązania dwukierunkowego w celu uzyskania danych z powrotem do modelu widoków.

Używanie powiązań dwukierunkowych, takich jak to, jest preferowanym sposobem reagowania na zmiany z interfejsu użytkownika za każdym razem, gdy zmiany danych . Wiele rzeczy, które można obsłużyć jako zdarzenia , można obsłużyć za pomocą powiązań dwukierunkowych i wzorca Model-View-ViewModel (MVVM). Inne przykłady to takie elementy jak Switch.IsToggled i Slider.Value, które można odzwierciedlić w modelu viewmodel jako wartość logiczną lub całkowitą bez konieczności używania zdarzeń.

Istnieją jednak pewne elementy, takie jak aktywacja Button lub MenuItem , które nie są bezpośrednio powiązane ze zmianą danych. Te interakcje nadal wymagają obsługi podobnej do zdarzeń. Ponieważ te składniki interfejsu użytkownika zwykle wywołują jakąś logikę z danymi, chcemy, aby ta logika w modelu widoków. Ale nie chcemy obsługiwać ich jako Clicked i Selected zdarzeń w kodzie, jeśli to możliwe. Chcemy, aby jak najwięcej było w modelu widoków, w ten sposób można to przetestować.

Korzystanie ze wzorca polecenia

Wiele kontrolek MAUI platformy .NET, które mają tego rodzaju obsługę interakcji, wiążą się z właściwością uwidaczniającą ICommand interfejs. Ta właściwość najprawdopodobniej nosi nazwę Command. Kontrolka Button jest jednym z przykładów:

<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" />

Kontrolka wie, kiedy wywołać polecenie. Na przykład przycisk wywołuje polecenie po naciśnięciu . Polecenie w tym przykładzie jest powiązane z właściwością GiveBonus modelu viewmodel. Typ właściwości musi implementować ICommand interfejs. Kod będzie wyglądać podobnie do poniższego przykładu:

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonusCommand {get; private set;}
    ...
}

Interfejs ICommand ma metodę wywoływaną Execute po naciśnięciu przycisku. W ten sposób funkcja ICommand.Execute bezpośrednio zastępuje Button.Click kod obsługi zdarzeń.

ICommand Pełny interfejs ma jeszcze dwie metody: CanExecute i CanExecuteChanged służą do określania, czy kontrolka powinna być włączona, czy wyłączona.

Na przykład przycisk może pojawić się wygaszony, jeśli CanExecute zwraca wartość false.

Oto jak ICommand wygląda interfejs w języku C#:

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

Używanie klasy Command

Ten wzorzec polecenia umożliwia zachowanie czystego oddzielenia zachowania interfejsu użytkownika od implementacji interfejsu użytkownika. Jednak może to komplikować kod, jeśli musisz utworzyć oddzielną klasę w celu zaimplementowania każdej procedury obsługi zdarzeń.

Zamiast tworzyć kilka klas niestandardowych, które implementują interfejs, często używane Command są klasy lub Command<T>. Te klasy implementują ICommand , ale uwidaczniają jego zachowanie jako właściwości w modelu widoków, które można ustawić. W ten sposób możemy zaimplementować właściwość opisaną GiveBonus wcześniej całkowicie w naszej klasie viewmodel:

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonusCommand {get; private set;}
    public EmployeeViewModel(Employee model)
    {
        GiveBonusCommand = new Command(GiveBonusExecute, GiveBonusCanExecute)
    }

    void GiveBonusExecute()
    {
        //logic for giving bonus
    }

    bool GiveBonusCanExecute()
    {
        //logic for deciding if "give bonus" button should be enabled.
    }
}

W tym kodzie Execute zachowanie jest udostępniane przez metodę GiveBonusExecute. Funkcja CanExecute jest dostarczana przez program GiveBonusCanExecute. Delegaty do tych metod są przekazywane do konstruktora Command . W tym przykładzie nie ma implementacji dla elementu CanExecuteChanged.

Upraszczanie pracy z zestawem narzędzi MVVM Toolkit

Biblioteka zestawu narzędzi MVVM Toolkit zawiera implementacje ICommand znane jako RelayCommand i AsyncRelayCommand. Dostarcza również generatory źródłowe, aby jeszcze bardziej uprościć ten kod. W poniższym przykładzie zostanie wygenerowane ustawienie zarówno metody do wywołania do wykonania, jak i wywołania w celu sprawdzenia, GiveBonusCommand czy może zostać wykonane. Atrybut [RelayCommand] jest używany w metodzie GiveBonus i wygeneruje wartość GiveBonusCommand. Ponadto, ustawiając CanExecute właściwość atrybutu na nazwę metody, którą chcemy podłączyć do CanExecute metody ICommand, spowoduje wygenerowanie kodu w celu skonfigurowania go dla nas.

public partial class EmployeeViewModel : ObservableObject
{
    public EmployeeViewModel(Employee model)
    {
    }

    [RelayCommand(CanExecute = nameof(GiveBonusCanExecute))]
    void GiveBonus()
    {
        //logic for giving bonus
    }

    bool GiveBonusCanExecute()
    {
        //logic for deciding if "give bonus" button should be enabled.
        return true;
    }
}

Zestaw narzędzi MVVM obsługuje async również metody, które są wspólne w programowaniu platformy .NET.

Polecenia z parametrami

Interfejs ICommand akceptuje object parametr dla CanExecute metod i Execute . Program .NET MAUI implementuje ten interfejs bez sprawdzania Command typu za pośrednictwem klasy. Delegaty dołączane do polecenia muszą wykonać własne sprawdzanie typów, aby upewnić się, że został przekazany prawidłowy parametr. Program .NET MAUI udostępnia również implementację Command<T> , w której ustawiono typ oczekiwanego parametru. Podczas tworzenia polecenia, które akceptuje pojedynczy typ parametru, użyj polecenia Command<T>.

Kontrolki MAUI platformy .NET, które implementują wzorzec polecenia, zapewniają CommandParameter właściwość . Ustawiając tę właściwość, można przekazać parametr do polecenia podczas wywoływania go za pomocą Executepolecenia lub, gdy polecenie sprawdza metodę CanExecute stanu.

W tym przykładzie wartość ciągu "25" jest wysyłana do polecenia:

<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" CommandParameter="25" />

Polecenie musi interpretować i konwertować ten parametr ciągu. Istnieje wiele sposobów zapewnienia silnie typizowanego parametru.

  • Zamiast używać składni atrybutu do definiowania CommandParameter, użyj elementów XAML.

    <Button Text="Give Bonus" Command="{Binding GiveBonusCommand}">
        <Button.CommandParameter>
            <x:Int32>25</x:Int32>
        </Button.CommandParameter>
    </Button>
    
  • Powiąż element z CommandParameter wystąpieniem poprawnego typu.

  • Jeśli element CommandParameter jest powiązany z nieprawidłowym typem, zastosuj konwerter, aby przekonwertować wartość na poprawny typ.

Sprawdź swoją wiedzę

1.

Jaki jest cel interfejsu ICommand ?

2.

Jak klasy lub Command<T> mogą Command uprościć korzystanie z interfejsuICommand?

3.

Jaka jest rola CommandParameter właściwości w kontrolkach MAUI platformy .NET?