如何使用另一个文档文本更新文档文本
本文关键字:文档 文本 更新 另一个 何使用 | 更新日期: 2023-09-27 17:56:28
我有一个Windows Form
,它有一个WebBrowser
控件名称formWebBrowser。我正在使用另一个名为 newThreadBrowser 的 WebBrowser 实例创建一个新的非 UI 线程。在新线程中引用 Web 浏览器控件
当 documentcomplete 事件被触发时,我能够使用 C# - 使用非主线程更新 GUI 中提到的方法将 url 写入文本框
现在,我正在尝试从"newThreadBrowser"的html更新"formWebBrowser"的html。它导致异常指出指定的强制转换无效。
在 WebBrowser 控件中:"指定的强制转换无效",接受的答案说 -
WebBrowser是一个底层的COM组件。一个单元线程,COM 负责以线程安全的方式调用其方法。您的 Navigate() 调用正是出于这个原因,它实际上是在 UI 线程上执行的。不起作用的是 DocumentText 属性,它是在 .NET 包装器中实现的,他们有点摸索代码。当 CLR 中的 COM 互操作支持注意到 MTA 中的线程尝试访问驻留在 STA 上的组件的属性时,它会轰炸。
问题
为了在formWebBrowser中呈现newThreadBrowser的html,我应该怎么做?我不确定Control.Invoke()如何解决这个问题。
注意:此应用程序对性能不重要。因此,即使执行需要一些时间也可以。
参考
- 如何更改网络浏览器文档文本?
- 如何从网页中提取信息?
- http://htmlagilitypack.codeplex.com/
来自 WebBrowser.DocumentText Property
如果要使用字符串处理工具操作 Web 浏览器控件中显示的 HTML 页的内容,请使用此属性。例如,可以使用此属性从数据库加载页面或使用正则表达式分析页面。设置此属性时,Web 浏览器控件在加载指定的文本之前自动导航到 about:blank URL。这意味着当您设置此属性时,将发生"导航"、"已导航"和"文档已完成"事件,并且 Url 属性的值不再有意义。
法典
public partial class Form1 : Form
{
public void WriteToTextBoxEvent(object sender, WebBrowserDocumentCompletedEventArgs e)
{
#region Textbox
if (this.textBox1.InvokeRequired)
{
//BeginInvoke is Asynchronus
this.textBox1.BeginInvoke(new Action(() => WriteToTextBoxEvent(sender, e)));
}
else
{
textBox1.Text = e.Url.ToString();
}
#endregion
#region WebBrowser
if (this.formWebBrowser.InvokeRequired)
{
//BeginInvoke is Asynchronus
this.textBox1.BeginInvoke(new Action(() => WriteToTextBoxEvent(sender, e)));
}
else
{
var newThreadBrowser = sender as WebBrowser;
if (sender != null)
{
//The function evaluation requires all threads to run
formWebBrowser.DocumentText = newThreadBrowser.DocumentText;
}
}
#endregion
}
System.Windows.Forms.TextBox textBox1 = new TextBox();
System.Windows.Forms.WebBrowser formWebBrowser = new WebBrowser();
public Form1()
{
WriteLogFunction("App Satrt");
// Web Browser
#region Web Browser
formWebBrowser.Location = new Point(10, 20);
formWebBrowser.Size = new Size(1200, 900);
this.Controls.Add(formWebBrowser);
textBox1.Location = new Point(0, 0);
textBox1.Size = new Size(800, 10);
this.Controls.Add(textBox1);
var th = new Thread(() =>
{
var newThreadBrowser = new WebBrowser();
//To Process the DOM.
newThreadBrowser.DocumentCompleted += browser_DocumentCompleted;
//To update URL textbox
newThreadBrowser.DocumentCompleted += WriteToTextBoxEvent;
newThreadBrowser.ScriptErrorsSuppressed = true;
newThreadBrowser.Navigate(GetHomoePageUrl());
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
#endregion
// Form1
this.Text = "B2B Crawler";
this.Size = new Size(950, 950);
}
List<string> visitedUrls = new List<string>();
List<string> visitedProducts = new List<string>();
private void ExerciseApp(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var wbReceived = sender as WebBrowser;
int catalogElementIterationCounter = 0;
var elementsToConsider = wbReceived.Document.All;
string productUrl = String.Empty;
bool isClicked = false;
foreach (HtmlElement e1 in elementsToConsider)
{
catalogElementIterationCounter++;
string x = e1.TagName;
String idStr = e1.GetAttribute("id");
if (!String.IsNullOrWhiteSpace(idStr))
{
//Each Product Navigation
if (idStr.Contains("catalogEntry_img"))
{
productUrl = e1.GetAttribute("href");
if (!visitedProducts.Contains(productUrl))
{
WriteLogFunction("productUrl -- " + productUrl);
visitedProducts.Add(productUrl);
isClicked = true;
e1.InvokeMember("Click");
//nextNavigationUrl = productUrl;
break;
}
}
}
}
if (visitedProducts.Count == 4)
{
visitedProducts = new List<string>();
isClicked = true;
HomoePageNavigate(wbReceived);
}
if (!isClicked)
{
HomoePageNavigate(wbReceived);
}
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
ExerciseApp(sender, e);
}
private string GetHomoePageUrl()
{
return @"C:'Samples_L'MyTableTest.html";
}
private void HomoePageNavigate(WebBrowser bw)
{
WriteLogFunction("HomoePageNavigate");
bw.Navigate(GetHomoePageUrl());
}
private void WriteLogFunction(string strMessage)
{
using (StreamWriter w = File.AppendText("log.txt"))
{
w.WriteLine("'r'n{0} ..... {1} ", DateTime.Now.ToLongTimeString(), strMessage);
}
}
}
我的表测试.html
<html>
<head>
<style type="text/css">
table {
border: 2px solid blue;
}
td {
border: 1px solid teal;
}
</style>
</head>
<body>
<table id="four-grid">
<tr>
<td>
<a href="https://www.wikipedia.org/" id="catalogEntry_img63666">
<img src="ssss"
alt="B" width="70" />
</a>
</td>
<td>
<a href="http://www.keralatourism.org/" id="catalogEntry_img63667">
<img src="ssss"
alt="A" width="70" />
</a>
</td>
</tr>
<tr>
<td>
<a href="https://stackoverflow.com/users/696627/lijo" id="catalogEntry_img63664">
<img src="ssss"
alt="G" width="70" />
</a>
</td>
<td>
<a href="http://msdn.microsoft.com/en-US/#fbid=zgGLygxrE84" id="catalogEntry_img63665">
<img src="ssss"
alt="Y" width="70" />
</a>
</td>
</tr>
</table>
</body>
</html>
首先,请注意,WebBrowser.DocumentText
是静态的,它保存原始内容而不进行任何 DOM/AJAX 更改。要获取实际的当前 HTML,请在后台线程上执行此操作:
var html = hiddenWebBrowser.Document.GetElementsByTagName("html")[0].OuterHtml;
然后,可以在 UI 线程上更新另一个WebBrowser
实例:
mainForm.BeginInvoke(new Action(() => mainForm.webBrowser.DocumentText = html));
请注意,BeginInvoke
是异步的,DocumentText
赋值也是异步的。加载 HTML 后,将触发 DocumentCompleted
事件以mainForm.webBrowser
。