浏览器行为问题

本文关键字:问题 浏览器 | 更新日期: 2023-09-27 18:29:57

我正在尝试用.NET C#自动化Webbrowser。问题是控件或者我应该说IE浏览器在不同的计算机上表现得很奇怪。例如,我在第一台计算机上点击链接并填写Ajax弹出表单,没有任何错误:

private void btn_Start_Click(object sender, RoutedEventArgs e)
{
    webbrowserIE.Navigate("http://www.test.com/");
    webbrowserIE.DocumentCompleted += fillup_LoadCompleted; 
}
void fillup_LoadCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
    System.Windows.Forms.HtmlElement ele = web_BrowserIE.Document.GetElementById("login");
    if (ele != null)
        ele.InvokeMember("Click");
    if (this.web_BrowserIE.ReadyState == System.Windows.Forms.WebBrowserReadyState.Complete)
    {
        web_BrowserIE.Document.GetElementById("login").SetAttribute("value", myUserName);
        web_BrowserIE.Document.GetElementById("password").SetAttribute("value", myPassword);
        foreach (System.Windows.Forms.HtmlElement el in web_BrowserIE.Document.GetElementsByTagName("button"))
        {
            if (el.InnerText == "Login")
            {
                el.InvokeMember("click");
            }
        }
        web_BrowserIE.DocumentCompleted -= fillup_LoadCompleted;        
    }
}

然而,上面的代码不会在第二台电脑上工作,唯一的点击方式是这样的:

private void btn_Start_Click(object sender, RoutedEventArgs e)
{
    webbrowserIE.DocumentCompleted += click_LoadCompleted;
    webbrowserIE.Navigate("http://www.test.com/"); 
}
void click_LoadCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
    if (this.webbrowserIE.ReadyState == System.Windows.Forms.WebBrowserReadyState.Complete)
    {
        System.Windows.Forms.HtmlElement ele = webbrowserIE.Document.GetElementById("login");
        if (ele != null)
            ele.InvokeMember("Click");
        webbrowserIE.DocumentCompleted -= click_LoadCompleted;
        webbrowserIE.DocumentCompleted += fillup_LoadCompleted;
    }
}
void click_LoadCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
        webbrowserIE.Document.GetElementById("login_login").SetAttribute("value", myUserName);
        webbrowserIE.Document.GetElementById("login_password").SetAttribute("value", myPassword);
        //If you know the ID of the form you would like to submit:
        foreach (System.Windows.Forms.HtmlElement el in webbrowserIE.Document.GetElementsByTagName("button"))
        {
            if (el.InnerText == "Login")
            {
                el.InvokeMember("click");
            }
        }
        webbrowserIE.DocumentCompleted -= click_LoadCompleted;      
}

所以,在第二个解决方案中,我必须调用两个Load Completed Chain。有人能建议我如何处理这个问题吗?此外,提出一个更有力的方法将非常有益。提前感谢

浏览器行为问题

我可以推荐两件事:

  • 不要在DocumentComplete事件上执行代码,而是在DOM window.onload事件上执行
  • 要确保您的网页在WebBrowser控件中的行为与在完整的Internet Explorer浏览器中的行为相同,请考虑实现功能控制

[EDITED]根据您的代码结构,还有一个建议。显然,您执行了一系列导航/句柄DocumentComplete操作。使用async/await可能更自然、更容易。以下是一个这样做的示例,无论是否使用async/await。它还说明了如何处理onload

async Task DoNavigationAsync()
{
    bool documentComplete = false;
    TaskCompletionSource<bool> onloadTcs = null;
    WebBrowserDocumentCompletedEventHandler handler = delegate 
    {
        if (documentComplete)
            return; // attach to onload only once per each Document
        documentComplete = true;
        // now subscribe to DOM onload event
        this.wb.Document.Window.AttachEventHandler("onload", delegate
        {
            // each navigation has its own TaskCompletionSource
            if (onloadTcs.Task.IsCompleted)
                return; // this should not be happening
            // signal the completion of the page loading
            onloadTcs.SetResult(true);
        });
    };
    // register DocumentCompleted handler
    this.wb.DocumentCompleted += handler;
    // Navigate to http://www.example.com?i=1
    documentComplete = false;
    onloadTcs = new TaskCompletionSource<bool>();
    this.wb.Navigate("http://www.example.com?i=1");
    await onloadTcs.Task;
    // the document has been fully loaded, you can access DOM here
    MessageBox.Show(this.wb.Document.Url.ToString());
    // Navigate to http://example.com?i=2
    // could do the click() simulation instead
    documentComplete = false;
    onloadTcs = new TaskCompletionSource<bool>(); // new task for new navigation
    this.wb.Navigate("http://example.com?i=2");
    await onloadTcs.Task;
    // the document has been fully loaded, you can access DOM here
    MessageBox.Show(this.wb.Document.Url.ToString());
    // no more navigation, de-register DocumentCompleted handler
    this.wb.DocumentCompleted -= handler;
}

以下是没有async/await模式的相同代码(适用于.NET 4.0):

Task DoNavigationAsync()
{
    // save the correct continuation context for Task.ContinueWith
    var continueContext = TaskScheduler.FromCurrentSynchronizationContext(); 
    bool documentComplete = false;
    TaskCompletionSource<bool> onloadTcs = null;
    WebBrowserDocumentCompletedEventHandler handler = delegate 
    {
        if (documentComplete)
            return; // attach to onload only once per each Document
        documentComplete = true;
        // now subscribe to DOM onload event
        this.wb.Document.Window.AttachEventHandler("onload", delegate
        {
            // each navigation has its own TaskCompletionSource
            if (onloadTcs.Task.IsCompleted)
                return; // this should not be happening
            // signal the completion of the page loading
            onloadTcs.SetResult(true);
        });
    };
    // register DocumentCompleted handler
    this.wb.DocumentCompleted += handler;
    // Navigate to http://www.example.com?i=1
    documentComplete = false;
    onloadTcs = new TaskCompletionSource<bool>();
    this.wb.Navigate("http://www.example.com?i=1");
    return onloadTcs.Task.ContinueWith(delegate 
    {
        // the document has been fully loaded, you can access DOM here
        MessageBox.Show(this.wb.Document.Url.ToString());
        // Navigate to http://example.com?i=2
        // could do the 'click()' simulation instead
        documentComplete = false;
        onloadTcs = new TaskCompletionSource<bool>(); // new task for new navigation
        this.wb.Navigate("http://example.com?i=2");
        onloadTcs.Task.ContinueWith(delegate 
        {
            // the document has been fully loaded, you can access DOM here
            MessageBox.Show(this.wb.Document.Url.ToString());
            // no more navigation, de-register DocumentCompleted handler
            this.wb.DocumentCompleted -= handler;
        }, continueContext);
    }, continueContext);
}

注意,在这两种情况下,它仍然是一段返回Task对象的异步代码。下面是一个如何处理完成此类任务的示例:

private void Form1_Load(object sender, EventArgs e)
{
    DoNavigationAsync().ContinueWith(_ => {
        MessageBox.Show("Navigation complete!");
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

在这里使用TAP模式的好处是DoNavigationAsync是一个自包含的、独立的方法。它可以重用,并且不会干扰父对象(在本例中为主窗体)的状态。