C#如何等待网页加载完成后再继续执行

35

我正在尝试创建一个程序,通过我们的缺陷跟踪系统的 Web 接口一次性克隆多个漏洞。在继续之前,我该如何等待页面完全加载?

//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.
}

我有相同的问题,但是针对WebView。有什么办法可以等待它加载完成? - yalematta
12个回答

34

这段代码对我非常有帮助。也许它对你也有用。

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

2
可以运行。但是CPU负载会在那个“while”上飙升。 - Alex from Jitbit
2
你可以在while循环中使用Start-Sleep -Milliseconds 1000;来避免CPU峰值,但是Qintins DocumentCompleted回调更为准确。 - 79E09796
1
@jitbit- 你是怎么发现CPU负载会飙升的?是什么让你明白了这一点? - Zameer Ansari
1
警告:当您使用invokeMember(“click”)时,此代码无法等待真正的documentCompleted,然后尝试等待readystate完成。请查看下面的答案以了解何时失败。 - bh_earth0

34

尝试使用DocumentCompleted事件:

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

2
这段代码是否意味着网页浏览器在执行下面的命令之前总是要等待完成? - AdorableVB
我的页面基本结构加载得非常快,但是我有一些JS代码,它会将内容从数据库加载到不同的DIV中,这需要几秒钟的时间。是否有另一个事件可以触发? - AleX_

23

在不阻塞UI线程的情况下执行此操作的最佳方法是使用在.net 4.5中引入的异步和等待Async和Await。
只需将浏览器更改为您的WebBrowser名称即可将此代码粘贴到您的代码中。 这样,您的线程等待页面加载,如果没有及时加载,则停止等待,您的代码继续运行:

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

您可以像这样使用它,在异步方法中或单击按钮事件中,只需使其成为异步:

您可以在异步方法中或按钮点击事件中使用它,只要将其变为异步即可。

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.
}

5
我认为:"if (Browser.Task.IsCompleted) return; PageLoaded.SetResult(true);" 应该改为 "if (PageLoaded.Task.IsCompleted) return; PageLoaded.SetResult(true);". - TH Todorov

12

请查看WatiN项目

受到Watir启发,WatiN的开发始于2005年12月,旨在为.Net语言实现类似的Web应用程序测试。自那时以来,WatiN已经成长为一个易于使用、功能丰富和稳定的框架。WatiN是用C#开发的,旨在为您提供一种简单的方法,使用.Net自动化测试Internet Explorer和FireFox...


1
完全同意。我已经走过这两条路——自行开发WebBrowser控件的噩梦和使用WatiN。WebBrowser可以被设置为同步,但是如果你让WatiN为你完成工作,你以后会感谢自己的选择。 - Adam Neal

8

对于我来说,任务方法很有效,但必须将Browser.Task.IsCompleted更改为PageLoaded.Task.IsCompleted。

很抱歉我没有添加评论,这是因为我需要更高的声望才能添加评论。


4

yuna和bnl在以下情况下代码失败;

失败的示例:

第一个等待完成。但是,第二个使用invokemember("submit")没有等待。invoke可以工作。但是ReadyState.Complete在真正完成之前就像已经完成一样:

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;

如何解决这种不良情况:

用法

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

 }

2

2
有使用 Watin 的完整示例吗? - Kiquenet

1

如果您正在使用InternetExplorer.Application COM对象,请检查ReadyState属性的值是否为4。


1
我认为 WebBrowser 控件的 DocumentCompleted 事件应该能够帮你达到目的。

1
假设"commit"元素代表标准的表单提交按钮,那么您可以将事件处理程序附加到Web浏览器的Navigated事件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接