AsyncRelayCommand e AsyncRelayCommand<T>

Os AsyncRelayCommand e AsyncRelayCommand<T> são ICommand implementações que estendem as funcionalidades oferecidas por RelayCommand, com suporte para operações assíncronas.

APIs da plataforma:AsyncRelayCommand, AsyncRelayCommand<T>, RelayCommand, IAsyncRelayCommand, IAsyncRelayCommand<T>

Como funcionam

AsyncRelayCommand e AsyncRelayCommand<T> têm as seguintes características principais:

  • Estendem as funcionalidades dos comandos síncronos incluídos na biblioteca, com suporte para delegados que devolvem Task.
  • Podem envolver funções assíncronas com um parâmetro adicional CancellationToken para suportar cancelamento, e expõem as propriedades CanBeCanceled e IsCancellationRequested, bem como um método Cancel.
  • Eles expõem uma ExecutionTask propriedade que pode ser usada para monitorizar o progresso de uma operação pendente, e IsRunning que pode ser usada para verificar quando uma operação termina. Isto é particularmente útil para associar um comando a elementos da interface, como indicadores de carregamento.
  • Implementam as interfaces IAsyncRelayCommand e IAsyncRelayCommand<T>, o que significa que o modelo de vista pode expor facilmente comandos através delas para reduzir o acoplamento forte entre tipos. Por exemplo, isto facilita substituir um comando por uma implementação personalizada que expõe a mesma superfície pública da API, se necessário.

Trabalhar com comandos assíncronos

Vamos imaginar um cenário semelhante ao descrito no RelayCommand exemplo, mas com um comando a executar uma operação assíncrona:

public class MyViewModel : ObservableObject
{
    public MyViewModel()
    {
        DownloadTextCommand = new AsyncRelayCommand(DownloadText);
    }

    public IAsyncRelayCommand DownloadTextCommand { get; }

    private Task<string> DownloadText()
    {
        return WebService.LoadMyTextAsync();
    }
}

Com o código de interface relacionado:

<Page
    x:Class="MyApp.Views.MyPage"
    xmlns:viewModels="using:MyApp.ViewModels"
    xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters">
    <Page.DataContext>
        <viewModels:MyViewModel x:Name="ViewModel"/>
    </Page.DataContext>
    <Page.Resources>
        <converters:TaskResultConverter x:Key="TaskResultConverter"/>
    </Page.Resources>

    <StackPanel Spacing="8" xml:space="default">
        <TextBlock>
            <Run Text="Task status:"/>
            <Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask.Status, Mode=OneWay}"/>
            <LineBreak/>
            <Run Text="Result:"/>
            <Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask, Converter={StaticResource TaskResultConverter}, Mode=OneWay}"/>
        </TextBlock>
        <Button
            Content="Click me!"
            Command="{x:Bind ViewModel.DownloadTextCommand}"/>
        <ProgressRing
            HorizontalAlignment="Left"
            IsActive="{x:Bind ViewModel.DownloadTextCommand.IsRunning, Mode=OneWay}"/>
    </StackPanel>
</Page>

Ao clicar no Button, o comando é executado e o ExecutionTask é atualizado. Quando a operação termina, a propriedade gera uma notificação que é refletida na interface. Neste caso, tanto o estado da tarefa como o resultado atual da tarefa são apresentados. Note que, para mostrar o resultado da tarefa, é necessário usar o TaskExtensions.GetResultOrDefault método – isto dá acesso ao resultado de uma tarefa que ainda não foi concluída sem bloquear o thread (e possivelmente causar um deadlock).

Exemplos

  • Dá uma vista de olhos à aplicação de exemplo (para múltiplos frameworks de interface) para veres o MVVM Toolkit em ação.
  • Também podes encontrar mais exemplos nos testes unitários.