Подтвердить что ты не робот

Как использовать async в модели представления mvvmcross?

У меня есть длительный процесс в mvvmcross viewmodel и вы хотите сделать его асинхронным (http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx).

В настоящее время поддерживается ключевое слово async в бета-канале для Xamarin.

Ниже приведен пример того, как я в настоящее время реализую async. Флаг IsBusy может быть привязан к элементу пользовательского интерфейса и отображать загружаемое сообщение.

Правильно ли это?

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

}
4b9b3361

Ответ 1

Вам следует избегать 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 в порядке; что один общий подход в асинхронных пользовательских интерфейсах.

Я изменил Task.Factory.StartNew на Task.Run; Task.Run является предпочтительным в коде async для причин, описанных Стивеном Туубом.

Ответ 2

Теперь MvvmCross имеет MvxAsyncCommand (см. GitHub commit).

Итак, вместо этого

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

Вы можете сделать это

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

Ответ 3

Выглядит хорошо, но я бы добавил, что наконец-то поймать пробку, вот что ждет.

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

Далее у меня есть пример в моем блоге с помощью MvxCommand с асинхронным. Очень похож на ваш пример http://deapsquatter.blogspot.com/2013/03/updating-my-mobile-apps-for-async.html

Ответ 4

Вы также можете использовать MethodBinding plugin, чтобы избежать кодовых таблиц (команд), и привязать ваш интерфейс непосредственно к методу асинхронного вызова.

Кроме того, если вы используете 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;
    }
}

Вы можете сделать привязку как: "Нажмите" DoSomething ".

С другой стороны, вместо использования await Task.Factory.StartNew(), почему бы не сделать _myService.LongRunningProcess async? Это выглядело бы намного лучше:

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