Есть ли простой способ выполнить метод в потоке пользовательского интерфейса во время асинхронной задачи? - PullRequest
0 голосов
/ 18 января 2019

Во время длительного выполнения асинхронного метода, запущенного с Task.Run() Мне может потребоваться вернуться к пользователю для дополнительного ввода или подтверждения, например, окна сообщения или диалогового окна файла, которое должно быть выполнено в потоке пользовательского интерфейса.

Есть ли простой способ сделать это?

private void buttonApply_Click (object sender, EventArgs e) {
  try {
    // ...
    await executeAsync (_cts.Token, progress);
  } catch (OperationCanceledException) { }    
  // ...
}

private async Task executeAsync (CancellationToken cancellationToken, IProgress<string> progress) { 
  // ...      
  await Task.Run (() => execute (path, cancellationToken, progress), cancellationToken);
}

private void execute (string path, CancellationToken cancellationToken, IProgress<string> progress) {
  // do some work, report progress, check for cancellation
  // --> depending on initial work, request additional input via UI thread, how?
  // do more work, based on initial work and requested input
}

Ответы [ 2 ]

0 голосов
/ 18 января 2019

Существует структура, называемая ReactiveUI - она ​​использует ReactiveExtensions и наблюдаемые, чтобы упростить жизнь MVVM.

У вас нет глубокого погружения и вы используете весь фреймворк, но у него есть четкая концепция Взаимодействие :

public class ViewModel : ReactiveObject
{
    private readonly Interaction<string, bool> confirm;

    public ViewModel()
    {
        this.confirm = new Interaction<string, bool>();
    }

    public Interaction<string, bool> Confirm => this.confirm;

    public async Task DeleteFileAsync()
    {
        var fileName = ...;

        // this will throw an exception if nothing handles the interaction
        var delete = await this.confirm.Handle(fileName);

        if (delete)
        {
            // delete the file
        }
    }
}

public class View
{
    public View()
    {
        this.WhenActivated(
            d =>
            {
                d(this
                    .ViewModel
                    .Confirm
                    .RegisterHandler(
                        async interaction =>
                        {
                            var deleteIt = await this.DisplayAlert(
                                "Confirm Delete",
                                $"Are you sure you want to delete '{interaction.Input}'?",
                                "YES",
                                "NO");

                            interaction.SetOutput(deleteIt);
                        }));
            });
    }
}

Вы можете просто сосредоточиться на взаимодействиях.

Я бы также рекомендовал ReactiveCommand, чтобы избежать проблем с кнопками и асинхронными действиями.

0 голосов
/ 18 января 2019

Спасибо за ваши комментарии, они указали мне правильное направление. Я посмотрел на реализацию System.Progress<T> и, думаю, начну оттуда.

System.Progress<T> захватывает SynchronizationContext, а Report(T value) вызывает асинхронный метод Post() контекста синхронизации.

Поскольку мне нужна обратная связь от моего вызова, я вместо этого остановлюсь на синхронном методе Send() и предположительно основываю его на новом интерфейсе, ICallbackQuery<T, TResult> или чем-то подобном, смоделированном после IProgress<T> и Progress<T>

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