浏览器行为问题 [英] Webbrowser behaviour issues
问题描述
我正在尝试使用 .NET C# 自动化 Webbrowser.问题是控制或我应该说 IE 浏览器在不同的计算机上表现得很奇怪.例如,我在第一台计算机上点击链接并填写 Ajax 弹出表单,没有任何错误:
I am trying to automate Webbrowser with .NET C#. The issue is that the control or should I say IE browser behaves strange on different computers. For example, I am clickin on link and fillup a Ajax popup form on 1st computer like this, without any error:
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;
}
}
然而,上面的代码在第二台电脑上不起作用,唯一的点击方式是这样的:
However, the above code wont work on 2nd pc and the only way to click is like this:
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;
}
因此,在第二个解决方案中,我必须调用两个加载完成的链.有人可以建议我应该如何处理这个问题吗?此外,提出更稳健的方法将非常有帮助.提前谢谢您
So, in second solution I have to call two Load Completed Chains. Could someone advise on how should I can handle this issue? Also, a proposal for more robust approach would be very helpfull. Thank you in advance
推荐答案
我可以推荐两件事:
- 不要在
DocumentComplete
事件上执行您的代码,而是在 DOM window.onload 事件. - 要确保您的网页在
WebBrowser
控件中的行为方式与在完整 Internet Explorer 浏览器中的行为方式相同,请考虑实施 功能控制.
- Don't execute your code upon
DocumentComplete
event, rather do upon DOM window.onload event. - To make sure your web page behaves in
WebBrowser
control the same way as it would in full Internet Explorer browser, consider implementing Feature Control.
[已编辑]还有一个建议,基于您的代码结构.显然,您执行了一系列导航/处理 DocumentComplete
操作.为此使用 async/await
可能更自然、更容易.这是执行此操作的示例,无论是否使用 async/await
.它也说明了如何处理 onload
:
There's one more suggestion, based on the structure of your code. Apparently, you perform a series of navigation/handle DocumentComplete
actions. It might be more natural and easy to use async/await
for this. Here's an example of doing this, with or without async/await
. It illustrates how to handle onload
, too:
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):
Here's the same code without async/await
pattern (for .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);
}
注意,在这两种情况下,它仍然是一段异步代码,返回一个 任务 对象.下面是如何处理完成此类任务的示例:
Note, it both cases it is still a piece of asynchronous code which returns a Task object. Here's an example of how to handle the completion of such task:
private void Form1_Load(object sender, EventArgs e)
{
DoNavigationAsync().ContinueWith(_ => {
MessageBox.Show("Navigation complete!");
}, TaskScheduler.FromCurrentSynchronizationContext());
}
使用 TAP 模式 的好处是 DoNavigationAsync
是一种自包含的独立方法.它可以重复使用,并且不会干扰父对象(在本例中为主窗体)的状态.
The benefit of using TAP pattern here is that DoNavigationAsync
is a self-contained, independent method. It can be reused and it doesn't interfere with the state of parent object (in this case, the main form).
这篇关于浏览器行为问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!