7

I have a for loop:

for (i = 0; i <= 21; i++)
{
  webB.Navigate(URL);
}

webB is a webBrowser control and i is an int.

I want to wait for the browser to finish navigating.

I found this, however:

  • I don't want to use any APIs or addins
  • I can't use another void function, as suggested in this answer

Is there a way to wait while in a for loop?

Community
  • 1
  • 1
Minicl55
  • 947
  • 5
  • 14
  • 21

7 Answers7

7

Assuming you host WebBrowser in a WinFroms application, you can do it in a loop easily and efficiently, using async/await pattern. Try this:

async Task DoNavigationAsync()
{
    TaskCompletionSource<bool> tcsNavigation = null;
    TaskCompletionSource<bool> tcsDocument = null;

    this.WB.Navigated += (s, e) =>
    {
        if (tcsNavigation.Task.IsCompleted)
            return;
        tcsNavigation.SetResult(true);
    };

    this.WB.DocumentCompleted += (s, e) =>
    {
        if (this.WB.ReadyState != WebBrowserReadyState.Complete)
            return;
        if (tcsDocument.Task.IsCompleted)
            return;
        tcsDocument.SetResult(true); 
    };

    for (var i = 0; i <= 21; i++)
    {
        tcsNavigation = new TaskCompletionSource<bool>();
        tcsDocument = new TaskCompletionSource<bool>();

        this.WB.Navigate("http://www.example.com?i=" + i.ToString());
        await tcsNavigation.Task;
        Debug.Print("Navigated: {0}", this.WB.Document.Url);
        // navigation completed, but the document may still be loading

        await tcsDocument.Task;
        Debug.Print("Loaded: {0}", this.WB.DocumentText);
        // the document has been fully loaded, you can access DOM here
    }
}

Now, it's important to understand that DoNavigationAsync executes asynchronously. Here's how you'd call it from Form_Load and handle the completion of it:

void Form_Load(object sender, EventArgs e)
{
    var task = DoNavigationAsync();
    task.ContinueWith((t) =>
    {
        MessageBox.Show("Navigation done!");
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

I've answered a similar question here.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • can you explain what happens with the task when those **if**s in **DocumentCompleted** handler get hit? Does the **DocumentCompleted** handler get fired multiple times? – lexeme Jun 22 '18 at 16:39
  • @lexeme yes it may get fired multiple times, e.g. for a document with `iframes`. I remember I covered that in more details elsewhere, if interested search for my other answers with `webbrowser-control` tag. – noseratio Jun 22 '18 at 20:44
  • I tried implementing silent printing at my winforms app. I took your other answer (https://stackoverflow.com/questions/19734151/print-webbrowser-without-previewing-i-e-single-click-print/19737374#19737374) as a base. Yet I always got web browser in uninitialized readystate. What might be the cause? – lexeme Jun 26 '18 at 07:47
  • @lexeme, do you mean the sample code from that answer doesn't work for you, as is? – noseratio Jun 26 '18 at 10:46
  • Noseratio, for some reason, yes, it doesn't work as is. I'm trying to print pdf document, maybe there's something to do with ie security settings idk. WebBrowser stays in the uninitialized ready state. – lexeme Jun 26 '18 at 11:08
  • I used the whole `DoWorkAsync()` method as is. – lexeme Jun 26 '18 at 11:24
  • @lexeme, I see, it won't work for PDFs, only for HTML. – noseratio Jun 26 '18 at 12:00
  • Noseratio, what can I do with pdf? I know I can stick to Adobe Reader (we're using it already) process call.But I thought it would be nicer to do this with WebBrowser in some utility class. – lexeme Jun 26 '18 at 12:21
  • @lexeme, I have a very limited experience with hosting PDFs inside WebBrowser, but I don't think it's possible to automate PDF printing that way. You might be better off with [CefSharp](https://cefsharp.github.io/) (I haven't used myself though). – noseratio Jun 26 '18 at 13:16
2

You don't have to use another void function. Simply use a lambda like so:

webB.DocumentCompleted += (sender, e) =>
{
    // your post-load code goes here
};
Jashaszun
  • 9,207
  • 3
  • 29
  • 57
1

The proper way is to use events.
In your loop, how can you know that that the navigation have completed? maybe you are out of the loop but it is only half way through...

Also, looping while waiting is called Busy waiting and is CPU expensive.

In order to be notified when page is ready, and in the meanwhile keep CPU available for other stuff, use events as @Jashaszun suggested:

void YourFunction()
{
  //Do stuff...
  webB.DocumentCompleted += (sender, e) =>
  {
      //Code in here will be triggered when navigation is complete and document is ready
  };
  webB.Navigate(URL);
  //Do more stuff...
}
Community
  • 1
  • 1
Avi Turner
  • 10,234
  • 7
  • 48
  • 75
1

Use this while loop inside the for loop.

while (webB.ReadyState != tagREADYSTATE.READYSTATE_COMPLETE)
            {
               Thread.Sleep(500);
            }

This waits until the WebBrowser loads the page completely.

Gokul
  • 788
  • 2
  • 12
  • 30
0

To wait in a thread you can do something like this

 System.Threading.Thread.Sleep(2000); //waits 2 seconds

Unfortunately, it is not linked up with the navigation finish time.

ismellike
  • 629
  • 1
  • 5
  • 14
0

Try using tasks like this:

for (i = 0; i <= 21; i++)
{
   Task taskA = Task.Factory.StartNew(() => webB.Navigate(URL));
   taskA.Wait();
}

Hope I helped.

FlameBlazer
  • 1,592
  • 2
  • 10
  • 9
0
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        WebBrowser1.Navigate("stackoverflow.com/")

    End Sub

    Private Sub WebBrowser1_DocumentCompleted(ByVal sender As System.Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted

    ------yourcode------

    End Sub

End Class
Bugs
  • 4,491
  • 9
  • 32
  • 41