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;
}
}
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.
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);
}
}
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
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;
}