Comment puis-je utiliser async dans un modèle de vue mvvmcross?

J'ai un processus de longue durée dans un viewmodel mvvmcross et je souhaite le rendre asynchrone ( http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx).

Le mot-clé async est actuellement pris en charge dans le canal bêta pour Xamarin.

Voici un exemple de la façon dont j'implémente actuellement async. L'indicateur IsBusy peut être lié à un élément D'interface utilisateur et afficher un message de chargement.

Est - ce la bonne façon?

public class MyModel: MvxViewModel
{
    private readonly IMyService _myService;
    private bool _isBusy;

    public bool IsBusy
    {
        get { return _isBusy; }
        set { _isBusy = value; RaisePropertyChanged(() => IsBusy); ; }
    }

    public ICommand MyCommand
    {
        get
        {
            return new MvxCommand(DoMyCommand);
        }
    }

    public MyModel(IMyService myService)
    {
        _myService = myService;
    }

    public async void DoMyCommand()
    {
        IsBusy = true;
        await Task.Factory.StartNew(() =>
            {
                _myService.LongRunningProcess();
            });
        IsBusy = false;
    }

}
25
demandé sur svick 2013-06-19 13:01:00

4 réponses

Vous devriez éviter async void. Lorsque vous avez affaire à ICommand, vous devez utiliser async void, mais sa portée doit être minimisée.

Ce code modifié expose votre action en tant que async Task, qui est testable à l'unité et consommable à partir d'autres parties de votre code:

public class MyModel: MvxViewModel
{
  private readonly IMyService _myService;
  private bool _isBusy;

  public bool IsBusy
  {
    get { return _isBusy; }
    set { _isBusy = value; RaisePropertyChanged(() => IsBusy); ; }
  }

  public ICommand MyCommand
  {
    get
    {
      return new MvxCommand(async () => await DoMyCommand());
    }
  }

  public MyModel(IMyService myService)
  {
    _myService = myService;
  }

  public async Task DoMyCommand()
  {
    IsBusy = true;
    await Task.Run(() =>
    {
      _myService.LongRunningProcess();
    });
    IsBusy = false;
  }
}

Votre utilisation de IsBusy est correcte; c'est une approche courante dans les interfaces utilisateur asynchrones.

, j'ai fait changer Task.Factory.StartNew pour Task.Run; Task.Run de préférence async code raisons décrites par Stephen Toub.

30
répondu Stephen Cleary 2013-06-19 11:06:55

MvvmCross a maintenant MvxAsyncCommand (voir GitHub commettre).

Donc au lieu de faire cela

public ICommand MyCommand
{
  get
  {
    return new MvxCommand(async () => await DoMyCommand());
  }
}

, Vous pouvez le faire

public ICommand MyCommand
{
  get
  {
    return new MvxAsyncCommand(DoMyCommand);
  }
}
5
répondu Colin Bacon 2016-06-22 13:41:30

Semble OK sauf que j'ajouterais un try catch enfin autour de cette attente.

    public async void DoMyCommand()
    {
        IsBusy = true;
        try{
            await Task.Factory.StartNew(() =>
                                        {
                _myService.LongRunningProcess();
            });
        }catch{
            //Log Exception
        }finally{
            IsBusy = false;
        }
    }

De plus, j'ai un exemple sur mon blog en utilisant un MvxCommand avec async. Très similaire à votre exemple http://deapsquatter.blogspot.com/2013/03/updating-my-mobile-apps-for-async.html

3
répondu Kevin 2013-06-19 10:33:09

Vous pouvez également utiliser MethodBinding plugin pour éviter le code de la plaque de chaudière( commandes), et lier votre interface utilisateur directement à la méthode asynchrone.

En outre, si vous utilisez Fody PropertyChanged , votre code ressemblerait à ceci:

[ImplementPropertyChanged]
public class MyModel: MvxViewModel
{
    private readonly IMyService _myService;

    public bool IsBusy { get; set; }

    public MyModel(IMyService myService)
    {
        _myService = myService;
    }

    public async Task DoSomething()
    {
        IsBusy = true;
        await Task.Factory.StartNew(() =>
        {
                _myService.LongRunningProcess();
        });
        IsBusy = false;
    }
}

Vous pouvez faire la liaison comme: "Cliquez sur DoSomething".

D'autre part, plutôt que d'utiliser await Task.Factory.StartNew(), pourquoi ne pas faire de _myService.LongRunningProcess asynchrone? Il aurait l'air beaucoup mieux:

public async Task DoSomething()
{
    IsBusy = true;
    await _myService.LongRunningProcess();
    IsBusy = false;
}
1
répondu xleon 2016-07-05 19:57:37