Элемент управления WebBrowser
89WPF --- Элементы управления WPF --- Элемент управления WebBrowser
WPF стирает границы между традиционными настольными и веб-приложениями. За счет использования страниц можно создавать WPF-приложения с возможностями навигации в веб-стиле. ХВАР позволяют выполнять WPF-приложения внутри окна браузера. С применением элемента управления Frame можно проделывать обратное, размещая HTML-страницу в окне WPF.
Однако при использовании элемента Frame для отображения HTML-содержимого все возможности по управлению содержимым утрачиваются. Не остается никакого способа для его инспектирования или для следования ему, когда пользователь переходит на новую страницу по щелчку на ссылке. И, конечно же, отсутствует возможность вызова методов JavaScript внутри HTML-страницы либо их обращения к коду WPF. Вот здесь как раз и приходит на помощь элемент управления WebBrowser.
Элемент управления Frame является прекрасным вариантом, когда необходим контейнер, способный гладко переключаться между содержимым WPF и HTML. Элемент управления WebBrowser более удобен, когда нужно изучать объектную модель страницы, ограничивать или следить за страничной навигацией или создавать путь, через который код JavaScript и код WPF могут взаимодействовать.
И WebBrowser, и Frame (с HTML-содержимым) отображают стандартное окно Internet Explorer. Это окно обладает всеми возможностями и средствами браузера Internet Explorer, включая поддержку JavaScript, Dynamic HTML, элементов управления ActiveX и подключаемых модулей. Однако дополнительных деталей вроде панели инструментов, адресной строки или строки информации о состоянии это окно не содержит (хотя все эти ингредиенты легко добавить к форме с использованием соответствующих элементов управления).
Элемент управления WebBrowser не был написан с нуля в виде управляемого кода. Подобно Frame (когда он отображает HTML-содержимое), он является оболочкой для СОМ-компонента shdocvw.dll, который представляет собой часть Internet Explorer и включен в Windows. Как следствие, WebBrowser и Frame имеют несколько графических ограничений, которые отсутствуют у остальных элементов управления WPF. Например, не допускается размещать другие элементы поверх HTML-содержимого, отображаемого в этих элементах управления, равно как применять трансформации для скашивания или поворота.
В качестве средства, способность WPF отображать HTML-содержимое (с помощью Frame или WebBrowser) даже близко не настолько же полезно, как страничная модель или ХВАР. Тем не менее, в особых ситуациях при наличии готового HTML-содержимого, которое не хотелось бы заменять, это средство может пригодиться. Например, элемент WebBrowser может использоваться для отображения внутри приложения HTML-документации или для предоставления возможности переходить к функциональности, предлагаемой на веб-сайте сторонних разработчиков.
Навигация к странице
После помещения элемента управления WebBrowser в окно ему должен быть указан какой-нибудь документ. Самый простой подход предполагает установку его свойства Source в соответствующий URL-адрес. В общем случае это свойство может принимать в качестве значения любой удаленный URL-адрес (наподобие http://mysite.com/mypage.html) и полностью уточненный путь к файлу (такой как file:///c:\mydocument.text). URL-адрес может указывать на файл любого типа, который Internet Explorer способен открывать, хотя практически всегда WebBrowser используется для отображения HTML-страниц:
<WebBrowser Source="http://www.professorweb.ru"></WebBrowser>
Элемент управления WebBrowser можно также направить на каталог. Например, если присвоить его свойству Source путь вроде file:///c:\, то в этом случае окно WebBrowser приобретет вид хорошо знакомого окна для просмотра файлов в стиле проводника Windows, позволяя открывать, копировать, вставлять и удалять файлы. Однако никаких событий или свойств, которые бы дали возможность ограничить эти функции (либо хотя бы отслеживать их), не предусмотрено, поэтому соблюдайте осторожность!
Кроме свойства Source доступны также и методы навигации, которые перечислены ниже:
- Navigate()
Выполняет переход по указанному URL-адресу. Перегруженная версия позволяет загружать документ в определенный фрейм, выполнять обратную отправку данных и передавать дополнительные HTTP-заголовки
- NavigateToString()
Загружает содержимое из переданной ему строки с полным HTML-содержимым веб-страницы. Это открывает интересные возможности, такие как извлечение HTML-текста из ресурса внутри приложения и его отображение
- NavigateToStream()
Загружает содержимое из потока, который содержит HTML-документ. Это позволяет открывать файл и передавать его прямо в элемент управления WebBrowser для визуализации без удержания всего HTML-содержимого в памяти
- GoBack() и GoForward()
Переходят к предыдущему или следующему документу в хронологии навигации. Во избежание ошибок перед использованием этих методов необходимо проверять свойства CanGoBack и CanGoForward, т.к. попытка перейти к документу, которого не существует (например, перейти назад, находясь на первом документе в хронологии), вызывает генерацию исключения
- Refresh()
Перезагружает текущий документ
Вся навигация WebBrowser является асинхронной. Это значит, что код приложения продолжает выполнение во время загрузки страницы. Элемент управления WebBrowser поддерживает небольшой набор событий, которые описаны ниже:
Событие Navigating срабатывает при установке нового URL-адреса или при выполнении пользователем щелчка на ссылке. URL-адрес можно инспектировать и отменять навигацию, устанавливая е.Cancel в true.
Событие Navigated срабатывает после Navigating непосредственно перед тем, как в WebBrowser начинается загрузка страницы.
Событие LoadCompleted срабатывает после завершения процесса загрузки страницы и представляет собой удобную возможность для обработки полученной страницы.
Построение дерева DOM
При использовании элемента управления WebBrowser допускается писать код C#, который позволяет проходить по дереву HTML-элементов на странице. Можно даже изменять, удалять или вставлять элементы с помощью программной модели, подобной HTML DOM, которая применяется в таких языках сценариев для веб-браузеров, как JavaScript.
Прежде чем можно будет использовать модель DOM с WebBrowser, необходимо добавить ссылку на библиотеку Microsoft HTML Object Library (mshtml.tlb). Поскольку она является библиотекой СОМ-объектов, в Visual Studio понадобится сгенерировать для нее соответствующую управляемую оболочку. Для этого нужно выбрать в меню Project (Проект) пункт Add Reference (Добавить ссылку), перейти на вкладку СОМ, выбрать опцию Microsoft HTML Object Library (Библиотека объектов HTML от Microsoft) и щелкнуть на кнопке ОК.
Стартовой точкой для исследования содержимого на веб-странице является свойство WebBrowser.Document. Оно дает объект HTMLDocument, который представляет одиночную веб-страницу в виде иерархической коллекции объектов IHTMLElement.
Для каждого имеющегося на веб-странице дескриптора предусмотрен отдельный объект IHTMLElement, включая параграфы (<р>), гиперссылки (<а), изображения (<img>) и прочие знакомые ингредиенты HTML-разметки.
Свойство WebBrowser.Document доступно только для чтения. Это означает, что связанный с ним объект HtmlDocument модифицировать можно, но создавать для него новый объект HtmlDocument "на лету" нельзя. Вместо этого понадобится либо установить свойство Source, либо вызывать метод Navigate() для загрузки новой страницы. После срабатывания события WebBrowser.LoadCompleted можно получать доступ к свойству Document.
Построение объекта HTMLDocument занимает небольшое, но все же заметное время (в зависимости от размеров и сложности веб-страницы). Элемент управления WebBrowser на самом деле не создает объект HTMLDocument для страницы до тех пор, пока не будет предпринята первая попытка доступа к свойству Document. Каждый объект IHTMLElement имеет описанные ниже ключевые свойства:
Свойство tagName содержит сам дескриптор без квадратных скобок. Например, дескриптор привязки имеет вид <а href = "...">...</a>, поэтому его имя в tagName будет выглядеть как А.
Свойство id содержит значение атрибута id, если он был указан. Элементы идентифицируются с помощью уникальных атрибутов id, когда необходимости иметь возможность манипулировать ими в средстве автоматизации или серверном коде.
Свойство children содержит коллекцию объектов IHTMLElement, по одному объекту для каждого имеющегося дескриптора.
Свойство innerHTML хранит полное содержимое дескриптора, включая любые вложенные в него дескрипторы вместе с их содержимым.
Свойство innerText хранит полное содержимое самого дескриптора и содержимое любых вложенных в него дескрипторов. Однако все HTML-дескрипторы отбрасываются.
Свойства outerHTML и outerText исполняют ту же роль, что и innerHTML и innerText, но включают текущий дескриптор (а не только его содержимое).
Чтобы лучше понять свойства innerText, innertHTML и outerHTML, предположим, что есть следующий дескриптор:
<p>Here is some <i>interesting</i> text.</p>
Значение свойства innerText для этого дескриптора будет выглядеть так:
Here is some interesting text.
Значение свойства innerHTML — так:
Here is some <i>interesting</i> text.
И, наконец, значение свойства outerHTML включает в себя полностью весь дескриптор:
<p>Here is some <i>interesting</i> text.</p>
Вдобавок с помощью метода IHTMLElement.getAttribute() можно извлечь значение атрибута для элемента по имени.
Для навигации по модели документа HTML-страницы нужно просто перемещаться по коллекции дочерних элементов каждого объекта IHTMLElement. В следующем коде эта задача выполняется в ответ на щелчок на кнопке. При этом создается дерево, отражающее структуру элементов и содержимого на странице:
private void DOM_Click(object sender, RoutedEventArgs e)
{
this.Cursor = Cursors.Wait;
// Объект DOM из WebBrowser
HTMLDocument dom = (HTMLDocument)browser.Document;
ProcessElement(dom.documentElement, tree.Items);
this.Cursor = null;
}
private void ProcessElement(IHTMLElement parentElement, ItemCollection nodes)
{
foreach (IHTMLElement element in parentElement.children)
{
TreeViewItem node = new TreeViewItem();
node.Header = "<" + element.tagName + ">";
nodes.Add(node);
if ((element.children.length == 0) && (element.innerText != null))
node.Items.Add(element.innerText);
else
ProcessElement(element, node.Items);
}
}