У меня есть Windows Form
, у которого есть управляющие имена WebBrowser
formWebBrowser. Я создаю новый поток без UI с другим экземпляром WebBrowser с именем newThreadBrowser. Ссылка Управление WebBrowser в новом потоке
Когда завершено событие, связанное с документацией, я могу написать URL-адрес в текстовое поле, используя подход, упомянутый в С# - Обновление GUI с использованием не основного потока
Теперь я пытаюсь обновить html "formWebBrowser" из html "newThreadBrowser". Это вызывает исключение, если указанное задание недействительно.
В Элемент управления WebBrowser: "Указанный отбор недействителен." , принятый ответ говорит -
WebBrowser - это COM-компонент под капотом. В квартире с резьбой, COM заботится о том, чтобы называть ее методы поточно-безопасным способом. По этой причине ваш вызов Navigate() работает, он фактически выполняется в потоке пользовательского интерфейса. Что не работает, это свойство DocumentText, оно реализовано в оболочке .NET, и они несколько пошатнули код. Он бомбит, когда поддержка взаимодействия COM в CLR замечает, что поток в MTA пытается получить доступ к свойству компонента, который живет на STA.
Вопрос
Что мне делать для рендеринга html из newThreadBrowser в formWebBrowser? Я не уверен, как Control.Invoke() может решить эту проблему.
Примечание. Это приложение не критично для производительности. Так что это нормально, даже если потребуется некоторое время для выполнения.
Ссылка
Из Свойство WebBrowser.DocumentText
Используйте это свойство, если вы хотите манипулировать содержимым страницы HTML, отображаемой в элементе управления WebBrowser, с помощью инструментов обработки строк. Вы можете использовать это свойство, например, для загрузки страниц из базы данных или для анализа страниц с использованием регулярных выражений. Когда вы устанавливаете это свойство, элемент управления WebBrowser автоматически переходит к адресу: пустой URL-адрес перед загрузкой указанного текста. Это означает, что события Navigating, Navigated и DocumentCompleted возникают, когда вы устанавливаете это свойство, а значение свойства Url больше не имеет смысла.
CODE
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);
}
}
}
MyTableTest.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;
Затем вы можете обновить еще один экземпляр WebBrowser
в потоке пользовательского интерфейса:
mainForm.BeginInvoke(new Action(() => mainForm.webBrowser.DocumentText = html));
Примечание. BeginInvoke
является асинхронным, а также назначается DocumentText
. Событие DocumentCompleted
будет запущено для mainForm.webBrowser
при загрузке HTML.
WebBrowser
только для загрузки страницы, вам действительно не нужен фоновый поток для загрузки. Загрузите его в основной поток пользовательского интерфейса, затем переложите обработку HTML Agility Pack в фоновый поток.