Взаимодействие с HTML

170

Приложения Silverlight работают в собственной, тщательно изолированной, среде и потому избавлены от болезненных проблем несовместимости, терзающих разработчиков традиционных браузерных приложений. Это огромное преимущество. Оно позволяет эффективно сочетать коды C# и XAML, не продираясь сквозь проблемы несовместимости с браузерами разных типов.

Однако в некоторых ситуациях возникает необходимость создать приложение, выходящее за рамки среды Silverlight. Например, иногда нужно добавить содержимое Silverlight на существующую веб-страницу или обеспечить взаимодействие компонентов HTML и Silverlight.

Существует несколько причин, которые могут привести к необходимости взаимодействия классического мира браузеров с управляемой средой Silverlight:

Silverlight содержит ряд управляемых классов, поддерживающих инфраструктуру DOM (Document Object Model — объектная модель документов) HTML-страниц. Эти классы позволяют коду Silverlight взаимодействовать с HTML-кодом, расположенным на той же странице. В зависимости от ситуации взаимодействие может представлять собой чтение значений элемента управления, обновление текста, добавление на страницу новых элементов HTML и т.п.

Классы, необходимые для взаимодействия с HTML-кодом, находятся в пространстве имен System.Windows.Browser:

Классы пространства имен System.Windows.Browser
Имя класса Описание
HtmlPage Представление текущей HTML-страницы, на которой расположен элемент управления Silverlight. Класс HtmlPage — отправная точка для большинства средств взаимодействия с HTML. Он предоставляет средства доступа к элементам HTML, расположенным на странице (свойство Document), извлечения информации о браузере (свойство BrowserInformation), взаимодействия с текущим окном браузера (свойство Window) и регистрации методов Silverlight, которые нужно сделать доступными в коде JavaScript (методы RegisterCreatableType() и RegisterScriptableType()).
BrowserInformation Предоставляет базовую информацию о браузере, в котором выполняется приложение, включая тип и версию браузера, а также тип операционной системы. Экземпляр класса BrowserInformation можно извлечь из свойства HtmlPage.BrowserInformation.
HtmlDocument Полное представление HTML-документа. Экземпляр класса HtmlDocument, представляющий текущую HTML-страницу, можно извлечь из свойства HtmlPage.Document. Затем объект HtmlDocument можно использовать для исследования структуры и содержимого страницы. Объекты HtmlElement расположены на разных уровнях вложенности в объекте HtmlDocument.
HtmlElement Представление элемента HTML-страницы. Объектом HtmlElement можно манипулировать с помощью методов SetAttribute() и SetProperty(). Обычно объект HtmlElement находится в объекте HtmlDocument.
HtmlWindow Представление окна браузера. Содержит методы навигации к новой странице или анкорам текущей страницы. Экземпляр HtmlWindow можно извлечь из свойства HtmlPage.Window текущей страницы.
HttpUtility Предоставляет статические методы обработки элементов HTML, включая кодирование и декодирование текста (для обеспечения безопасности веб-страницы), кодирование и декодирование адресов URL (для обеспечения их безопасности, например, в аргументе строки запроса)
ScriptableTypeAttribute и ScriptableMemberAttribute Эти атрибуты используются для расширения классов и методов приложения Silverlight, вызываемых из кода JavaScript на HTML-странице
ScriptObject Представляет функцию JavaScript, определенную на странице, и позволяет вызывать ее в приложении Silverlight

Получение информации о браузере

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

Информация о браузере доступна в классе BrowserInformation. Он предоставляет четыре строковых свойства, содержащих имя браузера, его версию, тип операционной системы и строку пользовательского агента — длинную строку, содержащую технические подробности браузера (например, в Internet Explorer в нее включены текущие установленные версии .NET Framework).

Кроме того, булево свойство CookiesEnabled можно использовать для выяснения, поддерживает ли текущий браузер файлы "cookie" и включены ли они (в этом случае свойство равно true и с помощью класса HtmlPage можно читать и редактировать файлы "cookie").

Информация, получаемая от класса BrowserInformation, зависит от того, как браузер представляет себя внешнему миру, и может не отражать истинные параметры браузера. Например, браузер может быть сконфигурирован таким образом, что будет представляться внешним агентам как браузер другого типа. Этот метод часто используется для обеспечения более широкой совместимости. Следовательно, не очень полагайтесь на информацию, предоставляемую браузером о себе.

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

