C# 从WebBrowser控件获取ReadyState而不使用DoEvents

C# 从WebBrowser控件获取ReadyState而不使用DoEvents,c#,.net,webbrowser-control,readystate,doevents,C#,.net,Webbrowser Control,Readystate,Doevents,这已经在这里和其他站点及其工作中多次使用,但我希望通过其他方式: 在使用导航或post后获取ReadyState=Complete,由于其所有缺点,不使用DoEvents 我还要指出,在这里使用DocumentComplete事件没有帮助,因为我不会只浏览一个页面,而是像这样一个接一个地浏览 wb.navigate("www.microsoft.com") //dont use DoEvents loop here wb.Document.Body.SetAttribute(textbox1,

这已经在这里和其他站点及其工作中多次使用,但我希望通过其他方式:

在使用导航或post后获取ReadyState=Complete,由于其所有缺点,不使用DoEvents

我还要指出,在这里使用DocumentComplete事件没有帮助,因为我不会只浏览一个页面,而是像这样一个接一个地浏览

wb.navigate("www.microsoft.com")
//dont use DoEvents loop here
wb.Document.Body.SetAttribute(textbox1, "login")
//dont use DoEvents loop here
if (wb.documenttext.contais("text"))
//do something
它现在的工作方式是使用DoEvents。我想知道是否有人有一个合适的方法来等待浏览器方法的异步调用,然后继续进行其余的逻辑。只是为了它

提前感谢。

这里有一个“快速脏”的解决方案。它不是100%万无一失的,但它不会阻止UI线程,而且应该能够满足WebBrowser控制自动化程序原型的要求:

    private async void testButton_Click(object sender, EventArgs e)
    {
        await Task.Factory.StartNew(
            () =>
            {
                stepTheWeb(() => wb.Navigate("www.yahoo.com"));
                stepTheWeb(() => wb.Navigate("www.microsoft.com"));
                stepTheWeb(() => wb.Navigate("asp.net"));
                stepTheWeb(() => wb.Document.InvokeScript("eval", new[] { "$('p').css('background-color','yellow')" }));
                bool testFlag = false;
                stepTheWeb(() => testFlag = wb.DocumentText.Contains("Get Started"));
                if (testFlag) {    /* TODO */ }
                // ... 
            }
        );
    }

    private void stepTheWeb(Action task)
    {
        this.Invoke(new Action(task));

        WebBrowserReadyState rs = WebBrowserReadyState.Interactive;
        while (rs != WebBrowserReadyState.Complete)
        {
            this.Invoke(new Action(() => rs = wb.ReadyState));
            System.Threading.Thread.Sleep(300);
        }
   }
下面是一个更通用的
testButton\u点击
方法:

    private async void testButton_Click(object sender, EventArgs e)
    {
        var actions = new List<Action>()
            {
                () => wb.Navigate("www.yahoo.com"),
                () => wb.Navigate("www.microsoft.com"),
                () => wb.Navigate("asp.net"),
                () => wb.Document.InvokeScript("eval", new[] { "$('p').css('background-color','yellow')" }),
                () => {
                         bool testFlag = false;
                         testFlag  = wb.DocumentText.Contains("Get Started"); 
                         if (testFlag)  {   /*  TODO */  }
                       }
                //... 
            };

        await Task.Factory.StartNew(() => actions.ForEach((x)=> stepTheWeb (x)));  
    }

下面是一个基本的WinForms应用程序代码,说明了如何使用
async/await
异步等待
DocumentCompleted
事件。它一个接一个地导航到多个页面。一切都发生在主UI线程上

与其调用
this.webBrowser.Navigate(url)
,不如模拟表单按钮单击,以触发帖子样式的导航

