Używanie poleceń w modelu viewmodel
- 5 min
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 jest preferowanym sposobem reagowania na zmiany z interfejsu użytkownika za każdym razem, gdy zmieniają się dane. Wiele rzeczy, które zwykle traktujemy jako zdarzenia, można obsłużyć wykorzystując powiązania dwukierunkowe i wzorzec 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 jak w przypadku zdarzeń. Ponieważ te składniki interfejsu użytkownika zwykle wprowadzają pewną logikę związaną z danymi, chcemy, aby ta logika była umieszczona w ViewModelu. Ale nie chcemy obsługiwać ich jako zdarzeń Clicked
i Selected
w pliku code-behind, jeśli to możliwe. Chcemy, aby jak najwięcej było w modelu widoku, tak aby można było to przetestować.
Korzystanie ze wzorca polecenia
Wiele kontrolek MAUI platformy .NET, które mają tego rodzaju interakcję, obsługuje powiązanie z właściwością udostępniają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ć interfejs ICommand
. 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ę Execute
, która jest wywoływana 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żywa się Command
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 zapewniane 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 GiveBonusCommand
, które ustawia zarówno metodę do wywołania w celu wykonania, jak i metodę do sprawdzenia, czy można ją wykonać. Atrybut [RelayCommand]
jest używany w metodzie GiveBonus
i wygeneruje wartość GiveBonusCommand
. Ponadto, ustawienie właściwości CanExecute
w atrybucie na nazwę metody, którą chcemy powiązać z metodą CanExecute
ICommand
, spowoduje wygenerowanie kodu, aby ją skonfigurować.
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 również obsługuje async
metody powszechnie używane w programowaniu .NET.
Polecenia z parametrami
Interfejs ICommand
akceptuje object
parametr dla CanExecute
metod i Execute
. Program .NET MAUI implementuje ten interfejs bez żadnego sprawdzania typu za pomocą klasy Command
. 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, posiadają właściwość CommandParameter
. Ustawiając tę właściwość, można przekazać parametr do polecenia podczas wywoływania go za pomocą Execute
, lub gdy polecenie sprawdza metodę CanExecute
dla statusu.
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 na zapewnienie 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.