35

I'm trying to create a program to clone multiple bugs at a time through the web interface of our defect tracking system. How can I wait before a page is completely loaded before I continue?

//This is pseudo code, but this should give you an idea of what I'm trying to do.  The
//actual code uses multi-threading and all that good stuff :).
foreach (string bug in bugs)
{
    webBrowser.Navigate(new Uri(url));
    webBrowser.Document.GetElementById("product").SetAttribute("value", product);
    webBrowser.Document.GetElementById("version").SetAttribute("value", version);
    webBrowser.Document.GetElementById("commit").InvokeMember("click");

    //Need code to wait for page to load before continuing.
}
McMillan Cheng
  • 382
  • 1
  • 6
  • 20
MD6380
  • 1,007
  • 4
  • 13
  • 26

11 Answers11

34

Try the DocumentCompleted Event:

webBrowser.DocumentCompleted +=
    new WebBrowserDocumentCompletedEventHandler(webBrowser_DocumentCompleted);

void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    webBrowser.Document.GetElementById("product").SetAttribute("value", product);
    webBrowser.Document.GetElementById("version").SetAttribute("value", version);
    webBrowser.Document.GetElementById("commit").InvokeMember("click");
}
EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
Quintin Robinson
  • 81,193
  • 14
  • 123
  • 132
  • 2
    Does this code mean that the web browser will always wait to be completed before executing the commands below? – AdorableVB Apr 26 '17 at 00:16
  • My page bare bone structure loads pretty quick, but I have JS code that loads contents from a DB into different DIVs and that takes couple of seconds. Is there another event that I can trigger off of? – AleX_ Aug 02 '17 at 21:13
33

This code was very helpful for me. Maybe it could be for you also

wb.Navigate(url);
while(wb.ReadyState != WebBrowserReadyState.Complete)
{
     Application.DoEvents();
}
MessageBox.Show("Loaded");
BNL
  • 7,085
  • 4
  • 27
  • 32
  • 2
    Works. But CPU load will spike on that "while" – Alex from Jitbit Apr 19 '12 at 22:53
  • 2
    You could use Start-Sleep -Milliseconds 1000; in the while loop to avoid the CPU spike, but Qintins DocumentCompleted call back is more correct. – 79E09796 May 14 '12 at 09:09
  • 1
    @jitbit- how in this world did you figured out that CPU load will spike? I mean what made you understand this?? – Zameer Ansari Sep 12 '14 at 14:06
  • 1
    WARNİNG this code FAİLS to wait REAL documentCompleted when you use invokeMember("click") then try to wait readystate complete. check my answer below to see when it fails – bh_earth0 Jan 05 '17 at 12:18
23

Best way to do this without blocking the UI thread is to use Async and Await introduced in .net 4.5.
You can paste this in your code just change the Browser to your webbrowser name. This way, your thread awaits the page to load, if it doesnt on time, it stops waiting and your code continues to run:

private async Task PageLoad(int TimeOut)
    {
        TaskCompletionSource<bool> PageLoaded = null;
        PageLoaded = new TaskCompletionSource<bool>();
        int TimeElapsed = 0;
        Browser.DocumentCompleted += (s, e) =>
        {
            if (Browser.ReadyState != WebBrowserReadyState.Complete) return;
            if (PageLoaded.Task.IsCompleted) return; PageLoaded.SetResult(true);
        };
        //
        while (PageLoaded.Task.Status != TaskStatus.RanToCompletion)
        {
            await Task.Delay(10);//interval of 10 ms worked good for me
            TimeElapsed++;
            if (TimeElapsed >= TimeOut * 100) PageLoaded.TrySetResult(true);
        }
    }

And you can use it like this, with in an async method, or in a button click event, just make it async:

private async void Button1_Click(object sender, EventArgs e)
{
   Browser.Navigate("www.example.com");
   await PageLoad(10);//await for page to load, timeout 10 seconds.
   //your code will run after the page loaded or timeout.
}
Biswajit Chopdar
  • 831
  • 5
  • 12
  • 31
YıldızGezen
  • 239
  • 2
  • 5
  • 5
    I believe: "if (Browser.Task.IsCompleted) return; PageLoaded.SetResult(true);" should be "if (PageLoaded.Task.IsCompleted) return; PageLoaded.SetResult(true);" – TH Todorov Feb 06 '17 at 09:08
