Опубликован: 02.04.2013 | Уровень: для всех | Доступ: свободно
Лекция 5:

Элементы управления, их стилизация и привязка данных

Расширения для HTML-элементов

Как вы уже, наверное, знаете, есть множество разрабатываемых стандартов для HTML и CSS. До тех пор, пока разработка не завершена, реализация этих стандартов в различных браузерах обычно бывает доступна с помощью префикса поставщика. В дополнение к этому, разработчики браузеров иногда добавляют собственные расширения к DOM API для различных элементов.

В случае с приложениями для Магазина Windows, конечно, вам не нужно беспокоиться о различиях между браузерами, но так как эти приложения исполняются на основе механизмов Internet Explorer, полезно знать об этих расширениях, которые всё еще применимы. Они приведены в таблице ниже, вы можете найти полное описание элементов (http://msdn.microsoft.com/library/windows/apps/hh767345.aspx) в документации, узнать там любые интересующие вас подробности (их слишком много, чтобы излагать их здесь).

Если вы уже работали с HTML5 и CSS3 в Internet Explorer, вы можете задаться вопросом, почему в таблице не показаны различные анимации (msAnimation*), переходы (msTransition*) и свойства трансформации (msPerspective* and msTransformStyle), вместе с msBackfaceVisibility. Это потому, что эти возможности теперь стандартны и не нуждаются в префиксах поставщиков для Internet Explorer 10 или для приложений Магазина Windows (хотя, их вариант с префиксом ms* всё еще работает).

Таблица 4.2.
Методы Описание
msMatchesSelector Определяет, удовлетворяет ли элемент управления некоторым условиям.
ms[Set | Get | Release]PointerCapture Осуществляет захват, восстановление и освобождение указателя для элемента.
Свойства стиля (в element.style) Описание
msGrid*, msRow* Получает или задаёт расположение элемента внутри CSS-сетки.
События (добавьте "on" для свойств события) Описание
mscontentzoom Вызывается, когда пользователь меняет масштаб элемента (Ctrl+ +/-, Ctrl +колесо мыши), жесты сжатия и расширения.
msgesture[change | end | hold | tap | pointercapture] Жест событий ввода (смотрите "Анатомия приложения и навигация по страницам" курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript").
msinertiastart Жесты событий ввода (смотрите "Анатомия приложения и навигация по страницам" курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript").
mslostpointercapture Элемент теряет указатель (ранее установленный msSetPointerCapture.
mspointer[cancel | down | hover | move | out | over | up] События ввода указателя (смотрите "Анатомия приложения и навигация по страницам" курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript").
msmanipulationstatechanged Состояние элемента изменилось после манипуляции

Элементы управления WinJS

Windows 8 определяет множество элементов управления, которые помогают приложениям удовлетворить требования к дизайну. Как отмечено ранее, они реализованы в WinJS а не в WinRT для приложений, написанных на HTML, CSS и JavaScript. Это позволяет данным элементам управления естественным образом интегрироваться с другими DOM-элементами. Каждый элемент управления определяется как часть пространства имен WinJS.UI с использованием WinJS.Class.define, где имя конструктора совпадает с именем элемента управления. В итоге, полное имя конструктора для элемента управления наподобие Rating (Оценка) выглядит как WinJS.UI.Rating.

Самые простые элементы, которые мы рассмотрим здесь, это DatePicker, Rating, ToggleSwitch, и Tooltip, стиль которых по умолчанию показан на Рис. 4.3.

Стили по умолчанию (светлые) для простых элементов управления WinJS

Рис. 4.3. Стили по умолчанию (светлые) для простых элементов управления WinJS

Элемент управления WinJS.UI.Tooltip, вам нужно это знать, может использовать любой HTML-код, включая другие элементы управления, поэтому он идёт дальше простых текстовых всплывающих подсказок, где HTML автоматически предоставляется для атрибута title. Мы увидим дополнительные примеры дальше.

Итак, еще раз, элементы управления WinJS объявляют в разметке, присоединяя атрибуты data-win-control и data-win-options к некоторым корневым элементам. Это обычно элементы div (они задают блоки) или span (внутренние элементы), так как они не добавляют слишком много лишнего, но использовать можно любой элемент. Эти элементы, конечно, могут иметь атрибуты id и class, если они нужны. Параметры, доступные для этих элементов, приведены в таблице, ниже, они включают в себя события, которые могут быть подключены посредством строки data-win-options, если нужно. Для того, чтобы найти полную документацию по этим элементам управления, начните со "Списка элементов управления" (http://msdn.microsoft.com/library/windows/apps/hh465453.aspx) и следуйте по ссылкам, ведущим к подробным описаниям конкретных элементов.

Таблица 4.3.
Полное имя конструктора в data-win-control Параметры data-win-options (обратите внимание, что имена событий используют префикс "on" в синтаксисе атрибутов)
WinJS.UI.DatePicker Свойства: calendar, current, datePattern, disabled, maxYear, minYear, monthPattern, yearPattern События: onchange
WinJS.UI.Rating Свойства: averageRating, disabled, enableClear, maxRating, tooltipStrings (массив строк размера maxRating), userRating События: oncancel, onchange, onpreviewchange
WinJS.UI.TimePicker Свойства: clock, current, disabled, hourPattern, minuteIncrement, periodPattern. (Обратите внимание, что данные в current всегда будут 15 Июля 2011 так как в этот день нет известных переходов на летнее время.) События: onchange
WinJS.UI.ToggleSwitch Свойства: checked, disabled, labelOff, labelOn, title События: onchange
WinJS.UI.Tooltip Свойства: contentElement, innerHTML, infotip, extraClass, placement События: onbeforeclose, onbeforeopen, onclosed, onopened Методы: open, close

Что касается строки data-win-options, она содержит пары ключ-значение, одну для каждого свойства или события, разделенные запятыми, в форме { <key1>: <value1>, <key1>: <value2>, ... }. Для событий, имена которых находятся в строке параметров, они всегда начинаются с on, вы должны задать имя обработчика событий, который вы хотите им назначить.

В коде JavaScript вы так же можете назначить обработчики событий, используя <element>.addEventListener ("<event>", ...), где <element> это элемент, для которого был объявлен элемент управления и <event> без "on", как обычно. Для того чтобы получить прямой доступ к свойствам и событиями, используйте <element>.winControl.<property>. Объект winControl создаётся, когда создаётся экземпляр элемента управления WinJS и прикрепляется к элементу, в итоге, там доступны эти параметры.

Создание экземпляров объектов элементов управления WinJS

Как мы уже видели много раз, элементы управления WinJS объявляются в разметке с атрибутом data-* и их экземпляры не создаются до тех пор, пока не будет вызван WinJS.UI.process(<element>) для отдельного элемента или WinJS.UI.processAll для всех подобных элементов в DOM. Для того, чтобы понять этот процесс, опишем, что делает WinJS.UI.process для отдельного элемента.

  1. Превращает строку data-win-options в объект, содержащий параметры.
  2. Извлекает конструктор, заданный в data-win-control и вызывает команду new с обнаруженной функцией, передавая сведения о корневом элементе и объекте с параметрами.
  3. Конструктор создаёт любые необходимые ему элементы-потомки в корневом элементе.
  4. Объект возвращённый из конструктора - объект элемента управления - сохраняется в свойстве winControl корневого элемента.

Очевидно, что большая часть работы, на самом деле, происходит в конструкторе. Как только она будет выполнена, другой JavaScript-код (как в методе activated) может вызывать методы, манипулировать свойствами, добавлять прослушиватели событий и к корневому элементу и к объекту winControl. Последние, очевидно, должны быть методами, свойствами и событиями, специфичными для элементов управления WinJS. WinJS.UI.processAll, в свою очередь, просто обходит DOM в поиске атрибутов data-win-control и выполняет для каждого из них. Как вы воспользуетесь тем и другим, это, на самом деле, ваш выбор: processAll проходится по всей странице (или по элементу управления страницы - там, где объект document на неё ссылается), в то время как process позволяет вам контролировать последовательность происходящего или создавать экземпляры элементов управления, для которых вы динамически добавляете разметку. Обратите внимание, что в обоих случаях возвращённое значение является promise-объектом, в итоге, если вам нужно предпринять дополнительные шаги после того, как обработка завершена, предоставьте обработчик завершения методу done promise-объекта.

Кроме того, полезно понимать, что и process и processAll - это лишь вспомогательные функции. Если вам нужно, вы можете напрямую вызвать new для конструктора элемента управления, передав ему элемент и объект с параметрами. Это позволит создать элемент управления и автоматически присоединить его к переданному элементу. Кроме того, вы можете передать для элемента null, в таком случае, конструктор элемента управления WinJS создаст новый div-элемент, содержащий элемент управления, который в подобном случае отсоединён от DOM. Это позволит вам, например, создать элемент управления в скрытом виде и присоединить его к DOM тогда, когда он будет нужен.

Для того чтобы увидеть всё это в действии, мы скоро взглянем на некоторые примеры с элементами управления Rating (Оценка) и Tooltip (Подсказка). В первую очередь, однако, нам нужно обсудить то, что касается строгой обработки (strict processing).

Строгая обработка и функции processAll

WinJS имеет три функции для обхода DOM: WinJS.UI.processAll, WinJS.Binding.processAll (которую мы рассмотрим позже в этой лекции), и WinJS.Resources.processAll (которую мы увидим в "Макет" Курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой"). Каждая из этих функций ищет специфические атрибуты data-win-* и затем предпринимает дополнительные действия с использованием данного содержимого. Эти действия, однако, могут включать в себя вызов некоторого количества функций различных типов:

  • Функции, появляющиеся в "пути с точками" для обработки элементов управления и источников связанных данных.
  • Функции, появляющиеся слева для целевых объектов привязки данных, целей ресурсов или обработки элементов управления.
  • Конструкторы элементов управления и обработчики событий.
  • Функции инициализации привязки данных или функции, используемые в выражениях привязки данных.
  • Любые пользовательские макеты, используемые для элемента управления ListView.

Подобные действия представляют риск атак методом внедрения, если функция processAll вызывается для недоверенного HTML, такого, как любая разметка, полученная из веб. Для уменьшения этого риска, WinJS имеет понятие строгая обработка (strict processing), которая принудительно используется внутри всех HTML/JavaScript-приложений. Эффект строгой обработки заключается в том, что любая функция, отображающаяся в разметке, которую могут встретить методы processAll, должна быть "маркирована для обработки", в противном случае обработка не состоится. Сама по себе маркировка - это обычное свойство, названное supportedForProcessing в объекте функции, которая установлено в true.

Функции, возвращаемые из WinJS.Class.define, WinJS.Class.derive, WinJS.UI.Pages.define, и WinJS.Binding.converter автоматически маркированы подобным образом. Для других функций, вы можете либо установить свойство supportedForProcessing в true напрямую, либо воспользоваться одной из маркирующих функций:

WinJS.Utilities.markSupportedForProcessing(myfunction); WinJS.UI.eventHandler(myHandler);
WinJS.Binding.initializer(myInitializer);

//Так тоже можно
<namespace>.myfunction = WinJS.UI.eventHandler(function () {
});

Обратите внимание на то, что функции, поступающие напрямую из WinJS, такие, как конструкторы элементов управления WinJS.UI.*, и функции WinJS.Binding.* маркированы по умолчанию.

В итоге, если вы ссылаетесь на пользовательскую функцию из вашей разметки, убедитесь в том, что вы маркировали её соответствующим образом. Но это только для ссылок из разметки: вам не нужно маркировать функции, которые вы присваиваете свойствам on<event> в JavaScript или передаёте в addEventListener.

Упражнение: Элемент управления WinJS.UI.Rating (Оценки)

Хорошо, теперь, когда мы обсудили вопрос строгой обработки, посмотрим конкретные примеры работы с элементами управления WinJS.

Для начинающих, вот разметка для элемента управления WinJS.UI.Rating, где параметры задают значения двух начальных свойств и обработчик событий:

<div id="rating1" data-win-control="WinJS.UI.Rating"
data-win-options="{averageRating: 3.4, userRating: 4, onchange: changeRating}">
</div>

Для создания экземпляра этого элемента управления, нам нужен один из следующих вызовов:

WinJS.UI.process(document.getElementById("rating1")); WinJS.UI.processAll();

Повторюсь, обе эти функции возвращают promise-объект, но нет необходимости вызывать done, только если вы не нуждаетесь в дальнейшей обработке созданного экземпляра объекта, или в обработке исключений, которые могут произойти (в противном случае они будут потеряны). Кроме того, обратите внимание на то, что функция changeRating заданная в разметке, должна быть глобально видима и маркирована для обработки, иначе экземпляр элемента управления не будет создан.

Альтернативным образом мы можем создать экземпляр элемента управления и настроить его параметры программно. В разметке:

<div id="rating1" data-win-control="WinJS.UI.Rating"></div>

И в коде:

var element = document.getElementById("rating1");
WinJS.UI.process(element);	
element.winControl.averageRating = 3.4;	
element.winControl.userRating = 4;	
element.winControl.onchange = changeRating;

Последние три строки выше, кроме того, могут быть записаны как показано ниже, с использованием метода WinJS.UI.setOptions, но подобный подход не рекомендуется, так как он сложнее для отладки:

var options = { averageRating: 3.4, userRating: 4, onchange: changeRating }; WinJS.UI.setOptions(element.winControl, options);

Так же, мы можем напрямую создать экземпляр элемента, в таком случае разметка неспецифична:

<div id="rating1"></div>

И мы вызываем new в конструкторе самостоятельно:

var newControl = new WinJS.UI.Rating(document.getElementById("rating1"));
newControl.averageRating = 3.4;	
newControl.userRating = 4;	
newControl.onchange = changeRating;

Или, как упомянуто выше, мы можем полностью убрать разметку, позволить конструктору создать элемент для нас (div) и присоединить его к DOM самостоятельно:

var newControl = new WinJS.UI.Rating(null,	
{ averageRating: 3.4, userRating: 4, onchange: changeRating });
newControl.element.id = "rating1";	
document.body.appendChild(newControl.element);
Подсказка. Если вы сталкиваетесь со странными ошибками при создании экземпляров объектов в последних двух случаях, проверьте, не забыли ли вы new и таким образом попытались напрямую вызвать функцию конструктора.

Кроме того, обратите внимание на то, что в последних двух случаях элемент rating1 будет иметь свойство winControl, которое является тем же самым, что и newControl, возвращённое из конструктора.

Для того, чтобы увидеть этот элемент управления в действии, пожалуйста посмотрите пример "HTML-Элемент управления Rating" (http://code.msdn.microsoft.com/windowsapps/Rating-control-sample-4666c750).

Упражнение: элемент управления WinJS.UI.Tooltip (Подсказка)

С большинством других простых элементов управления, а именно, DatePicker, TimePicker и ToggleSwitch, вы можете работать так же, как мы только что работали с Ratings. Все изменения касаются особенностей их свойств и событий. Снова, начните со страницы "Список элементов управления" (http://msdn.microsoft.com/library/windows/apps/hh465453.aspx) и просмотрите информацию по каждому из элементов управления для того, чтобы узнать подробности о них. Кроме того, обратитесь за работающими примерами к примерам "HTML-элементы управления DatePicker и TimePicker" (http://code.msdn.microsoft.com/windowsapps/Date-and-time-picker-sample-0424c7c2) и "HTML-элемент управления ToggleSwitch" (http://code.msdn.microsoft.com/windowsapps/ToggleSwitch-control-sample-84c0aacb).

Элемент управления WinJS.UI.Tooltip, однако, слегка отличается от вышеупомянутых, поэтому я показал особенности его использования. Во-первых, для того, чтобы присоединить подсказку к конкретному элементу, вы можете либо добавить атрибут data-win-control к этому элементу, либо поместить сам элемент внутрь данного элемента управления:

<!-- Прямо присоединяем подсказку к целевому элементу -->
<targetElement data-win-control="WinJS.UI.Tooltip">
</targetElement>

<!-- Размещаем элемент внутри подсказки -->
<span data-win-control="WinJS.UI.Tooltip">
<!--Здесь будет элемент, для которого предназначена подсказка -->
</span>

<div data-win-control="WinJS.UI.Tooltip">
<!--Здесь будет элемент, для которого предназначена подсказка -->
</div>

Во-вторых, свойство contentElement элемента управления-подсказки вполне может содержать имя другого элемента, который будет отображён, когда подсказка будет вызвана. Например, рассмотрим этот фрагмент скрытого HTML в нашей разметке, который содержит другие элементы управления:

<div style="display: none;">
<!--Здесь находится элемент содержимого. Он помещен внутрь скрытого контейнера
в итоге, невидим для пользователя, пока не будет отображён благодаря элементу управления Tooltip.-->
<div id="myContentElement">
<div id="myContentElement_rating">
<div data-win-control="WinJS.UI.Rating" class="win-small movieRating"
data-win-options="{userRating: 3}">
</div>
</div>
<div id="myContentElement_description">
<p> Вы можете разместить любой DOM-элемент в качестве содержимого, 
даже имеющий внутри элементы управления WinJS. 
Элемент управления TootTip сделает родительским объектом для 
размещенного элемента собственный контейнер и 
блокирует события взаимодействия в этом элементе,
 так как не предлагает модель взаимодействия.p>
</div>
<div id="myContentElement_picture">
</div>
</div>
</div>

Мы можем сослаться на это, например, так:

<div data-win-control="WinJS.UI.Tooltip"	
data-win-options="{infotip: true, contentElement: myContentElement}">
<span>My piece of data</span>	
</div>

Когда вы проводите над текстом (мышью, или с помощью жеста на поддерживающем подобную функцию оборудовании), появится подсказка:

Код этого упражнения взят из примера "HTML-элемент управления Tooltip" (http://code.msdn.microsoft.com/windowsapps/Tooltip-control-sample-cb24c2ce), вы можете перейти к этому примеру и самостоятельно посмотреть, как это работает.

Юрий Макушин
Юрий Макушин
Россия, Москва, РЭА им. Плеханова, 2004