C# WebBrowser - такой простой и такой сложный. Часть 2: FAQ

понедельник, 28 июня 2010, Александр Краковецкий

В предыдущей части мы рассмотрели основные принципы разработки HTML Editor на базе WebBrowser. В этом посте я постараюсь дать ответы на часто задаваемые вопросы по WebBrowser. Элемент управления WebBrowser позволяет разместить в приложениях Windows Forms веб-страницы и другие документы с поддержкой браузера. В первую очередь хочу остановиться на том, где можно использовать компонент WebBrowser:

  • при написании "своих" веб-браузеров со специфической логикой
  • для обеспечения интеграции справочной HTML-системы
  • для разработки уже рассмотренного нами HTML Editor
  • web data extracting, data parsing c помощью Microsoft.mshtml.dll (хотя этот вариант парсинга не самый лучший)

Каким образом отследить HTTP ошибки

Для этого в проект необходимо добавить библиотеку "Microsoft Internet Contols" (Add Reference -> COM), после чего написать следующий код:

        private void Form1_Load(object sender, EventArgs e)
        {
            //InitWebBrowser();
            webBrowser.Navigate("http://m.bing.com/");
            var axBrowser = (SHDocVw.WebBrowser)this.webBrowser.ActiveXInstance;
            axBrowser.NavigateError += new SHDocVw.DWebBrowserEvents2_NavigateErrorEventHandler(axBrowser_NavigateError);
        }

        void axBrowser_NavigateError(object pDisp, ref object URL, ref object Frame, ref object StatusCode, ref bool Cancel)
        {
            if (StatusCode.ToString() == "404")
            {
                MessageBox.Show("Page no found");
            }
        }

Почему событие DocumentCompleted вызывается несколько раз и как этого избежать?

Это происходит по той причине, что событие DocumentComplete вызывается каждый раз для отдельного фрейма (frame). Таким образом, для корректной работы необходимо добавить дополнительные проверки:

void BrowserDocumentCompleted(object sender,
        WebBrowserDocumentCompletedEventArgs e)
{
  if (e.Url.AbsolutePath != (sender as WebBrowser).Url.AbsolutePath)
    return; 

  //The page is finished loading 
}

Еще один способ рассмотрен дальше.

Как дождаться загрузки страницы полностью?

Более эффективным вариантом дождаться полного окончания загрузки страницы является вариант с использованием свойства ReadyState:

webBrowser.Navigate("http:///google.com");
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser_DocumentCompleted);

while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
{
    Application.DoEvents();
}

Как сделать скриншот веб-страницы программно?

Для этого есть метод DrawToBitmap:

void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    WebBrowser webBrowser = (WebBrowser)sender;
    webBrowser.ClientSize = new Size(1024, 800);
    webBrowser.ScrollBarsEnabled = false;
    Bitmap image = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height);
    webBrowser.BringToFront();
    webBrowser.DrawToBitmap(image, webBrowser.Bounds);
}

Вы не сможете сделать скриншот тех участков веб-страницы, где используется Flash или Silverlight.

На эту тему есть целый проект: http://screenshotsextractor.codeplex.com/

Как запретить использование веб-браузера?

У WebBrowser нет свойства Enabled, поэтому вы не можете запретить его использование напрямую. Но с учетом того, что WebBrowser - наследник Control, то вы можете это сделать таким образом:

((Control)webBrowser1).Enabled = false;

Как выполнить свой код на JavaScript?

Для этого необходимо использовать следующий код:

HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
element.text = "function sayHello() { alert('hello') }";
head.AppendChild(scriptEl);
webBrowser1.Document.InvokeScript("sayHello");

Как распечатать страницу с помощью WebBrowser?

Пример кода можно найти на MSDN:

private void PrintHelpPage()
{
    // Create a WebBrowser instance. 
    WebBrowser webBrowserForPrinting = new WebBrowser();

    // Add an event handler that prints the document after it loads.
    webBrowserForPrinting.DocumentCompleted +=
        new WebBrowserDocumentCompletedEventHandler(PrintDocument);

    // Set the Url property to load the document.
    webBrowserForPrinting.Url = new Uri(@"\\myshare\help.html");
}

private void PrintDocument(object sender,
    WebBrowserDocumentCompletedEventArgs e)
{
    // Print the document now that it is fully loaded.
    ((WebBrowser)sender).Print();

    // Dispose the WebBrowser now that the task is complete. 
    ((WebBrowser)sender).Dispose();
}

Как запретить отображение окна с JavaScript ошибками?

Несмотря на то, что это достаточно простой вопрос, его также часто задают. Для этого необходимо воспользоваться свойством ScriptErrorsSuppressed:

webBrowser.ScriptErrorsSuppressed = true;

Использование элемента управления WebBrowser в консольных приложениях

WebBrowser работает в STA (Single Thread Application) режиме, что значит, что вы, наверняка, получите сообщение об ошибке:

'XXX' cannot be instantiated because the current thread is not in a single-threaded apartment

Для избежания этой проблемы необходимо помечать все методы атрибутом [STAThread].

Если необходимо получить скриншот в новом потоке, то можно использовать такой код:

Thread m_thread = new Thread(new ThreadStart(GenerateWebSiteImage));
m_thread.SetApartmentState(ApartmentState.STA);
m_thread.Start();
m_thread.Join();

где GenerateWebSiteImage - метод для получения скриншота, приведенный выше.

Как изменить UserAgent у WebBrowser?

Несмотря на то, что это довольно простая задача при условии использования WebClient, для WebBrowser решение не такое уж очевидное:

public class ExtendedWebBrowser : WebBrowser
{
    bool renavigating = false;

    public string UserAgent { get; set; }

    public ExtendedWebBrowser()
    {
        DocumentCompleted += SetupBrowser;

        //this will cause SetupBrowser to run (we need a document object)
        Navigate("about:blank");
    }

    void SetupBrowser(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        DocumentCompleted -= SetupBrowser;
        SHDocVw.WebBrowser xBrowser = (SHDocVw.WebBrowser)ActiveXInstance;
        xBrowser.BeforeNavigate2 += BeforeNavigate;
        DocumentCompleted += PageLoaded;
    }

    void PageLoaded(object sender, WebBrowserDocumentCompletedEventArgs e)
    {

    }

    void BeforeNavigate(object pDisp, ref object url, ref object flags, ref object targetFrameName,
        ref object postData, ref object headers, ref bool cancel)
    {
        if (!string.IsNullOrEmpty(UserAgent))
        {
            if (!renavigating)
            {
                headers += string.Format("User-Agent: {0}\r\n", UserAgent);
                renavigating = true;
                cancel = true;
                Navigate((string)url, (string)targetFrameName, (byte[])postData, (string)headers);
            }
            else
            {
                renavigating = false;
            }
        }
    }
}

Как запретить у WebBrowser переход на предыдущую страницу?

Пользуемся следующим кодом:

public class myBrowser : WebBrowser
{

    private bool? myCanGoBack = null;
    public bool CanGoBack
    {
        get { return myCanGoBack ?? base.CanGoBack; }
        set { myCanGoBack = value; }
    }

}

Проблемы с кодировкой

Если вы загружаете HTML страницу из файла, то кириллические символы будут отображаться в неправильной кодировке.

Для избежания этой проблемы необходимо либо читать из файла в нужной кодировке:

StreamReader f = new StreamReader(имя_файла, false, System.Text.Encoding.UNICODE); 

либо задать кодировку для WebBrowser с помощью свойства Documemt.Encoding:

string Encoding = "ISO-8859-1";
webBrowser1.Document.Encoding = Encoding;

Парсинг HTML

Для парсинга HTML использование WebBrowser не обязательное, так как все равно задача в итоге сводится к использованию библиотеки Microsoft.mshtml или сторонних библиотек:

using mshtml;
...
object[] oPageText = { html };
HTMLDocument doc = new HTMLDocumentClass();
IHTMLDocument2 doc2 = (IHTMLDocument2)doc;
doc2.write(oPageText);

Но нужно понимать, что парсинг HTML с помощью WebBrowser также возможен:

//изображения
foreach (HtmlElement img in webBrowser.Document.Images)
{
   string src = img.GetAttribute("SRC");
}
// ссылки
foreach (HtmlElement link in webBrowserDocument.Links)
{
   string href = link.GetAttribute("HREF");
}

Еще несколько советов

Элемент управления WebBrowserинтенсивно потребляет системные ресурсы. Завершив работу с элементом управления не забудьте вызвать метод Dispose(), чтобы своевременно освободить все задействованные ресурсы. Необходимо вызвать метод Dispose() в том же потоке, присоединившем события, который всегда должен быть потоком сообщений или потоком пользовательского интерфейса.

Класс WebBrowser  может использоваться только в потоках в режиме однопотокового апартамента (STA).Чтобы использовать этот класс, убедитесь, что используемый метод Main  помечен атрибутом STAThreadAttribute.

Дополнительные ссылки

Компании из статьи


Microsoft Украина


Сайт:
http://www.microsoft.com/ukr/ua/

Microsoft Украина Украинское подразделение компании Microsoft.

Ищите нас в интернетах!

Комментарии

Свежие вакансии