BrowserInformation b = HtmlPage.BrowserInformation;
lblInfo.Text = "Название: " + b.Name;
lblInfo.Text += "\nВерсия браузера: " + b.BrowserVersion;
lblInfo.Text += "\nПлатформа: " + b.Platform;
lblInfo.Text += "\nВключены ли cookie: " + (b.CookiesEnabled ? "да" : "нет");
lblInfo.Text += "\nUser Agent: " + b.UserAgent;
Информация о браузере

Окно HTML

Надстройка Silverlight предоставляет возможность (довольно ограниченную) управлять браузером посредством класса HtmlWindow. Этот класс содержит два метода, позволяющих запускать навигацию — Navigate() и NavigateToBookmark().

Метод Navigate() вынуждает браузер открыть другую HTML-страницу. Перегруженную версию метода Navigate() можно использовать для задания целевого фрейма. При вызове метода Navigate() текущее приложение Silverlight закрывается. Вызов метода Navigate() аналогичен вводу нового URL-адреса в адресной строке браузера.

Метод NavigateToBookmark() прокручивает текущую страницу до заданной закладки. Закладкой служит элемент <а> с атрибутом id или именем:

<а id="myBookmark">...</а>

Чтобы перейти к закладке, добавьте символ # перед именем закладки в конце URL-адреса:

<а href="page.html#myBookmark">Перейти к закладке</а>

Извлечь закладку из текущего URL-адреса браузера можно в любой момент с помощью свойства CurrentBookmark. Это единственное свойство класса HtmlWindow.

Метод NavigateToBookmark() и свойство CurrentBookmark предоставляют ряд интересных возможностей. Например, закладку можно использовать для сохранения информации о состоянии страницы. Эта информация является частью строки URL, поэтому она сохраняется в истории браузера и в списке предпочтений (если закладкой отмечена страница с содержимым Silverlight). Сохраненная таким образом информация служит основой высокоуровневой инфраструктуры навигации.

Всплывающее окно

Класс HtmlPage содержит метод PopupWindow(), позволяющий открыть всплывающее окно для вывода новой веб-страницы. Оно используется главным образом для отображения рекламных материалов и содержимого других веб-сайтов. Для вывода других компонентов текущего приложения Silverlight оно не предназначено. Если необходимо всплывающее окно для отображения компонентов текущего приложения Silverlight, лучше воспользоваться элементом управления ChildWindow.

Метод PopupWindow() довольно надежен: он "обманывает" большинство средств блокировки всплывающих окон, встроенных в браузеры (в зависимости от пользовательских установок). Однако полагаться на то, что он выведет важные компоненты приложения Silverlight, нельзя. С его помощью рекомендуется выводить только дополнительную информацию, несущественную для приложения. Метод PopupWindow() открывает окно путем вызова метода Window.Open() сценария JavaScript.

Ниже приведен пример использования метода PopupWindow(). Обратите внимание на то, что код проверяет свойство IsPopupWindowAllowed, чтобы избежать возможных ошибок вследствие того, что всплывающие окна поддерживаются не во всех сценариях:

if (HtmlPage.IsPopupWindowAllowed)
{
       // Конфигурирование всплывающего окна 
       HtmlPopupWindowOptions options = 
           new HtmlPopupWindowOptions(); 
       options.Resizeable = true;

       // Вывод всплывающего окна.
       // Окну передается абсолютный URI, необязательный 
       // целевой фрейм и значение HtmlPopupWindowOptions. 
       HtmlPage.PopupWindow(new Uri("ссылка на страницу"), null, options);
}

Ниже приведены правила и ограничения, накладываемые на всплывающее окно:

Анализ HTML-документа

Извлечение информации о браузере и навигация — относительно простые задачи. Более интересные проблемы возникают, когда нужно проанализировать структуру HTML-страницы, хостирующей содержимое Silverlight.

Анализ начинается с использования двух статических свойств класса HtmlPage. Свойство Plugin содержит ссылку на элемент <object>, который представляет элемент управления Silverlight как объект HtmlElement. Свойство Document предоставляет объект HtmlDocument, который представляет всю страницу с набором элементов страницы включительно:

