C# WebBrowser - такой простой и такой сложный. Часть 2: FAQ
В предыдущей части мы рассмотрели основные принципы разработки 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 Украина | Украинское подразделение компании Microsoft. |