Uso de comandos en un modelo de vista
Ha visto cómo obtener datos de los modelos de vista a la interfaz de usuario y cómo puede usar el enlace bidireccional para devolver los datos a los modelos de vista.
El uso de enlaces bidireccionales de este tipo es la mejor manera de reaccionar a los cambios en la interfaz de usuario cada vez que los datos cambian. Muchos de los elementos que se administrarían como eventos pueden controlarse con enlaces bidireccionales y MVVM. Otros ejemplos son cosas como Switch.IsToggled
y Slider.Value
, que se pueden reflejar en nuestro modelo de vista como un valor booleano o entero, sin tener que usar eventos.
Aun así, algunas cosas como la activación de un elemento Button
o MenuItem
no están vinculadas directamente a la modificación de datos. Estas interacciones todavía necesitan un control similar a un evento. Dado que estos componentes de interfaz de usuario suelen invocar algún tipo de lógica con los datos, queremos esa lógica en el modelo de vista. Pero no se pretende controlarlas como eventos Clicked
y Selected
en el código subyacente, si es posible. Queremos que tanto como sea posible estar en el modelo de vista, de esa manera es probable.
Uso del patrón de comandos
Muchos de los controles MAUI de .NET que tienen este tipo de interacción admiten el enlace a una propiedad que expone una interfaz ICommand
. Es más probable que esta propiedad tenga el nombre Command
. El botón es un ejemplo:
<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" />
El control sabe cuándo invocar el comando. Por ejemplo, un botón invoca el comando cuando se presiona. El comando de este ejemplo está enlazado a la propiedad GiveBonusCommand
del modelo de vista. El tipo de propiedad tiene que implementar la interfaz ICommand
. El código debería tener un aspecto parecido al siguiente:
public class EmployeeViewModel : INotifyPropertyChanged
{
public ICommand GiveBonusCommand {get; private set;}
...
}
La interfaz ICommand
tiene un método Execute
al que se llama cuando se hace clic en el botón. De este modo, ICommand.Execute
reemplazará directamente el código de control de eventos Button.Click
.
La interfaz completa de ICommand
tiene dos métodos más: CanExecute
y CanExecuteChanged
pueden usarse para determinar si un control debe aparecer habilitado o deshabilitado.
Un botón, por ejemplo, podría aparecer atenuado si CanExecute
devuelve false.
Este es el aspecto de la interfaz ICommand
en C#:
public interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
Usar la clase Command
Este patrón de comando permite mantener una separación clara entre el comportamiento de la interfaz de usuario y su implementación. Pero puede complicar el código si se necesita crear una clase independiente para implementar cada controlador de eventos.
En lugar de crear varias clases personalizadas que implementen la interfaz, lo habitual es usar Command
o Command<T>
. Estas clases implementan ICommand
pero exponen su comportamiento como propiedades en el modelo de vista que puede establecer. Esto permite implementar la propiedad GiveBonusCommand
que se ha descrito antes completamente en la clase modelo de vista:
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.
}
}
En este código, el comportamiento de Execute
se proporciona mediante el método GiveBonusExecute
. Y CanExecute
se proporciona mediante GiveBonusCanExecute
. Los delegados a esos métodos se pasan al constructor Command
. En este ejemplo, no hay ninguna implementación para CanExecuteChanged
.
Simplificación con el kit de herramientas de MVVM
La biblioteca del kit de herramientas de MVVM contiene implementaciones de ICommand
conocidas como RelayCommand
y AsyncRelayCommand
. También proporciona generadores de origen para simplificar aún más este código. En el siguiente ejemplo, se generará el GiveBonusCommand
estableciendo tanto el método a llamar para ejecutar como a llamar para ver si se puede ejecutar. El atributo [RelayCommand]
se usa en el método GiveBonus
y generará el GiveBonusCommand
. Además, al establecer la propiedad CanExecute
en el atributo en el nombre del método que queremos enlazar al método CanExecute
del ICommand
, generará el código para configurarlo para nosotros.
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;
}
}
El kit de herramientas de MVVM también controla los métodos async
, que son comunes en la programación de .NET.
Comandos con parámetros
La interfaz ICommand
acepta un parámetro object
para los métodos CanExecute
y Execute
. .NET MAUI implementa esta interfaz sin ninguna comprobación de tipos a través de la clase Command
. Los delegados que adjunte al comando deben realizar su propia comprobación de tipos para asegurarse de que se pasa el parámetro correcto. .NET MAUI también proporciona la implementación de Command<T>
donde se establece el tipo de parámetro esperado. Cuando cree un comando que acepte un único tipo de parámetro, use Command<T>
.
Los controles MAUI de .NET que implementan el patrón de comandos proporcionan la propiedad CommandParameter
. Al establecer esta propiedad, puede pasar un parámetro al comando cuando se invoca con Execute
, o cuando comprueba el estado del método CanExecute
.
En este ejemplo, el valor de cadena 25 se envía al comando:
<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" CommandParameter="25" />
El comando tendría que interpretar y convertir ese parámetro de cadena. Hay muchas maneras de proporcionar un parámetro fuertemente tipado.
En lugar de usar la sintaxis de atributo para definir
CommandParameter
, use elementos XAML.<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}"> <Button.CommandParameter> <x:Int32>25</x:Int32> </Button.CommandParameter> </Button>
Enlace el
CommandParameter
a una instancia del tipo correcto.Si el
CommandParameter
está enlazado al tipo incorrecto, aplique un convertidor para convertir el valor en el tipo correcto.