12

Check out the WatiN project:

Inspired by Watir development of WatiN started in December 2005 to make a similar kind of Web Application Testing possible for the .Net languages. Since then WatiN has grown into an easy to use, feature rich and stable framework. WatiN is developed in C# and aims to bring you an easy way to automate your tests with Internet Explorer and FireFox using .Net...

gnat
  • 6,213
  • 108
  • 53
  • 73
Charlino
  • 15,802
  • 3
  • 58
  • 74
  • 1
    Couldn't agree more. I've been down both roads - roll-your-own WebBrowser control nightmare, and WatiN. The WebBrowser can be made synchronous, but you'll thank yourself later if you let WatiN do the work for you. – Adam Neal Feb 24 '09 at 22:52
8

Task method worked for me, except Browser.Task.IsCompleted has to be changed to PageLoaded.Task.IsCompleted.

Sorry I didnt add comment, that is because I need higher reputation to add comments.

Vlad K
  • 113
  • 1
  • 8
4

yuna and bnl code failed in the case below;

failing example:

1st one waits for completed.but, 2nd one with the invokemember("submit") didnt . invoke works. but ReadyState.Complete acts like its Completed before its REALLY completed:

wb.Navigate(url);
while(wb.ReadyState != WebBrowserReadyState.Complete)
{
   Application.DoEvents();
}
MessageBox.Show("ok this waits Complete");

//navigates to new page
wb.Document.GetElementById("formId").InvokeMember("submit");
while(wb.ReadyState != WebBrowserReadyState.Complete)
{
   Application.DoEvents();
}
MessageBox.Show("webBrowser havent navigated  yet. it gave me previous page's html.");  
var html = wb.Document.GetElementsByTagName("HTML")[0].OuterHtml;

how to fix this unwanted situation:

usage

    public myForm1 {

        myForm1_load() { }

        // func to make browser wait is inside the Extended class More tidy.
        WebBrowserEX wbEX = new WebBrowserEX();

        button1_click(){
            wbEX.Navigate("site1.com");
            wbEX.waitWebBrowserToComplete(wb);

            wbEX.Document.GetElementById("input1").SetAttribute("Value", "hello");
            //submit does navigation
            wbEX.Document.GetElementById("formid").InvokeMember("submit");
            wbEX.waitWebBrowserToComplete(wb);
            // this actually waits for document Compelete. worked for me.

            var processedHtml = wbEX.Document.GetElementsByTagName("HTML")[0].OuterHtml;
            var rawHtml = wbEX.DocumentText;
         }
     }

//put this  extended class in your code.
//(ie right below form class, or seperate cs file doesnt matter)
public class WebBrowserEX : WebBrowser
{
   //ctor
   WebBrowserEX()
   {
     //wired aumatically here. we dont need to worry our sweet brain.
     this.DocumentCompleted += (o, e) => { webbrowserDocumentCompleted = true;};
   }
     //instead of checking  readState, get state from DocumentCompleted Event 
     // via bool value
     bool webbrowserDocumentCompleted = false;
     public void waitWebBrowserToComplete()
     {
       while (!webbrowserDocumentCompleted )
       { Application.DoEvents();  }
       webbrowserDocumentCompleted = false;
     }

 }
bh_earth0
  • 2,537
  • 22
  • 24
2

Have a go at Selenium (http://seleniumhq.org) or WatiN (http://watin.sourceforge.net) to save yourself some work.

PHeiberg
  • 29,411
  • 6
  • 59
  • 81
1
while (true)
        {//ie is the WebBrowser object
            if (ie.ReadyState == tagREADYSTATE.READYSTATE_COMPLETE)
            {
                break;
            }
            Thread.Sleep(500);
        }

I used this way to wait untill the page loads.

Gokul
  • 788
  • 2
  • 12
  • 30
1

If you are using the InternetExplorer.Application COM object, check the ReadyState property for the value of 4.

James Pogran
  • 4,279
  • 2
  • 31
  • 23
1

I think the DocumentCompleted event of the WebBrowser control should get you where you need to go.

Mike
  • 4,542
  • 1
  • 26
  • 29
1

Assuming the "commit" element represents a standard Form submit button then you can attach an event handler to the WebBrowsers Navigated event.

AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306