我如何在mvvmcross视图模型中使用异步

本文关键字:异步 模型 视图 mvvmcross | 更新日期: 2023-09-27 18:19:11

我在mvvmcross视图模型中有一个长时间运行的过程,并希望使其异步(http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx)。

async关键字目前在Xamarin的测试版通道中得到支持。

下面是我目前如何实现异步的一个例子。IsBusy标志可以被绑定到一个UI元素,并显示一个加载消息。

这是正确的方式吗?

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

我如何在mvvmcross视图模型中使用异步

您应该避免使用async void。当你处理ICommand时,你确实需要使用async void,但它的范围应该最小化。

修改后的代码将您的操作公开为async Task,这是单元可测试的,并且可以从代码的其他部分使用:

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

您使用IsBusy是好的;这是异步ui中的一种常见方法。

I did change Task.Factory.StartNew to Task.Run;在async代码中首选Task.Run,原因由Stephen Toub描述。

MvvmCross现在有MvxAsyncCommand(见GitHub提交)。

所以我们不这样做

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

你可以这样做

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

看起来很好,除了我将在等待周围添加一个try catch finally。

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

进一步,我有一个例子在我的博客上使用MvxCommand与async。与您的示例非常相似http://deapsquatter.blogspot.com/2013/03/updating-my-mobile-apps-for-async.html

你也可以使用 methodbbinding 插件来避免模板代码(命令),直接将你的UI绑定到async方法。

此外,如果您使用Fody PropertyChanged,您的代码将看起来像这样:

[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;
    }
}

你可以这样设置绑定:"Click DoSomething"。

另一方面,与其使用await Task.Factory.StartNew(),为什么不让_myService.LongRunningProcess异步?这会更好看:

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