Usar os comandos em um viewmodel

Concluído

Você viu como obter dados dos seus viewmodels para sua interface do usuário e como usar vinculação bidirecional para obter dados de volta nos seus viewmodels.

Usar vinculações bidirecionais como essa é a maneira preferencial para reagir às alterações na interface do usuário sempre que os dados forem alterados. Muitas coisas que trataríamos como eventos podem ser manipuladas usando o MVVM e as vinculações bidirecionais. Outros exemplos são coisas como Switch.IsToggled e Slider.Value, que podem ser refletidas no nosso viewmodel como um valor booliano ou inteiro, sem ter que usar eventos.

Mas há algumas coisas como uma ativação Button ou MenuItem que não estão vinculadas diretamente à alteração de dados. Essas interações ainda requerem o tratamento semelhante a eventos. Como esses componentes de interface do usuário geralmente invocam algum tipo de lógica com os dados, queremos que essa lógica esteja no viewmodel. Mas não queremos tratá-los como eventos Clicked e Selected no code-behind, se possível. Queremos o máximo possível no viewmodel, dessa forma ele é testável.

Usar o padrão de comando

Muitos dos controles do .NET MAUI que têm esse tipo de interação dão suporte para associação a uma propriedade que expõe uma interface ICommand. Essa propriedade provavelmente tem o nome Command. O botão é um exemplo:

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

O controle sabe quando invocar o comando. Por exemplo, um botão invoca o comando quando é pressionado. Neste exemplo, o comando está associado à propriedade GiveBonus do viewmodel. O tipo de propriedade deve implementar a interface ICommand. O código seria semelhante a isto:

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

A interface ICommand tem um método Execute que é chamado quando o botão é clicado. Dessa forma, o ICommand.Execute diretamente substitui o código de manipulação de eventos Button.Click.

A interface completa ICommand possui dois métodos adicionais: CanExecute e CanExecuteChanged, que são usados para determinar se um controle deve aparecer habilitado ou desabilitado.

Um botão, por exemplo, poderá aparecer esmaecido se CanExecute retornar false.

Aqui está como a interface ICommand se parece em C#:

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

Use a classe Command

Esse padrão de comando permite manter uma separação clara entre o comportamento e a implementação da interface do usuário. No entanto, isso poderá complicar o seu código se for necessário criar uma classe separada para implementar cada manipulador de eventos.

Em vez de criar várias classes personalizadas que implementam a interface, é comum usar Command ou Command<T>. Essas classes implementam ICommand, mas expõem seu comportamento como propriedades no seu viewmodel que você pode definir. Isso permite que você implemente a propriedade GiveBonus descrita anteriormente inteiramente dentro da nossa classe viewmodel:

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

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

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

Nesse código, o comportamento Execute é sendo fornecido pelo método GiveBonusExecute. E CanExecute é fornecido por GiveBonusCanExecute. Os itens delegados a esses métodos são passados para o construtor Command. Neste exemplo, não há nenhuma implementação para CanExecuteChanged.

Comandos com parâmetros

A interface ICommand aceita um parâmetro object para os métodos CanExecute e Execute. O .NET MAUI implementa essa interface sem verificação de tipo através da classe Command. Os delegados que você anexa ao comando devem fazer sua própria verificação de tipo para garantir que o parâmetro correto seja passado. O .NET MAUI também fornece a implementação Command<T> na qual você define o tipo de parâmetro esperado. Quando você cria um comando que aceita um único tipo de parâmetro, use Command<T>.

Os controles do .NET MAUI que implementam o padrão de comando fornecem a propriedade CommandParameter. Ao definir essa propriedade, você pode passar um parâmetro para o comando quando ele é invocado com Execute, ou quando ele verifica o método CanExecute para obter o status.

Neste exemplo, o valor da cadeia de caracteres 25 é enviado para o comando:

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

O comando precisaria interpretar e converter esse parâmetro de cadeia de caracteres. Existem muitas maneiras de fornecer um parâmetro fortemente tipado.

  • Em vez de usar a sintaxe de atributo para definir CommandParameter, use elementos XAML.

    <Button Text="Give Bonus" Command="{Binding GiveBonus}">
        <Button.CommandParameter>
            <x:Int32>25</x:Int32>
        </Button.CommandParameter>
    </Button>
    
  • Vincule o CommandParameter a uma instância do tipo correto.

  • Se o CommandParameter estiver associado ao tipo incorreto, aplique um conversor para converter o valor para o tipo correto.

Verifique seu conhecimento

1.

Qual é o propósito da interface ICommand?

2.

Como as classes Command ou Command<T> podem simplificar o uso da interface ICommand?

3.

Qual é a função da propriedade CommandParameter nos controles do .NET MAUI?