我正在尝试创建一个方法,使用WebBrowser
控件导航到网页并阻止执行,直到页面完全加载。
不幸的是,知道页面是否已完全加载并不是那么简单,而是基于事件Navigating
和DocumentCompleted
。
它们一起组成一对:当Navigating
激发时,DocumentCompleted
将始终在那之后激发。
不幸的是,DocumentCompleted
对加载的每一帧都会触发,因此它可能在每页上触发多次,这意味着看到以下序列并不罕见:Navigating
-DocumentCompleted
-Navigating
-DocumentCompleted
。
因此,我将"页面已完全加载"定义为:如果DocumentCompleted
已启动,并且一秒钟后Navigating
没有再次启动。
无论如何,我有以下作为我的Rx代码来尝试做到以上几点:
_pageLoaded = navigating.Select(_ => documentCompleted.Delay(TimeSpan.FromSeconds(1))).Switch().FirstAsync();
然后在继续之前,我在其他地方做了一个await _pageLoaded
。
然而,出于某种奇怪的原因,这第一次并不奏效。因此,当应用程序加载并且WebBrowser
导航到某个位置时,它会卡在await _pageLoaded
上,直到Navigating
和DocumentCompleted
再次激发(即,我转到另一个URL或同一个URL)。
我做了一个快速项目来测试这一点,事件Navigating
和DocumentCompleted
如预期一样启动,下面是我的完整代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private IObservable<object> _pageLoaded;
private void Form1_Load(object sender, EventArgs e)
{
var navigating = Observable.FromEventPattern(webBrowser, "Navigating");
var documentCompleted = Observable.FromEventPattern(webBrowser, "DocumentCompleted");
_pageLoaded = navigating.Select(_ => documentCompleted.Delay(TimeSpan.FromSeconds(1))).Switch().FirstAsync();
}
private async void button1_Click(object sender, EventArgs e)
{
webBrowser.Navigate(textBox1.Text);
await _pageLoaded;
MessageBox.Show("DoneLoading");
}
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
MessageBox.Show("DocumentCompleted fired");
}
private void webBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
MessageBox.Show("Navigating fired");
}
}
当有人在TextBox
(我用google.com测试过)中输入URL并点击Button
时,结果是:
- 导航已启动
- DocumentCompleted已解雇
- 。。。什么都没有
如果在WebBrowser
上我点击一个链接,比如顶部的图像,就会发生以下情况:
- 导航已启动
- DocumentCompleted已解雇
- 1秒后。。。DoneLoading
如果我再次点击Button
,那么会发生什么:
- 导航已启动
- DocumentCompleted已解雇
- 1秒后。。。DoneLoading
- 很明显,如果我以前没有点击过页面中的任何链接,那么我会得到第二个DoneLoading
MessageBox
所以,我的问题很简单:什么是第一次不起作用,我该如何解决?
编辑:我刚刚在另一个网站上做了一个测试,在这个网站上事件多次触发,它在这些网站上运行良好。因此,从字面上讲,它只会在网站上触发一次,而且似乎只是第一次出现问题。
问题是,在调用Navigate之后,您正在等待,因此,根据Navigate事件是否同步引发,Rx可能永远看不到第一个导航事件,或者这是一场比赛。
试试这个:
private async void button1_Click_1(object sender, EventArgs e)
{
var waiter = _pageLoaded.GetAwaiter();
webBrowser.Navigate(textBox1.Text);
await waiter;
MessageBox.Show("DoneLoading");
}
顺便说一句,正是因为这个原因,异步代码中非常常见的模式是开始等待事件完成,然后启动它,所以这是一个很好的记住模式。