Члены класса HtmlDocument
Имя Описание
DocumentUri URL-адрес текущего документа как объект Uri
QueryString Часть строки запроса URL
DocumentElement Объект HtmlElement, представляющий верхнеуровневый элемент <html> страницы
Body Объект HtmlElement, представляющий элемент <body> HTML-страницы
Cookies Коллекция всех текущих файлов "cookie". Файлы "cookie" можно читать и редактировать. Они предоставляют удобный способ передачи информации между серверным кодом ASP.NET и клиентским кодом Silverlight. Однако редактирование файлов "cookie" — не лучший способ сохранения небольшого объема данных на клиентском компьютере; для этого специально предназначены изолированные хранилища, обеспечивающие лучшую совместимость и поддерживающие интеграцию с кодом приложения
IsReady Это свойство равно true, если браузер не занят, и false, если страница все еще загружается
CreateElement() Новый объект HtmlElement, представляющий динамически созданный элемент HTML, который можно вставить на страницу
AttachEvent() и DetachEvent() Подключение обработчика, определенного в приложении Silverlight, к событию JavaScript, генерируемому документом, и его отключение
Submit() Передача страницы путем передачи формы и данных на сервер. Этот метод полезен, когда элемент управления Silverlight хостируется страницей ASP.NET, потому что он передает обратный запрос, позволяющий запустить выполнение серверного кода

Имея объект HtmlDocument, представляющий страницу, можно спуститься вниз по дереву элементов, начиная с HtmlDocument.DocumentElement или HtmlDocument.Body. Для перехода от одного элемента к другому используется свойство Children, содержащее элементы, вложенные в текущий элемент, или свойство Parent, содержащее родительский элемент.

Ниже показано приложение Silverlight, которое начинает просмотр с верхнеуровневого элемента <html> и применяет рекурсивный метод для прохода вниз по странице. Приложение выводит имя и атрибут id каждого элемента. Ниже приведен код:

private void UserControl_Loaded_1(object sender, RoutedEventArgs e)
{
    // Начало обработки высокоуровневого элемента html
    HtmlElement element = HtmlPage.Document.DocumentElement;
    ProcessElement(element, 0);
}

private void ProcessElement(HtmlElement element, int indent)
{
    // Пропуск комментариев
    if (element.TagName == "!") return;

    if (element.TagName != "")
    {
        // Создание отступов, характеризующих степень вложенности элементов
        lblElementTree.Text += new String(' ', indent * 4);

        // Отобразить название тега
        lblElementTree.Text += "<" + element.TagName;

        // Вывод атрибута id, если он есть
        if (element.Id != "") lblElementTree.Text += " id=\"" + element.Id + "\"";
        lblElementTree.Text += ">\n";
    }

    // Рекурсия вложенных элементов
    foreach (HtmlElement childElement in element.Children)
    {
        ProcessElement(childElement, indent + 1);
    }
}
Синтаксический разбор текущей страницы

Элемент HtmlElement предоставляет несколько свойств. Кроме Children и Parent, позволяющих переходить к другим элементам, он предоставляет свойства TagName и Id, продемонстрированные в предыдущем примере, и свойство CssClass, указывающее на имя стиля CSS, установленного посредством атрибута класса и применяемого для конфигурирования внешнего вида текущего элемента.

Манипулирование элементами HTML

Свойства Parent и Children предоставляют не единственный способ прохода по иерархии объекта HtmlDocument. Например, с помощью метода GetElementByID() или GetElementsByTagName можно найти элемент с заданным именем. Найдя нужный элемент, им можно манипулировать с помощью методов, описанных ниже:

Методы класса HtmlElement
Описание Метод
AppendChild() Вставка нового элемента HTML как вложенного в текущий элемент. Чтобы создать элемент, нужно сначала применить метод HtmlDocument.CreateElement()
RemoveChild() Удаление элемента HtmlElement, заданного посредством аргумента. Удаляемый элемент HtmlElement должен быть дочерним, вложенным в текущий элемент HtmlElement
Focus() Присвоение фокуса текущему элементу, в результате чего он будет получать события клавиатуры
GetAttribute(), SetAttribute() и RemoveAttribute() Извлечение, установка и удаление атрибута. Если при установке значения методом SetAttribute() атрибут не существует, он будет создан
GetStyleAttribute(), SetStyleAttribute() и RemoveStyleAttribute() Извлечение значения свойства CSS, установка значения и удаление атрибута стиля. Свойства CSS применяются для форматирования элементов HTML и позволяют управлять шрифтами, цветами фона и переднего плана, позиционированием, рамками и т.п.
GetProperty() и SetPrpperty() Извлечение и установка значений свойств, определенных в модели DOM. Обычно этими значениями манипулируют посредством кода JavaScript. Например, с помощью свойства innerHTML можно извлечь текстовое содержимое элемента
AttachEvent() И DetachEvent() Подключение обработчика, определенного в приложении Silverlight, к событиям JavaScript, генерируемым элементами HTML, и его отключение

