I am trying to create a method to, using the WebBrowser
control, navigate to a webpage and block execution until the page has fully loaded.
Unfortunately knowing if the page has fully loaded or not is not that simple and just based on the events Navigating
and DocumentCompleted
.
Together they make a pair: when Navigating
fires, a DocumentCompleted
will always fire after that.
Unfortunately DocumentCompleted
fires for every frame that has loaded, so it could fire multiple times per page, meaning it's not unusual to see the following sequence: Navigating
- DocumentCompleted
- Navigating
- DocumentCompleted
.
As a result, I've defined "page fully loaded" as the following: if DocumentCompleted
has fired and a second has elapsed without Navigating
firing again during that time.
Anyway, I've got the following as my Rx code to try to do the above:
_pageLoaded = navigating.Select(_ => documentCompleted.Delay(TimeSpan.FromSeconds(1))).Switch().FirstAsync();
Then somewhere else I do an await _pageLoaded
before continuing.
However, for some bizarre reason, this does not work the first time around. So when the application loads up and the WebBrowser
navigates somewhere, it gets stuck on the await _pageLoaded
until both Navigating
and DocumentCompleted
fire again (i.e., I go to another URL - or the same one).
I did a quick project just to test this out and the events Navigating
and DocumentCompleted
fire as expected, here's my full code:
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");
}
}
The result when someone enters a URL into the TextBox
(I tested with google.com) and hits the Button
is:
- Navigating fired
- DocumentCompleted fired
- ... ... nothing
If then on the WebBrowser
I click on a link, say images at the top for example, the following happens:
- Navigating fired
- DocumentCompleted fired
- 1 sec later ... DoneLoading
If I hit the Button
again what then happens is:
- Navigating fired
- DocumentCompleted fired
- 1 sec later ... DoneLoading
- And obviously if I haven't clicked any link within the page before, then I get a second DoneLoading
MessageBox
.
So, my question is simple: what does it not work the first time around and how can I fix it?
EDIT: I just did a test with a different website where the events fire multiple times, and it works fine on those websites. So it's literally websites where it only fires the one time, and it appears to only be an issue the first time around.