反应式扩展等待事件第一次不起作用



我正在尝试创建一个方法,使用WebBrowser控件导航到网页并阻止执行,直到页面完全加载。

不幸的是,知道页面是否已完全加载并不是那么简单,而是基于事件NavigatingDocumentCompleted

它们一起组成一对:当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上,直到NavigatingDocumentCompleted再次激发(即,我转到另一个URL或同一个URL)。

我做了一个快速项目来测试这一点,事件NavigatingDocumentCompleted如预期一样启动,下面是我的完整代码:

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

顺便说一句,正是因为这个原因,异步代码中非常常见的模式是开始等待事件完成,然后启动它,所以这是一个很好的记住模式。

最新更新