Как ждать события клик - PullRequest
       31

Как ждать события клик

1 голос
/ 11 октября 2019

Я пытаюсь дождаться обратного вызова, который срабатывает при нажатии кнопки. Важным моментом является то, что я хочу дождаться обратного вызова от простого await без изменения кода. Другими словами, я хочу добиться следующего:

internal async Task BatchLogic()
{
    ProgressMessage = "Batch Logic Starts";
    await OnCallbackFired();
    ProgressMessage = "Batch Logic Ends";
}

моя попытка

internal async Task BatchLogic()
{
    ProgressMessage = "Batch Logic Starts";
    await Task.Factory.FromAsync(beginMethod, endMethod, state);
    ProgressMessage = "Batch Logic Ends";
}

со следующими определениями

 private object state = null;
 private void endMethod(IAsyncResult obj)
 {
     IsBusy = false; 
 }
 private AsyncCallback callback;
 private IAsyncResult beginMethod(AsyncCallback callback, object state)
 {
     return Task.FromResult(true);
 }

Когда кнопка нажата,выполняется следующий код:

private async void RunNext()
{
    isBusy = true;
    await workToDo();
    isBusy = false; // the first 3 lines are not relevant
    callback = new AsyncCallback(endMethod);
    callback.Invoke(Task.FromResult(true));
}

Проблема в том, что endMethod вызывается из callback.Invoke, но Factory.FromAsync никогда не возвращается, скорее всего, потому что я не понял, как его использоватьи я не нашел пример, соответствующий тому, чего я пытаюсь достичь.

Ответы [ 3 ]

1 голос
/ 11 октября 2019

Для await чего-то, это должно быть Task (это упрощение, но это важная часть) (C # может await любой объект, который предоставляет метод TaskAwaiter GetAwaiter(), но 95% времени разработчики C # будут потреблять Task / Task<T> объектов). .

Для создания Task вы используете TaskCompletionSource. Вот как вы можете создать представление Task вокруг других концептуальных «задач», таких как API-интерфейсы в стиле IAsyncResult, потоки, перекрывающийся ввод-вывод и т. Д.

Предполагая, что это WinForms, сделайте это:

class MyForm : Form
{
    // Note that `TaskCompletionSource` must have a generic-type argument.
    // If you don't care about the result then use a dummy null `Object` or 1-byte `Boolean` value.
    private TaskCompletionSource<Button> tcs;

    private readonly Button button;

    public MyForm()
    {
        this.button = new Button() { Text = "Click me" };
        this.Controls.Add( this.button );
        this.button.Click += this.OnButtonClicked;
    }

    private void OnButtonClicked( Object sender, EventArgs e )
    {
        if( this.tcs == null ) return;

        this.tcs.SetResult( (Button)sender );
    }

    internal async Task BatchLogicAsync()
    {
        this.tcs = new TaskCompletionSource<Button>();

        ProgressMessage = "Batch Logic Starts";

        Button clickedButton = await this.tcs.Task;

        ProgressMessage = "Batch Logic Ends";
    }
}
0 голосов
/ 11 октября 2019

Вот метод расширения, который вы можете использовать. Отказ от подписки на событие Click важен во избежание утечек памяти, поскольку издатели событий сохраняют ссылки на подписчиков событий.

public static Task OnClickAsync(this Control source)
{
    var tcs = new TaskCompletionSource<bool>();
    source.Click += OnClick;
    return tcs.Task;

    void OnClick(object sender, EventArgs e)
    {
        source.Click -= OnClick;
        tcs.SetResult(true);
    }
}

Пример использования:

await Button1.OnClickAsync();
0 голосов
/ 11 октября 2019

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

AsyncCallback callback;

Затем я должен был передать callback закрытию из beginMethod:

private IAsyncResult beginMethod(AsyncCallback callback, object state)
{
    this.callback = callback;
    return Task.FromResult(true);
}

и, наконец, вызвать его изсобытие (т. е. метод Command в MVVM для WPF)

private async void RunNext()
{
    IsBusy = true;
    ProgressMessage = "Wait 10 seconds...";
    await workToDo();
    ProgressMessage = "Work done!";
    IsBusy = false;
    if (callback != null)
    {
        callback.Invoke(Task.FromResult(true));
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...