Xamarin - Как мне обрабатывать изменения состояния пользовательского интерфейса в команде перед вызовом asyn c API - PullRequest
0 голосов
/ 26 мая 2020

Обычно я использую шаблон Command с формами Xamarin. Недавно пользовательские интерфейсы, над которыми я работал, включают команды, которые вызывают удаленный API, и, пока они это делают, они указывают, что что-то происходит, разными способами. Например, ActivityIndicator должен начать вращаться, когда вызов API возвращает страницу, должна отключить кнопку, et c.

То, что я бы интуитивно закодировал, это asyn c ICommand, например:

VerifyCodeCommand = new Command(async () =>
            {
                await VerifyCode(_email,_code);
            });

в функции VerifyCode я бы изменил свойство, привязанное к свойству IsRunning объекта ActivityIndicator, на true, await вызов API, а затем по завершении изменил IsRunning / Visible для ActivityIndicator обратно к ложному. Например, вот так: Другой пример показывает состояние сбоя API (например: красные предупреждения), а затем сбрасывает его непосредственно перед вызовом API.

К сожалению, это не работает, как и установка связанного свойства Device.BeginInvokeOnMainThread, поскольку (предположительно) ICommand удерживает поток пользовательского интерфейса (?). В этом простом случае я устанавливаю свойство на true в команде, затем Task.Run оставшуюся часть тела функции, то есть вызов API и сброс индикатора, например:

 Sending = true;

            (VerifyCodeCommand as Command).ChangeCanExecute();

            await Task.Run(async () =>

            {

                try

                {

                  // await api call here

                }

                catch (Exception ex)

                {

                    App.TrackError(ex, "Login", "VerifyCode API call failed.");

                }

                finally

                {

                    Sending = false;

                    (VerifyCodeCommand as Command).ChangeCanExecute();

                }

            });







        }

Необходим ли этот подход или мне не хватает какой-то функциональности в Xamarin (Forms)? Если всегда необходимо обновлять пользовательский интерфейс таким образом, что было бы разумным подходом для этого? Например, должны ли команды всегда иметь две фактические лямбды, одну для настройки начального состояния пользовательского интерфейса, другую для фонового вызова? Почему здесь не работает простой однопоточный асинхронный c подход - вызов API все-таки ожидается.

Я полагаю, почему вопрос сводится к следующему: почему это вызывает асинхронный c Вызов API с ожиданием в основном потоке выглядит так, как будто основной поток не освобождается для обновления пользовательского интерфейса (похоже, что жизненный цикл пользовательского интерфейса Xamarin вводит некоторые ограничения), и какое стандартное решение для этого?

1 Ответ

1 голос
/ 26 мая 2020

Я думаю, что ваша проблема в том, что вы перекладываете задачу await на асинхронную природу команды.

В качестве примера из текущего приложения: все мои модели просмотра имеют свойство bool IsBusy

Свойство public ICommand RefreshStoredDataCommand {get;}

В конструкторе: RefreshStoredDataCommand = new Command(async () => await ExecuteRefreshDataCommand(), () => !IsBusy);

private async Task ExecuteRefreshDataCommand()
{
    IsBusy = true;
    try
    {
      //Long calls to asynchronous processes
    }
    catch (Exception e)
    {
        Debug.WriteLine($"ProjectViewModel:ExecuteRefreshDataCommand exception{e.Message}");
    }
    finally
    {
        IsBusy = false;
    }

}

В показанном примере у меня в представлении есть элемент управления ListView, где я set

RefreshCommand ="{Binding RefreshStoredDataCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"

Очевидно, что для вашего кода вы бы вместо этого привязывали свой индикатор активности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...