我使用.net 4.0,我试图弄清楚如何使用异步方法来等待DocumentCompleted事件完成并返回值。我的原始代码在上面,如何在这种情况下将其转换为异步/等待模型?
private class BrowserWindow
{
private bool webBrowserReady = false;
public string content = "";
public void Navigate(string url)
{
xxx browser = new xxx();
browser.DocumentCompleted += new EventHandler(wb_DocumentCompleted);
webBrowserReady = false;
browser.CreateControl();
if (browser.IsHandleCreated)
browser.Navigate(url);
while (!webBrowserReady)
{
//Application.DoEvents(); >> replace it with async/await
}
}
private void wb_DocumentCompleted(object sender, EventArgs e)
{
try
{
...
webBrowserReady = true;
content = browser.Document.Body.InnerHtml;
}
catch
{
}
}
public delegate string AsyncMethodCaller(string url);
}
答案 0 :(得分:10)
因此我们需要一个在DocumentCompleted
事件触发时返回任务的方法。无论何时您需要为给定的事件,您都可以创建一个这样的方法:
public static Task WhenDocumentCompleted(this WebBrowser browser)
{
var tcs = new TaskCompletionSource<bool>();
browser.DocumentCompleted += (s, args) => tcs.SetResult(true);
return tcs.Task;
}
一旦你有了,你可以使用:
await browser.WhenDocumentCompleted();
答案 1 :(得分:1)
@Servy具有我一直在寻找的天才答案,但这不适用于我的用例。由于事件处理程序尝试在后续事件调用的TaskCompletionSource
上设置结果,因此多次引发事件时,我发现了错误。
我通过两种方式增强了他的答案。第一种方法是在第一次处理DocumentCompleted
事件后就取消订阅。
public static Task WhenDocumentCompleted(this WebBrowser browser)
{
var tcs = new TaskCompletionSource<bool>();
browser.DocumentCompleted += DocumentCompletedHandler;
return tcs.Task;
void DocumentCompletedHandler(object sender, EventArgs e)
{
browser.DocumentCompleted -= DocumentCompletedHandler;
tcs.SetResult(true);
}
}
请注意,我在这里使用局部函数来捕获
TaskCompletionSource
实例,该实例至少需要C#7.0。
第二个增强功能是添加超时。我的特定用例是在单元测试中,我想确定性地等待特定事件,而不是无限期地等待,如果有问题。
我选择为此使用计时器,并将其设置为仅触发一次,然后在不再需要时停止计时器。另外,也可以在这里利用CancellationToken
来管理TaskCompletionSource
,但是我认为这需要维护者更多地了解其用法,并且计时器也得到了更广泛的了解。
public static Task WhenDocumentCompleted(this WebBrowser browser, int timeoutInMilliseconds = 500)
{
var tcs = new TaskCompletionSource<bool>();
var timeoutTimer = new System.Timers.Timer(timeoutInMilliseconds);
timeoutTimer.AutoReset = false;
timeoutTimer.Elapsed += (s,e) => tcs.TrySetCanceled();
timeoutTimer.Start();
browser.DocumentCompleted += DocumentCompletedHandler;
return tcs.Task;
void DocumentCompletedHandler(object sender, EventArgs e)
{
timeoutTimer.Stop();
browser.DocumentCompleted -= DocumentCompletedHandler;
tcs.TrySetResult(true);
}
}
请注意,以确保代码是线程安全的,在这里我变得更加防御,并使用了
Try...
函数。这样可以确保即使发生边缘案例交错执行时也不会错误地设置结果。