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. |
Украинское подразделение компании Microsoft.