How to await an event click [duplicate]

帅比萌擦擦* 提交于 2021-01-07 03:42:34

问题


I'm trying to await a callback that is fired when a button is pressed. The important point is that I want to wait for the callback from a simple await without reshaping the code. In other words I want to achieve the following:

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

my attempt is

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

with the following defintions

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

When the button is pressed, the following code is executed:

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

The problem is that the endMethod is called from the callback.Invoke but the Factory.FromAsync never returns, very likely because I've not understood how to use it and I've not found an example corresponding to what I'm trying to achieve.


回答1:


To await something, it needs to be a Task (this is an oversimplification, but that's the important part) (C# can await any object that exposes a TaskAwaiter GetAwaiter() method, but 95% of the time C# developers will be consuming Task/Task<T> objects)..

To create a Task you use TaskCompletionSource. This is how you can create a Task representation around other conceptual "tasks", like the IAsyncResult-style APIs, threads, overlapped IO, and so on.

Assuming this is WinForms, then do this:

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



回答2:


Here is an extension method you can use. Unsubscribing from the Click event is important to avoid memory leaks, because event publishers keep references of event subscribers.

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

Usage example:

await Button1.OnClickAsync();



回答3:


This is the fix for my initial code, that makes it work as expected. I had to define a closure for the callback

AsyncCallback callback;

Then I had to pass the callback to the closure from the beginMethod:

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

and finally invoke it from the event (i.e. the Command method in the MVVM for 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));
    }
}


来源:https://stackoverflow.com/questions/58340162/how-to-await-an-event-click

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!