Предположим, элемент <р> расположен под областью содержимого Silverlight, которая занимает не все окно браузера. Чтобы манипулировать элементом <р> в коде приложения Silverlight было удобнее, рекомендуется присвоить ему уникальное значение id:

<p id="paragraph">...</p>

Тогда извлечь объект HtmlElement, представляющий данный элемент, можно будет в любом обработчике Silverlight:

HtmlPage.Document.GetElementById("paragraph")
        .SetProperty("innerHTML", "Обновление абзаца из кода Silverlight");

Приведенный выше код вызывает метод HtmlElement.SetProperty() и устанавливает свойство innerHTML, являющееся одним из фундаментальных компонентов модели DOM.

На рисунке показана тестовая страница, демонстрирующая работу приведенного выше кода. Верхняя часть страницы занята областью содержимого Silverlight, выводящего кнопку. При щелчке на кнопке изменяется текст расположенного ниже элемента HTML:

Изменение элемента HTML с помощью кода Silverlight

Нетрудно заметить, что средства стыковки Silverlight с DOM HTML несовершенны. Платформа Silverlight предоставляет доступ не к полной модели DOM, а к ее сокращенной версии, стандартизированной на основе класса HtmlElement.

Во время работы с этим элементом иногда необходимо устанавливать свойства DOM (такие, как innerHTML в предыдущем примере) с помощью метода SetProperty() и предоставлять имя свойства как строку.

Если вы часто работаете с некоторым элементом HTML, то рекомендуется заключить его в оболочку высокоуровневого пользовательского класса (например, создав пользовательский класс Paragraph) и заменить свойства DOM или CSS строго типизированными свойствами. Многие разработчики делают так для предотвращения опечаток в именах свойств. Во время компиляции такие ошибки не выявляются.

Вставка и удаление элементов

В предыдущем примере код Silverlight модифицирует элемент HTML. Так же легко добавить или удалить элемент. Для этого предназначены методы HtmlDocument.CreateElement(), HtmlElement.AppendChild() и HtmlElement.RemoveChild(). Ниже приведен код, создающий элемент <р>:

HtmlElement element = HtmlPage.Document.CreateElement("р"); 
element.Id = "paragraph"; 
element.SetProperty("innerHTML", "Это текст нового элемента.");
HtmlPage.Document.Body.AppendChild(element);

В данном примере объект element добавляется как последний дочерний элемент, т.е. вставляется в конец элемента <body>. Иногда нужно вставить динамическое содержимое в определенное место. Легче всего определить для этого пустой контейнер <div> с уникальным значением id. Тогда можно будет извлечь объект HtmlElement, представляющий контейнер <div>, и вставить динамическое содержимое с помощью метода AppendChild().

Обычно метод AppendChild() добавляет новый элемент в конец коллекции вложенных дочерних элементов. Позиционировать элемент более точно можно с помощью перегруженной версии метода AppendChild(), которая принимает другой элемент HtmlElement, используемый в качестве ссылки. Новый элемент вставляется перед указанным в ссылке:

...
// Получение ссылки
HtmlElement referenceElement = HtmlPage.Document.Body.Children[0] as HtmlElement; 

// Вставка элемента перед указанным в ссылке
HtmlPage.Document.Body.AppendChild(element, referenceElement);

Удалить элемент так же легко, как и вставить. Учитывайте лишь, что нужно использовать метод RemoveChild() родительского элемента, а не удаляемого. Ниже приведен код, удаляющий абзац с атрибутом id="paragraph" (если он существует):

HtmlElement element = HtmlPage.Document.GetElementById("paragraph"); 
if (element != null)
       element.Parent.RemoveChild(element);
Пройди тесты
Лучший чат для C# программистов