webBrowser.IsBusy
异步循环逻辑是可选的,其目的是(非确定性地)说明页面的动态AJAX代码,该代码可能发生在
window.onload
事件之后

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WebBrowserApp
{
    public partial class MainForm : Form
    {
        WebBrowser webBrowser;

        public MainForm()
        {
            InitializeComponent();

            // create a WebBrowser
            this.webBrowser = new WebBrowser();
            this.webBrowser.Dock = DockStyle.Fill;
            this.Controls.Add(this.webBrowser);

            this.Load += MainForm_Load;
        }

        // Form Load event handler
        async void MainForm_Load(object sender, EventArgs e)
        {
            // cancel the whole operation in 30 sec
            var cts = new CancellationTokenSource(30000);

            var urls = new String[] { 
                    "http://www.example.com", 
                    "http://www.gnu.org", 
                    "http://www.debian.org" };

            await NavigateInLoopAsync(urls, cts.Token);
        }

        // navigate to each URL in a loop
        async Task NavigateInLoopAsync(string[] urls, CancellationToken ct)
        {
            foreach (var url in urls)
            {
                ct.ThrowIfCancellationRequested();
                var html = await NavigateAsync(ct, () => 
                    this.webBrowser.Navigate(url));
                Debug.Print("url: {0}, html: \n{1}", url, html);
            }
        }

        // asynchronous navigation
        async Task<string> NavigateAsync(CancellationToken ct, Action startNavigation)
        {
            var onloadTcs = new TaskCompletionSource<bool>();
            EventHandler onloadEventHandler = null;

            WebBrowserDocumentCompletedEventHandler documentCompletedHandler = delegate
            {
                // DocumentCompleted may be called several time for the same page,
                // if the page has frames
                if (onloadEventHandler != null)
                    return;

                // so, observe DOM onload event to make sure the document is fully loaded
                onloadEventHandler = (s, e) =>
                    onloadTcs.TrySetResult(true);
                this.webBrowser.Document.Window.AttachEventHandler("onload", onloadEventHandler);
            };

            this.webBrowser.DocumentCompleted += documentCompletedHandler;
            try
            {
                using (ct.Register(() => onloadTcs.TrySetCanceled(), useSynchronizationContext: true))
                {
                    startNavigation();
                    // wait for DOM onload event, throw if cancelled
                    await onloadTcs.Task;
                }
            }
            finally
            {
                this.webBrowser.DocumentCompleted -= documentCompletedHandler;
                if (onloadEventHandler != null)
                    this.webBrowser.Document.Window.DetachEventHandler("onload", onloadEventHandler);
            }

            // the page has fully loaded by now

            // optional: let the page run its dynamic AJAX code,
            // we might add another timeout for this loop
            do { await Task.Delay(500, ct); }
            while (this.webBrowser.IsBusy);

            // return the page's HTML content
            return this.webBrowser.Document.GetElementsByTagName("html")[0].OuterHtml;
        }
    }
}
使用系统;
使用系统诊断;
使用系统线程;
使用System.Threading.Tasks;
使用System.Windows.Forms;
命名空间WebBrowserApp
{
公共部分类主窗体:窗体
{
网络浏览器;
公共表格(
{
初始化组件();
//创建网络浏览器
this.webBrowser=新的webBrowser();
this.webBrowser.Dock=DockStyle.Fill;
this.Controls.Add(this.webBrowser);
此.Load+=MainForm_Load;
}
//表单加载事件处理程序
异步void MainForm_加载(对象发送方,事件参数e)
{
//在30秒内取消整个操作
var cts=新的CancellationTokenSource(30000);
var url=新字符串[]{
"http://www.example.com", 
"http://www.gnu.org", 
"http://www.debian.org" };
等待NavigateInLopAsync(URL、cts.Token);
}
//导航到循环中的每个URL
异步任务NavigateInLopAsync(字符串[]URL,CancellationToken ct)
{
foreach(url中的变量url)
{
ct.ThrowIfCancellationRequested();
var html=await NavigateAsync(ct,()=>
this.webBrowser.Navigate(url));
Print(“url:{0},html:\n{1}”,url,html);
}
}
//异步导航
异步任务NavigateAsync(取消令牌ct,操作开始导航)
{
var onload tcs=new TaskCompletionSource();
EventHandler onloadEventHandler=null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler=委托
{
//对于同一页,DocumentCompleted可以多次调用,
//如果页面有框架
if(onloadEventHandler!=null)
返回;
//因此,观察DOM onload事件以确保文档已完全加载
onloadEventHandler=(s,e)=>
onload tcs.TrySetResult(真);
this.webBrowser.Document.Window.AttachEventHandler(“onload”,onloadEventHandler);
};
this.webBrowser.DocumentCompleted+=documentCompletedHandler;
尝试
{
使用(ct.Register(()=>onload tcs.trysetconceled(),useSynchronizationContext:true))
{
startNavigation();
//等待DOM onload事件,如果取消则抛出
等待onload任务;
}
}
最后
{
this.webBrowser.DocumentCompleted-=documentCompletedHandler;
if(onloadEventHandler!=null)
this.webBrowser.Document.Window.DetachEventHandler(“onload”,onloadEventHandler);
}
//现在页面已完全加载
//可选:让页面运行其动态AJAX代码,
//我们可能会为此循环添加另一个超时
不要{等待任务。延迟(500,ct);}
而(这个.webBrowser.IsBusy);
//返回页面的HTML内容
返回此.webBrowser.Document.GetElementsByTagName(“html”)[0].OuterHtml;
}
}
}

如果您希望从控制台应用程序中执行类似的操作,请参见。

解决方案很简单:

    // MAKE SURE ReadyState = Complete
            while (WebBrowser1.ReadyState.ToString() != "Complete") {
                Application.DoEvents();         
            }
//转到您的子序列代码


又脏又快。。我是一个VBA的家伙,这种逻辑一直在发挥作用,只是花了我几天的时间,却找不到适合C#的,但我自己刚刚发现了这一点

以下是我的完整功能,目标是从网页中获取一段信息:

private int maxReloadAttempt = 3;
    private int currentAttempt = 1;

    private string GetCarrier(string webAddress)
    {
        WebBrowser WebBrowser_4MobileCarrier = new WebBrowser();
        string innerHtml;
        string strStartSearchFor = "subtitle block pull-left\">";
        string strEndSearchFor = "<";

        try
        {
            WebBrowser_4MobileCarrier.ScriptErrorsSuppressed = true;
            WebBrowser_4MobileCarrier.Navigate(webAddress); 

            // MAKE SURE ReadyState = Complete
            while (WebBrowser_4MobileCarrier.ReadyState.ToString() != "Complete") {
                Application.DoEvents();         
            }

            // LOAD HTML
            innerHtml = WebBrowser_4MobileCarrier.Document.Body.InnerHtml;  

            // ATTEMPT (x3) TO EXTRACT CARRIER STRING
            while (currentAttempt <=  maxReloadAttempt) {
                if (innerHtml.IndexOf(strStartSearchFor) >= 0)
                {
                    currentAttempt = 1; // Reset attempt counter
                    return Sub_String(innerHtml, strStartSearchFor, strEndSearchFor, "0"); // Method: "Sub_String" is my custom function
                }
                else
                {
                    currentAttempt += 1;    // Increment attempt counter
                    GetCarrier(webAddress); // Recursive method call
                } // End if
            } // End while
        }   // End Try

        catch //(Exception ex)
        {
        }
        return "Unavailable";
    }
private int maxreloadtright=3;
private int currenttrunt=1;
私有字符串GetCarrier(字符串Web地址)
{
WebBrowser WebBrowser_4MobileCarrier=新WebBrowser();
字符串innerHtml;
字符串strStartSearchFor=“subtitle block pull left\”>”;
string Strength SearchFor=“您必须使用DocumentCompleted事件。您需要做的只是跟踪已完成的内容。事件已经告诉您,您将返回e.Url属性。如果您需要了解更多信息,则只需使用跟踪状态的变量。简单的整数或枚举即可
private int maxReloadAttempt = 3;
    private int currentAttempt = 1;

    private string GetCarrier(string webAddress)
    {
        WebBrowser WebBrowser_4MobileCarrier = new WebBrowser();
        string innerHtml;
        string strStartSearchFor = "subtitle block pull-left\">";
        string strEndSearchFor = "<";

        try
        {
            WebBrowser_4MobileCarrier.ScriptErrorsSuppressed = true;
            WebBrowser_4MobileCarrier.Navigate(webAddress); 

            // MAKE SURE ReadyState = Complete
            while (WebBrowser_4MobileCarrier.ReadyState.ToString() != "Complete") {
                Application.DoEvents();         
            }

            // LOAD HTML
            innerHtml = WebBrowser_4MobileCarrier.Document.Body.InnerHtml;  

            // ATTEMPT (x3) TO EXTRACT CARRIER STRING
            while (currentAttempt <=  maxReloadAttempt) {
                if (innerHtml.IndexOf(strStartSearchFor) >= 0)
                {
                    currentAttempt = 1; // Reset attempt counter
                    return Sub_String(innerHtml, strStartSearchFor, strEndSearchFor, "0"); // Method: "Sub_String" is my custom function
                }
                else
                {
                    currentAttempt += 1;    // Increment attempt counter
                    GetCarrier(webAddress); // Recursive method call
                } // End if
            } // End while
        }   // End Try

        catch //(Exception ex)
        {
        }
        return "Unavailable";
    }