C# 如何等待事件单击
我正在等待一个按钮按下时触发的回调。重要的一点是,我想等待从一个简单的C# 如何等待事件单击,c#,async-await,asynccallback,C#,Async Await,Asynccallback,我正在等待一个按钮按下时触发的回调。重要的一点是,我想等待从一个简单的await回调,而不需要重塑代码。换句话说,我希望实现以下目标: internal async Task BatchLogic() { ProgressMessage = "Batch Logic Starts"; await OnCallbackFired(); ProgressMessage = "Batch Logic Ends"; } 我的尝试是 internal async Task Bat
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
是从回调调用的。Invoke
但工厂.fromsync
从未返回,很可能是因为我不了解如何使用它,也没有找到与我试图实现的目标相对应的示例。到等待什么,它需要是一个任务(这是一个过于简单化的问题,但这是重要的部分)(C#可以等待任何暴露任务等待器GetAwaiter()方法的对象,但是95%的时间C#开发人员将花费任务/任务对象)
要创建任务
,请使用TaskCompletionSource
。这就是如何围绕其他概念性“任务”(如IAsyncResult
样式的API、线程、重叠IO等)创建Task
表示的方法
假设这是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";
}
}
类MyForm:Form
{
//请注意,`TaskCompletionSource`必须具有泛型类型参数。
//如果您不关心结果,则使用一个伪空'Object'或1字节'Boolean'值。
私有任务完成源tcs;
私人只读按钮;
公共MyForm()
{
this.button=new button(){Text=“Click me”};
this.Controls.Add(this.button);
this.button.Click+=this.onbutton单击;
}
私有void OnButtonClicked(对象发送方,事件参数e)
{
if(this.tcs==null)返回;
此.tcs.SetResult((按钮)发送器);
}
内部异步任务BatchLogicAsync()
{
this.tcs=新任务完成源();
ProgressMessage=“批处理逻辑启动”;
按钮clickedButton=等待this.tcs.Task;
ProgressMessage=“批处理逻辑结束”;
}
}
这是对我的初始代码的修复,使其按预期工作。
我必须为回调定义一个闭包
AsyncCallback callback;
然后我必须将回调
从beginMethod
传递给闭包:
private IAsyncResult beginMethod(AsyncCallback callback, object state)
{
this.callback = callback;
return Task.FromResult(true);
}
最后从事件中调用它(即WPF的MVVM中的命令方法)
下面是一个可以使用的扩展方法。取消订阅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);
}
}
这是WPF、WinForms、UWP、WebForms还是其他什么?@Giulio是-MVVM框架以某种方式使用命令,而不是按钮事件。有些,如Caliburn.Micro,直接绑定到动作。WinForms和WPF都允许您编写异步事件处理程序,但本问题中的任何方法都没有事件处理程序签名。事实上,没有任何事件显示在code@Giulio在哪个框架内?问题不应该是“如何在FrameworkX中创建异步命令”?同样,这方面没有任何事件code@GiulioTaskFactory
仅适用于使用现有的基于IAsyncResult
的API时。您的代码没有做到这一点(您的代码似乎自己实现了IAsyncResult
,这是问题的另一端)。
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();