Как обновить DocumentText другим DocumentText

2

У меня есть 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>
  • 0
    Не могли бы вы объяснить, почему именно вам нужно несколько потоков здесь?
  • 0
    @Noseratio Если я выполню обработку DOM в потоке пользовательского интерфейса, это заблокирует поток пользовательского интерфейса, не так ли? Это повлияет на операции пользовательского интерфейса, такие как сворачивание окна. Что вы думаете об этом? Ссылка MSDN - Безопасная, простая многопоточность в Windows Forms
Показать ещё 2 комментария
Теги:
multithreading
winforms
webbrowser-control

1 ответ

1

Во-первых, обратите внимание, что 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.

  • 1
    @Lijo, еще одна мысль об этом. Вы указали HTML Agility Pack в своем вопросе. Если вы используете его для обработки страницы и используете WebBrowser только для загрузки страницы, вам действительно не нужен фоновый поток для загрузки. Загрузите его в основной поток пользовательского интерфейса, затем переложите обработку HTML Agility Pack в фоновый поток.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню