Файлы к данной лекции Вы можете скачать здесь.
Элементы управления - это одни из тех вещей, от которых никуда не деться, особенно в нашей культуре, одержимой технологиями, как та, которая окружает многих из нас. Даже не самые высокотехнологичные устройства, вроде велосипедов и разных садоводческих инструментов, имеют элементы управления. Но это не проблема - это реальная необходимость. Элементы управления - это средства, с помощью которых намерения людей переводятся в область механики и электроники и они созданы для того, чтобы приглашать человека к взаимодействию. Когда я это писал, на самом деле, я сидел в самолёте и разглядывал все элементы управления в поле моего зрения. Маленький мальчик, сидевший в следующем ряду, казалось, занят тем же, и большая кнопка "вызвать стюардессу" просто умоляла о том, чтобы её нажали!
Элементы управления, безусловно, необходимы приложениям для Windows 8, и они приглашают пользователей ткнуть в них, зовут прикасаться, нажимать, перетаскивать их. (Они, кроме того, приглашают пользователей, у которых не совсем чистые руки, а так же - пользователей-малышей. Кто-нибудь уже сделал планшетный компьютер, который можно мыть в посудомоечной машине?). Windows 8, конечно, предоставляет богатый набор элементов управления, написанных с использованием HTML, CSS, и JavaScript. Что наиболее заметно в этой связи, так то, что с самых ранних стадий проектирования, Microsoft решила избегать принуждения HTML/JavaScript-разработчиков к использованию элементов управления, которые не соответствовали бы тому, что уже знают эти разработчики, а именно, разработчики могут использовать HTML-элементы управления, такие, как <button>, стилизованные с помощью CSS и привязанные к JavaScript с использованием функций наподобие addEventListener и свойств, таких, как on<event>.
Конечно, вы можете использовать эти внутренние элементы управления HTML в приложениях для Windows 8, так как эти приложения выполняются на базе тех же подсистем рендеринга HTML/CSS, что и в Internet Explorer. Нет проблем. Здесь даже есть специальные классы, псевдо-классы и псевдо-элементы, которые дают вам точные средства настройки стилей, как мы увидим ниже. Но главный вопрос заключался в том, как реализовать элементы управления, специфичные для Windows 8, такие, как тумблеры (toggle switch) и элементы просмотра списков (list view), чтобы позволить вам работать с ними тем же способом - то есть - объявлять их в разметке, настраивать их внешний вид с помощью CSS и привязывать их к JavaScript-сценариям с помощью addEventListener и свойств on<event>.
В результате всего этого вы, разработчики HTML/JavaScript, можете найти всё это в WinJS, без необходимости заглядывать в WinRT. Другими словами, если вы обратили внимание на большой набор API в пространстве имен Windows.UI.Xaml (что составляет около 40% WinRT), знаете что? Вы можете полностью всё это игнорировать! Вместо этого вы будете использовать элементы управления из WinJS, которые поддерживают декларативную разметку, стилизацию с использованием CSS и так далее, что означает, что элементы управления Windows (и элементы управления, созданные разработчиками, следующие той же модели), в конечном счете, отображаются в DOM вместе со всем остальным, что делает их доступными в таком виде, который вы уже знаете и понимаете.
Рассказ об элементах управления в Windows 8, на самом деле, больше, чем одна лекция. Здесь мы рассмотрим, преимущественно, те элементы управления, которые работают с простыми данными (одиночными значениями) и включаются в макет страницы как элементы DOM. Включение в DOM, на самом деле, это причина того, что вы можете стилизовать элементы и управлять ими (в HTML и JavaScript), используя стандартные механизмы, и основная часть этой лекции посвящена показу внешнего вида параметров стилизации, которые вам доступны. В заключительной части данной лекции мы так же рассмотрим соответствующие темы, касающиеся привязки данных: создания взаимодействия между свойствами объектов, содержащих данные, и свойствами элементов управления (в том числе, со стилями). Таким образом, элементы управления отражают то, что происходит с данными.
Рассказ продолжится в лекции 5, "Коллекции и элементы управления для вывода коллекций", где мы посмотрим на элементы управления для работы с коллекциями - те, которые работают с потенциально большими наборами данных - и рассмотрим дополнительные возможности привязки данных, относящиеся к таким элементам управления. Кроме того, мы обратим особое внимание на мультимедиа-элементы (изображения, аудио, видео) в лекции 4 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript", точно названной "Мультимедиа", так как эти элементы управления имеют множество уникальных особенностей. Похожим образом, те элементы, которые используются, преимущественно, для создания макетов (таких, как сеточный (grid) макет или макет на основе гибких окон (flex box)), как описано в лекции 6, "Макет". И мы, кроме того, рассмотрим множество эл ементов пользовательского интерфейса, которые не включаются, как таковые, в макет - такие, как панели приложения (app bars), всплывающие элементы (flyouts), как мы увидим в лекции 1 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript".
Короче говоря, разобравшись с тем, как построить каркас приложения, наладить электропроводку и канализацию в лекции 3, мы готовы к приятным отделочным работам, вроде установки выключателей, дверных ручек и водопроводных кранов - всего того, что делает приложение по-настоящему готовым к тому, чтобы стать частью жизни пользователя.
Прежде чем мы продолжим, вам полезно будет узнать о двух важных материалах в Центре разработчиков Windows, к которым вы, что весьма вероятно, будете периодически обращаться. Первый - это полный список элементов управления (http://msdn.microsoft.com/library/windows/apps/hh465453.aspx), который содержит описания всех элементов управления, которые вам доступны, как мы подытожим позже в данной лекции. Второй - это всесторонний "Указатель рекомендаций по взаимодействию с пользователем" (http://msdn.microsoft.com/library/windows/apps/hh465424#ui_controls), который описывает лучшие подходы к использованию основных элементов управления и сценарии, в которых они не используются. Это очень ценный ресурс и для вас и для вашего дизайнера.
Снова повторюсь, что когда Microsoft создавала среду для разработчиков, мы стремились к высокой степени единообразия между встроенными элементами управления HTML, элементами WinJS и пользовательскими элементами управления. Я называю всё это "элементы управления", так как все они приводят к похожему опыту взаимодействия пользователя и системы: всё это различные визуальные элементы, с помощью которых пользователь взаимодействует с приложением. В этом смысле каждый такой элемент имеет три части:
Стандартные элементы управления HTML, конечно, уже имеют выделенную для них разметку, позволяющую объявлять их, наподобие <button>, <input>, и <progress>. Элементы управления WinJS и пользовательские элементы управления, лишены преимуществ существующих стандартов, их объявляют, используя некие корневые элементы, обычно - <div> или <span>, с двумя особыми атрибутами data-*: data-win-control и data-win-options. Значение data-win-control задаёт полное имя для публичной функции-конструктора, которая создаёт элемент управления в качестве дочернего элемента по отношению к корневому. Второе, data-win-options - это JSON-строка, содержащая пары ключ-значение, разделенные запятыми: { <key1>: <value1>, <key1>: <value2>, ... }.
data-win-options и ваше приложение, при следующем запуске, завершает работу без видимой причины (и не выдавая исключение), проверьте синтаксические ошибки в строке параметров. Например, подобное поведение характерно для случаев, когда забывают закрывающую фигурную скобку }.Функция-конструктор принимает два параметра: корневой элемент (родитель) и объект с параметрами. Удобная команда WinJS.Class.define позволяет создавать подобные функции, делая весьма удобным процесс определения элементов управления (так поступает и WinJS). Конечно, так как атрибуты data-*, в соответствии со спецификацией HTML5, полностью игнорируются подсистемой рендеринга HTML/CSS, нужны дополнительные усилия для того, чтобы превратить элемент с подобными атрибутами в настоящий элемент управления в DOM. И это, как я уже говорил ранее, цель существования методов WinJS.UI.process и WinJS.UI.processAll. Как мы скоро увидим, эти методы обрабатывают атрибуты с параметрами и передают получившийся объект и корневой элемент конструктору функции, заданной в data-win-control.
Результатом этой простой декларативной разметки, дополненной WinJS.UI.process/processAll является то, что элементы управления WinJS и пользовательские элементы управления являются, как и прочие, обычными DOM-элементами. На них могут ссылаться API для обхода DOM, они могут быть целями для стилизации с использованием полного набора CSS-селекторов (как мы увидим в галерее стилей, ниже). Они могут прослушивать внешние события, как и другие элементы и могут вызывать собственные события, реализуя методы [add/remove]EventListener и свойства on<event>. (WinJS, опять же, предоставляет стандартную реализацию addEventListener, removeEventListener и dispatchEvent для этой цели.)
Давайте посмотрим на элементы управления, которые мы можем использовать для Windows 8-приложений, начав с HTML- и WinJS-элементов. В обоих случаях мы посмотрим на их базовый внешний вид, как создаются их экземпляры, рассмотрим параметры, которые можно к ним применить.
Надеюсь, элементы управления HTML не нуждаются в долгих предварительных пояснениях. Они описаны в справочных материалах по HTML5, как в http://www.w3schools.com/tags/default.asp , и показаны в стандартном "светлом" стиле, как на Рис. 4.1 и Рис. 4.2. (В следующем разделе вы узнаете подробности о таблицах стилей WinJS). Важно отметить, что большинство внедренных объектов не поддерживается, за исключением специфических элементов управления ActiveX, смотрите материал "Перенос веб-приложения" (http://msdn.microsoft.com/library/windows/apps/hh465143.aspx).
Создание или создание экземпляров объектов HTML-элементов управления работает так, как вы можете ожидать. Вы можете объявить их в разметке, используя атрибуты для настройки параметров, краткое описание которых дано в таблице, следующей за Рис. 4.2. Вы, кроме того, можете создавать их программно, из JavaScript, вызывая new с соответствующим конструктором, настраивая свойства и прослушивателей событий так, как вам хочется, и добавлять новые элементы в DOM туда, где они нужны. Приложения для Windows 8 не предлагают в этом вопросе ничего нового.
Для того, чтобы увидеть пример создания и использования этих элементов управления, обратитесь к примеру "Основные элементы управления HTML" (http://code.msdn.microsoft.com/windowsapps/Common-HTML-controls-and-09a72a24) из Windows SDK, из которого получены изображения, приведенные на Рис. 4.1 и Рис. 4.2.
(рис 4.1) Стандартные элементы управления HTML5 в стандартном "светлом" стиле (таблица стилей ui-light.css в WinJS)
(рис 4.2) Стандартные элементы управления HTML5 в стандартном "светлом" стиле (таблица стилей ui-light.css в WinJS)
| Элемент управления | Разметка | Обычные параметры | Содержимое элемента (внутренний текст/HTML) |
|---|---|---|---|
| Button (Кнопка) | <button type="button"> | (обратите внимание, что без задания типа (type), тип по умолчанию - "submit") | Текст кнопки |
| Button (Кнопка) | <input type="button"> <input type="submit"> <input type="reset"> | value (текст кнопки) | n/a |
| Checkbox (Флажок) | <input type="checkbox"> | value, checked | n/a (используйте элемент Label (Метка) вокруг флажка для того, чтобы добавить кликабельный текст) |
| Drop Down List (Выпадающий список) | <select> | size="1" (по умолчанию), multiple, selectedIndex | Множество элементов <option> |
| Email (Поле ввода адреса электронной почты) | <input type="email"> | value (начальный текст) | n/a |
| File Upload (Отправка файла) | <input type="file"> | accept (mime-типы), mulitple | n/a |
| Hyperlink (Гиперссылка) | <a> | href, target | Текст ссылки |
| ListBox (Список) | <select> с размером > 1 | size (число больше, чем 1), multiple, selectedIndex | Множество элементов <option> |
| Multi-line Text (Многострочное текстовое представление) | <textarea> | cols, rows, readonly, data-placeholder (так как в placeholder есть ошибка) | Исходное текстовое содержимое |
| Number (Числовое поле ввода) | <input type="number"> | value (Исходный текст) | n/a |
| Password (Поле ввода пароля) | <input type="password"> | value (Исходный текст) | n/a |
| Phone Number (Поле ввода телефонного номера) | <input type="tel"> | value (Исходный текст) | n/a |
| Progress (Индикатор выполнения) | <progress> | value (начальная позиция), max (самая большая позиция; минимум - 0); отсутствие значений делает его неопределенным | n/a |
| Radiobutton (Переключатель) | <input type="radiobutton"> | value, checked, defaultChecked | Метка переключателя |
| Rich Text (Поле ввода форматированного текста) | <div> | contentEditable="true" | HTML-содержимое |
| Slider (Ползунок) | <input type="range"> | min, max, value (initial position), step (increment) | n/a |
| URL (Поле ввода URL-адреса) | <input type="url"> | value (Исходный текст) | n/a |
Два механизма, которые добавляют что-то HTML-элементам управления - это таблицы стилей WinJS и дополнительные методы, свойства и события, которые подсистема рендеринга Microsoft добавляет к большинству HTML-элементов. Этому посвящены два следующих раздела.
WinJS поставляется с двумя параллельно существующими таблицами стилей, которые предоставляют множество стилей по умолчанию и классов стилей для приложений Магазина Windows: ui-light.css и ui-dark.css. Вы всегда будете использовать либо один, либо второй, так как они взаимоисключающи. Первый предназначен для приложений, которые ориентированы на отображение текста, так как темный текст на светлом фоне обычно легче читается (поэтому данная тема часто используется в приложениях чтения новостей, книг, журналов и так далее, в том числе для изображений в публикующихся книгах!). Тёмная тема, с другой стороны, предназначена для приложений, ориентированных на мультимедийные данные, такие, как просмотрщики изображений и видео, где вы хотите обратить внимание пользователя на подобное содержимое.
Обе таблицы стилей задают множество классов стилей win-*, о которых я обычно думаю как о пакетах стилей, которые эффективно добавляют стили и поведение, основанное на CSS (наподобие псевдо-класса :hover), которые превращают стандартные HTML-элементы управления в их вариант, специфичный для Windows 8. Среди них - win-backbutton для кнопок, win-ring, win-medium, и win-large для кольцевых индикаторов выполнения, win-small для элемента управления выставления оценок, win-vertical для вертикального ползунка, и win-textareal для div, поддерживающего правку содержимого. Если вам нужны детали, поищите по их именам на закладке Правила стилей (Style Rules) в Blend.
Как вы уже, наверное, знаете, есть множество разрабатываемых стандартов для 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* всё еще работает).
| Методы | Описание |
|---|---|
msMatchesSelector | Определяет, удовлетворяет ли элемент управления некоторым условиям. |
ms[Set | Get | Release]PointerCapture | Осуществляет захват, восстановление и освобождение указателя для элемента. |
| Свойства стиля (в element.style) | Описание |
msGrid*, msRow* | Получает или задаёт расположение элемента внутри CSS-сетки. |
| События (добавьте "on" для свойств события) | Описание |
mscontentzoom | Вызывается, когда пользователь меняет масштаб элемента (Ctrl+ +/-, Ctrl +колесо мыши), жесты сжатия и расширения. |
msgesture[change | end | hold | tap | pointercapture] | Жест событий ввода (смотрите лекцию 3 курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript"). |
msinertiastart | Жесты событий ввода (смотрите лекцию 3 курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript"). |
mslostpointercapture | Элемент теряет указатель (ранее установленный msSetPointerCapture. |
mspointer[cancel | down | hover | move | out | over | up] | События ввода указателя (смотрите лекцию 3 курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript"). |
msmanipulationstatechanged | Состояние элемента изменилось после манипуляции |
Windows 8 определяет множество элементов управления, которые помогают приложениям удовлетворить требования к дизайну. Как отмечено ранее, они реализованы в WinJS а не в WinRT для приложений, написанных на HTML, CSS и JavaScript. Это позволяет данным элементам управления естественным образом интегрироваться с другими DOM-элементами. Каждый элемент управления определяется как часть пространства имен WinJS.UI с использованием WinJS.Class.define, где имя конструктора совпадает с именем элемента управления. В итоге, полное имя конструктора для элемента управления наподобие Rating (Оценка) выглядит как WinJS.UI.Rating.
Самые простые элементы, которые мы рассмотрим здесь, это DatePicker, Rating, ToggleSwitch, и Tooltip, стиль которых по умолчанию показан на Рис. 4.3.
(рис 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) и следуйте по ссылкам, ведущим к подробным описаниям конкретных элементов.
Полное имя конструктора в 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 объявляются в разметке с атрибутом data-* и их экземпляры не создаются до тех пор, пока не будет вызван WinJS.UI.process(<element>) для отдельного элемента или WinJS.UI.processAll для всех подобных элементов в DOM. Для того, чтобы понять этот процесс, опишем, что делает WinJS.UI.process для отдельного элемента.
data-win-options в объект, содержащий параметры.
data-win-control и вызывает команду new с обнаруженной функцией, передавая сведения о корневом элементе и объекте с параметрами.
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).
WinJS имеет три функции для обхода DOM: WinJS.UI.processAll, WinJS.Binding.processAll (которую мы рассмотрим позже в этой лекции), и WinJS.Resources.processAll (которую мы увидим в лекции 6 Курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой"). Каждая из этих функций ищет специфические атрибуты data-win-* и затем предпринимает дополнительные действия с использованием данного содержимого. Эти действия, однако, могут включать в себя вызов некоторого количества функций различных типов:
Подобные действия представляют риск атак методом внедрения, если функция 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.
Для начинающих, вот разметка для элемента управления 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).
С большинством других простых элементов управления, а именно, 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), вы можете перейти к этому примеру и самостоятельно посмотреть, как это работает.
Прежде чем мы займёмся стилизацией объектов, полезно будет выделить некоторые дополнительные функции в Blend для Visual Studio, которые касаются элементов управления. Как я говорил в Видео 2-2, закладка Активы (Assets) в Blend предоставляет вам быстрый доступ ко всем HTML-элементам и элементам управления WinJS (среди многих других элементов), которые вы можете просто перетащить и поместить на любую страницу, которая отображается на монтажной панели (смотрите Рис. 4.4). Это действие создаст базовую разметку, такую, как div с атрибутом data-win-control для элементов управления WinJS. Затем вы можете перейти на панель Атрибуты HTML (HTML Attributes) (справа) для настройки параметров в разметке (Рис. 4.5).
(рис 4.4) HTML-элементы (слева) и элементы управления WinJS (справа), на закладке Активы (Assets) в Blend
(рис 4.5) Закладка Атрибуты HTML (HTML Attributes) в Blend показывает параметры элемента управления WinJS, их правка повлияет на атрибут data-win-options в разметке
Далее, найдите время для того, чтобы загрузить пример "Основные HTML-элементы управления" (http://code.msdn.microsoft.com/windowsapps/Common-HTML-controls-and-09a72a24) в Blend. Это - отличная возможность для того, чтобы опробовать Интерактивный режим (Interactive mode) Blend, пройдя на страницу и посмотрев на взаимодействие между монтажной панелью и закладкой Динамическая DOM (Live DOM) (Рис. 4.6). Открыв проект, перейдите в интерактивный режим, выбрав в меню Вид > Интерактивный режим (View > Interactive Mode), нажав Ctrl+Shift+I или щёлкнув на крайнюю слева маленькую кнопку в правом верхнем углу монтажной панели. Затем выделите Сценарий 5 (Progress Introduction, Введение в индикаторы выполнения) в выпадающем списке, который откроет страницу, показанную на Рис. 4.6. Затем выйдите из интерактивного режима (с помощью тех же команд) и вы сможете кликать всюду на этой странице. Краткая демонстрация использования интерактивного режима подобным образом дана в файле Video 4-1 среди материалов, которые прилагаются к курсу.
(рис 4.6) Взаимодействие между монтажной панелью и Динамической DOM в Blend
Щелчок по элементу либо в панели динамической DOM, либо на монтажной панели выделяет этот элемент и там, и там. Это сделано для упрощения выделения и стилизации объектов. (Clicking on an element in either live DOM or designer highlight it in the other. This makes for easy selection and styling.)
В примере, посвященном основным HTML-элементам управления, вы увидите, что для встроенных элементов управления в Динамической DOM присутствует лишь единственный элемент, как это и должно быть, в то время как все внутренние детали являются неотъемлемой частью подсистемы рендеринга HTML/CSS. С другой стороны, загрузите вместо этого примера пример "HTML-элемент управления Rating" (http://code.msdn.microsoft.com/windowsapps/Rating-control-sample-4666c750) и разверните элемент div, который содержит один из подобных элементов управления. Здесь вы увидите дополнительные дочерние элементы, которые настраивают этот элемент управления (Рис. 4.7), и вы можете воспользоваться панелями HTML-атрибутов и CSS-свойств, которые находятся справа. Вы можете видеть что-то подобное (с даже большим количеством подробной информации) в Проводнике DOM (DOM Explorer) в Visual Studio, когда приложение исполняется (Рис. 4.8).
(рис 4.7) Развернув структуру элемента управления WinJS в панели Динамическая DOM в Blend, можно увидеть элементы, использованные для его построения
(рис 4.8) Развернув структуру элемента управления WinJS в Проводнике DOM в Visual Studio, так же можно увидеть полные подробности об элементе управления
Сейчас мы подошли к материалу, где мы, преимущественно, будем разглядывать множество красивых картинок: различные способы, которыми могут быть стилизованы элементы управления HTML и WinJS. Как мы уже обсудили, это производится, в любом случае, с помощью CSS, либо в таблице стилей, либо путём присваивания свойств style.*, имея в виду, что приложение имеет полный контроль над внешним видом элементов управления. На самом деле, абсолютно вся разница между HTML-элементами управления в приложениях для Магазина Windows и теми же самыми элементами на веб-страницах, заключается во внешнем виде и только в нём.
Для элементов управления HTML и WinJS, применимы CSS-стандарты, включая псевдо-селекторы наподобие :hover, :active, :checked и так далее, вместе со стилями с префиксами -ms-* для новых стандартов.
Для HTML-элементов управления, кроме того существуют дополнительные -ms-* стили, которые не являются частью CSS3 - для изоляции специфических частей этих элементов управления. То есть, так как составляющие подобных элементов управления не существуют раздельно в DOM, псевдо-селекторы, наподобие ::-ms-check для изоляции значка флажка, или ::-ms-fill-lower для изоляции левой или нижней части ползунка - позволяют вам связать стилизацию с внутренними механизмами системы рендеринга. В противоположность этому, все подобные части элементов управления WinJS могут быть адресованы через DOM, в итоге они просто стилизуются с помощью специальных классов win-*, определенных в таблицах стилей WinJS. То есть, эти элементы управления просто отрисовываются с данными классами стилей. Стили по умолчанию определены в таблицах стилей WinJS, но приложение может переопределить любой аспект этих стилей, приведя элемент управления к любому необходимому вам внешнему виду.
В некоторых случаях, как уже говорилось, определенные win-* классы определяют пакеты стилей для использования с элементами управления HTML, такие, как win-backbutton, win-vertical (для ползунка) и win-ring (для кольцевого индикатора выполнения). Они предназначены для приведения внешнего вида стандартных элементов управления к виду, похожему на вид специальных системных элементов управления.
Кроме того, существует несколько стилей -ms-* общего назначения, которые могут быть применены ко многим элементам управления (и, в целом, к любым элементам), вместе с некоторыми общими win-* классами стилей WinJS. Информация об этом собрана в следующей таблице.
| Стиль или класс | Описание |
|---|---|
-ms-user-select: none | inherit | element | text | auto | Включает или отключает возможность выделения элемента. Установка в none особенно полезна для предотвращения выделения в текстовых элементах. |
-ms-zoom: <percentage> | Оптический зум (увеличение). |
-ms-touch-action: auto | none (и больше) | Производит специальную адаптацию элементов управления для сенсорного взаимодействия, активируя усовершенствованную модель взаимодействия. |
win-interactive | Предотвращает стандартное поведение элементов управления, включенных в элементы FlipView и ListView (смотрите лекцию 5). |
win-swipeable | Устанавливает стили -ms-touch-action для элементов управления внутри ListView, так, что по ним можно провести (для выделения) в одном направлении, что не вызывает прокрутки в другом направлении. |
win-small, win-medium, win-large | Варианты размеров для некоторых элементов управления. |
win-textareal | Устанавливает типичные стили для правки текста. |
Для лучшего изучения вышеупомянутых вопросов, да и других тоже, потратьте некоторое время на следующие материалы: "WinJS CSS-классы для типографики" (http://msdn.microsoft.com/library/windows/apps/hh770582.aspx), "WinJS CSS-классы для HTML-элементов управления" (http://msdn.microsoft.com/en-us/library/windows/apps/Hh770562.aspx), и "CSS-классы для элементов управления WinJS" (http://msdn.microsoft.com/library/windows/apps/hh440966.aspx). Мне так же хочется предоставить вам краткие сведения обо всех других стилях с префиксами поставщиков (или селекторах), которые поддерживаются подсистемой CSS приложений для Магазина Windows. Обратитесь к следующей таблице. Стили с префиксами поставщиков для анимаций, трансформаций и переходов всё еще поддерживаются, хотя они больше не нужны, так как эти стандарты уже приняты. Я составил данный список, так как документация по этим вопросам может быть довольно сложной: вы можете перемещаться по страницам материала "Каскадные таблицы стилей" (http://msdn.microsoft.com/library/windows/apps/Hh996828.aspx) в документах для того, чтобы увидеть, как мало добавлено к CSS, которую вы уже знаете.
| Область | Стили |
|---|---|
| Фоны и границы | -ms-background-position-[x | y] |
| Модель контейнера | -ms-overflow-[x | y] |
| Базовый пользовательский интерфейс | -ms-text-overflow (для вывода многоточий) -ms-user-select (задаёт или возвращает информацию о возможности выделения пользователем текста внутри элемента) -ms-zoom (Оптическое увеличение) |
| Гибкое окно | -ms-[inline-]flexbox (значения для display); -ms-flex и -ms-flex-[align | direction | order | pack | wrap] |
| Градиенты | -ms-[repeating-]linear-gradient, -ms-[repeating-]radial-gradient |
| Сетка | -ms-grid и -ms-grid-[column | column-align | columns | column-span | grid-layer | row | row-align | rows | row-span] |
| Высокая контрастность | -ms-high-contrast-adjust |
| Области | -ms-flow-[from | into] вместе с методом MSRangeCollection |
| Текст | -ms-block-progression, -ms-hyphens и -ms-hypenate-limit-[chars | lines | zone], -ms-text-align-last, -ms-word-break, -ms-word-wrap, -ms-ime-mode, -ms-layout-grid и -ms-layout-grid-[char | line | mode | type], и -ms-text-[autospace | kashida-space | overflow | underline-position] |
| Другое | -ms-writing-mode |
Сейчас пришло время насладиться внешним видом возможностей стилизации приложений для Магазина Winsows. Многое может быть сделано с помощью стандартных стилей, и, затем, можно воспользоваться специальными стилями и классами, как показано в иллюстрациях к этому разделу. Подробности всех этих примеров можно найти в примере "Основные элементы управления HTML" (http://code.msdn.microsoft.com/windowsapps/Common-HTML-controls-and-09a72a24).
Кроме того, взгляните на хороший пример "Применение цветовой темы приложения" (http://code.msdn.microsoft.com/windowsapps/Theme-roller-sample-64b679f2). Этот замечательный пример позволяет вам настроить основной и дополнительный цвета для приложения, увидеть, как эти цвета воздействуют на различные элементы управления, и создаёт около 200 строк отличного CSS-кода, который вы можете скопировать в собственную таблицу стилей. Это сильно поможет вам создавать цветовые темы для приложения, которые поощряются как средства создания индивидуальности приложения в русле рекомендаций по дизайну Windows 8 и позволяют приложению отличаться от системы (Отметим, что элементы управления в элементах интерфейса, предоставляемых системой, наподобие всплывающего элемента для подтверждения при создании дополнительных плиток, будут стилизованы с использованием системных цветов. Приложение не может этим управлять.)
Кнопка (Button)
Текстовая область (Text Area)
Индикатор выполнения (Progress)
Выделение (Select)
Флажок/переключатель (Checkbox/Radiobutton)
Отправка файла (File upload)
Ввод обычного текста (Text input (most forms))
Индикатор выполнения (Progress)
Ввод пароля (Text input (password))
Ползунки (Range/Sliders)
Поле со списком/список (Combo/list box)
Аналогично, здесь показан внешний вид стилизованных элементов управления WinJS, иллюстрации взяты из SDK-примеров "HTML-элементы управления DatePicker и TimePicker" (http://code.msdn.microsoft.com/windowsapps/Date-and-time-picker-sample-0424c7c2), "HTML-элемент управления Rating" (http://code.msdn.microsoft.com/windowsapps/Rating-control-sample-4666c750), "HTML-элемент управления ToggleSwitch" (http://code.msdn.microsoft.com/windowsapps/ToggleSwitch-control-sample-84c0aacb) и "HTML-элемент управления Tooltip" (
http://code.msdn.microsoft.com/windowsapps/Tooltip-control-sample-cb24c2ce).
В случае с элементами управления WinJS DatePicker и TimePicker, применяют стилизацию HTML-элемента select вместе с псевдо-элементами ::-ms-value и ::-ms-expand. Хочу отметить, что примеры нельзя назвать всеобъемлющими, поэтому на иллюстрациях ниже выделены основные моменты:
Элемент управления Rating (Оценки) имеет состояния, которые могут быть стилизованы в дополнение к его звёздочкам и элементу управления в целом. Классы win-* идентифицируют эти индивидуальные особенности. Различные комбинации стилей приведены в таблице
| Класс стиля | Часть |
|---|---|
win-rating | Задаёт стиль всего элемента управления |
win-star | Общий стиль звездочек |
win-empty | Стиль пустых звездочек |
win-full | Стиль заполненных звездочек |
| Классы .win-star | Состояние |
win-average | Управляет отображением средней оценки (пользователь не указал оценку и свойство averageRating не равно нулю) |
win-disabled | Элемент управления заблокирован |
win-tentative | Элемент отображает предварительную оценку |
win-user | Элемент отображает оценку, которую выбрал пользователь |
| Варианты | Классы (селекторы) |
| Среднее количество пустых звезд | .win-star.win-average.win-empty |
| Среднее количество заполненных звезд | .win-star.win-average.win-full |
| Отключить пустые звезды | .win-star.win-disabled.win-empty |
| Отключить заполненные звезды | .win-star.win-disabled.win-full |
| Пустые звезды предварительной оценки | .win-star.win-tentative.win-empty |
| Заполненные звезды предварительной оценки | .win-star.win-tentative.win-full |
| Пустые звезды пользовательской оценки | .win-star.win-user.win-empty |
| Заполненные звезды пользовательской оценки | .win-star.win-user.win-full |
В случае с элементом управления ToggleSwitch (Тумблер), классы win-* идентифицируют его части; состояния задаются неявно. Обратите внимание на то, что часть win-switch - это обычный HTML-ползунок (<input type="range">), поэтому для него вы можете использовать все псевдо-элементы.
И, наконец, элемент управления Tooltip (Подсказка), win-tooltip - это отдельный класс для элементов-подсказок в целом. Элемент может содержать любой другой HTML-код, к которому применяется CSS с использованием обычных селекторов:
У этой подсказки скруглённые углы (This tooltip has rounded corner.)
Эта подсказка овальная (This tooltip is oval.)
<input type="range">) всегда выводит числовые значения; это не средство для отображения других форм текста, такого, как Low, Medium или High. Для реализации чего-то подобного, вы можете рассматривать элемент управления WinJS.UI.Rating с тремя значениями, используя свойство tooltipStrings для настройки подсказки.
::-ms-tooltip для ползунка воздействует только на видимость (с display: none ); он не используется для обычной стилизации подсказки. Он может быть полезен для того, чтобы скрыть подсказку, предоставляемую по умолчанию, если вы хотите реализовать собственный пользовательский интерфейс.
input (различные значения атрибута type ), о которых я не упоминал. Это потому, что данные типы не отличаются специфическим поведением и выводятся как обычное текстовое поле. Те из них, которые определяются особым образом, могут так же выводиться как обычные текстовые поля, но они могут влиять, например, на конфигурацию экранной клавиатуры, которая отображается на сенсорных устройствах (смотрите лекцию 3 Курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript") и обеспечивать особые средства для проверки вводимых данных (например, числовой тип принимает лишь цифры).
data-win-selectable (http://msdn.microsoft.com/library/windows/apps/hh440973.aspx), установленный в true, задаёт возможность выделения элемента таким же образом, как все элементы input и contentEditable .
width и height для элемента управления, попробуйте использовать вместо них style.width и style.height.
<button> и <input type="button">. Выглядят они одинаково, но первая - это блочный тег, который может выводить внутри себя HTML, в то время, как второй - это встроенный тег, который может отображать только текст. button, это имя по умолчанию для <input type="submit">, которая имеет собственную семантику, так что обычно, для уверенности, пользуются <button type="button">.
WinJS.UI.Tooltip подвергается обрезке, вы можете переопределить стиль max-width в классе win-tooltip, который установлен в 30 ем в таблице стилей WinJS. Просмотр стиля на закладке Blend Правила стиля (Style Rules) - это быстрый способ увидеть значения по умолчанию.
meter не поддерживается приложениями для Магазина Windows.
focus в JavaScript). Для того, чтобы отключить этот прямоугольник, выводимый по умолчанию для элементов управления, используйте <selector>:focus { outline: none; } в CSS.
window.getComputedStyle (http://msdn.microsoft.com/library/windows/apps/Hh702516.aspx) для получения объекта currentStyle (http://msdn.microsoft.com/library/windows/apps/hh453398.aspx), который содержит стили, примененные для элемента или для псевдо-элемента. Это весьма полезно, особенно при отладке, так как псевдо-элементы, наподобие ::-ms-thumb для HTML-ползунка, никогда не появляются в DOM, в итоге, их стилизация не доступна ни через свойство элемента style, ни посредством инструментов наподобие Blend. Вот пример получения цвета фона для подсказки (thumb) ползунка:
var styles = window.getComputedStyle(document.getElementById("slider1"), "::-ms-thumb");
styles.getPropertyValue("background-color");Как ни обширен набор элементов управления HTML и WinJS, всегда будет что-то, что вам нужно, но чего система делать не умеет. "Есть ли здесь элемент управления для календаря?", - это вопрос, который мне часто приходится слышать. "А как насчёт элементов для вывода графиков?". Всё это не включено в Windows 8, и несмотря на пожелания обратного, это означает, что вы, или другие разработчики, нуждаются в создании пользовательских элементов управления.
К счастью, всё, что мы уже узнали, особенно об элементах управления WinJS, применимо к пользовательским элементам управления. На самом деле, элементы управления WinJS полностью реализованы с использованием той же модели, которую вы можете использовать напрямую, и если вы посмотрите код WinJS, вы сможете найти множество примеров подобной реализации.
Возвращаясь к уже пройденному материалу, отметим, что элементы управления - это лишь декларативная разметка (создающая элементы в DOM), плюс - применимый CSS, методы, свойства и события, доступные из JavaScript. Для создания подобного элемента управления в модели WinJS, воспользуйтесь следующей стандартной последовательностью шагов:
WinJS.Namespace.define для обеспечения области видимости имён и для того, чтобы не включать дополнительные идентификаторы в глобальное пространство имен. (Не добавляйте элементы управления в пространство имен WinJS). Помните, что вы можете вызывать WinJS.Namespace.define множество раз для добавления новых членов, таким образом, приложение обычно имеет единое пространство имен для всех его пользовательских элементов управления.
WinJS.Class.define (или derive), присваивая возвращаемое значение имени, которое вы хотите использовать в атрибутах data-win-control. Его полное имя будет выглядеть как <namespace>.<constructor>.
<constructor>(element, options)):element равен null или undefined, создайте div для использования вместо него.
element корневым элементом, содержащим элемент управления, убедитесь, что установили element.winControl=this и this.element=element для того, чтобы ваше решение соответствовало шаблону WinJS.WinJS.Class.define, второй аргумент - это объект, который содержит ваши публичные методы и свойства (доступные в созданном экземпляре объекта). Третий аргумент - это объект со статическими методами и свойствами (которые доступны путём обращения к имени класса, без необходимости вызывать new).
WinJS.Class.mix) ваш класс результатами из WinJS.Utilities.createEventProperties(<events>), где <events> это массив имён событий (без префиксов on). Это создаст свойства on<event> в вашем классе для каждого имени из списка.
WinJS.Utilities.eventMixin, которая похожа на описываемую (но без setOptions) и полезна для создания объектов, которые не являются элементами управления, не присутствуют в DOM, но нуждаются в вызове событий. Здесь реализация не принимает участие в восходящей (bubbling) и нисходящей (tunneling) маршрутизации событий DOM.win-* классы для того, чтобы ваши элементы управления соответствовали общему подходу к стилизации.
WinJS.Namespace.define для одного и того же пространства имён аддитивен, таким образом, вы можете заполнить одно пространство имён элементами управления, определенными в различных файлах.Вы можете решить использовать WinJS.UI.Pages, если всё, что вам нужно - это фрагмент HTML/CSS/JavaScript, подходящий для повторного использования, для которого нет особой необходимости во множестве методов, свойств и событий. WinJS.UI.Pages, на самом деле, реализован как пользовательский элемент управления. Точно так же, если то, что вам нужно - это участок HTML, подходящий для повторного использования, с которым вы хотите связать некоторые данные во время выполнения программы, обратитесь к WinJS.Binding.Template, о котором мы поговорим далее. Это не элемент управления - в том виде, как мы его здесь описали - он не поддерживает события, например, но он может быть в точности тем, что вам нужно.
Важно напомнить, что всё в WinJS, вроде WinJS.Class.define и WinJS.UI.DOMEventMixin - это лишь вспомогательные функции для широко распространённых шаблонов. Вам не нужно безальтернативно использовать их, так как в итоге, пользовательский элемент управления - это лишь элемент DOM, такой же, как и все остальные и вы можете создавать такие элементы и управлять ими так, как вы того хотите. Утилиты WinJS лишь ускоряют и упрощают выполнение основных задач.
Примеры пользовательских элементов управления
Вот пара примеров, которые помогут увидеть эти рекомендации в действии. Первый - это то, что Крис Траверс, один из инженеров WinJS, который оказал мне неоценимую помощь с этим курсом, назвал "самым тупым элементом управления, который можно себе представить". Однако, он чётко показывает самые главные структуры:
WinJS.Namespace.define("AppControls", {
HelloControl: WinJS.Class.define(function (element, options) {
element.winControl = this;
this.element = element;
if (options.message) {
element.innerText = options.message;
}
})
});
После того, как элемент определен, вы можете использовать следующую разметку, чтобы WinJS.UI.process/processAll создали экземпляр объекта элемента управления (как встроенного элемента, так как мы использует span в качестве корневого элемента)
<span data-win-control="AppControls.HelloControl" data-win-options="{ message: 'Hello, World'}">
</span>
Обратите внимание на то, что код определения элемента должен быть исполнен перед вызовами WinJS.UI.process/processAll, то есть, чтобы функция конструктора, упомянутая в data-win-control, уже существовала в этот момент.
Для того, чтобы увидеть более полно реализованный элемент управления, вы можете взглянуть на пример "HTML SemanticZoom для пользовательских элементов управления" (http://code.msdn.microsoft.com/windowsapps/SemanticZoom-for-custom-4749edab). Мой друг Кеничиро Танака, из отделения Microsoft в Токио, кроме того, создал элемент управления, показанный на Рис. 4.9 и предоставил упражнение CalendarControl для этой лекции. (Обратите внимание на то, что этот пример лишь частично восприимчив к локализованным параметрам календаря, его не собирались делать полнофункциональным).
Следуя указаниям, данным выше, этот элемент управления определен с использованием WinJS.Class.define внутри пространства имен Control (calendar.js, строки 4-10 показанные здесь [комментарии опущены]).
WinJS.Namespace.define("Controls", {
Calendar : WinJS.Class.define(
function (element, options) {
this.element = element || document.createElement("div");
this.element.className = "control-calendar";
this.element.winControl = this;
Оставшаяся часть конструктора (строки 12 - 63) создают дочерние элементы, которые определяют элемент управления, обеспечивая то, что каждый участок имеет собственное имя класса, будучи помещенным в пространство имен класса control-calendar, помещенного в корневой элемент, что позволяет независимо стилизовать отдельные части. Таблица стилей по умолчанию, применяемая здесь - это calendar.css; некоторые параметры переназначены в default.css, это отличает два элемента управления, приведенных на Рис. 4.9.
(рис 4.9) Внешний вид элемента управления Calendar (календарь)
Внутри конструктора вы, кроме того, можете видеть, что элемент управления подключает собственные обработчики событий для элементов-потомков, таких, как кнопки предыдущий/следующий и каждая ячейка с датой. В последнем случае, щелчок по ячейке использует dispatchEvent для того, чтобы вызвать событие элемента управления dateselected.
Строки 63 - 127 определяют членов элемента управления. Здесь присутствуют два внутренних метода, _setClass и _update, за которыми следуют два публичных метода nextMonth и prevMonth, и три публичных свойства - year, month и date. Эти свойства можно установить посредством строки data-win-options в разметке, или напрямую через объект элемента управления, как мы скоро увидим.
В конце calendar.js вы можете увидеть два вызова WinJS.Class.mix для добавления свойств для событий (здесь лишь одно свойство), и методов стандартных событий DOM, таких, как addEventListener, removeEventListener и dispatchEvent вместе с setOptions:
WinJS.Class.mix(Controls.Calendar, WinJS.Utilities.createEventProperties("dateselected"));
WinJS.Class.mix(Controls.Calendar, WinJS.UI.DOMEventMixin);
Очень хорошо то, что всё это весьма просто добавлять. WinJS, спасибо!WinJS.Class.mix принимает различное число аргументов, таким образом вы вполне можете объединить два вышеприведенных вызова в один.
Между calendar.js и calendar.css у нас есть объявление элемента управления. В default.html и default.js мы можем увидеть, как используется элемент управления. На Рис. 4.9 элемент управления слева объявлен в разметке и его экземпляр создан благодаря вызову WinJS.UI.processAll в default.js.
<div id="calendar1" class="control-calendar" aria-label="Calendar 1"
data-win-control="Controls.Calendar"
data-win-options="{ year: 2012, month: 5, ondateselected: CalendarDemo.dateselected}">
</div>
Вы можете видеть, как мы используем полное имя конструктора, так же, как и обработчика событий, который мы назначаем событию ondateselected. Но помните, что функции, на которые мы ссылаемся в разметке, как на эти, должны быть маркированы для строгой обработки. Конструктор автоматически маркирован с помощью WinJS.Class.Define, но обработчик события требует дополнительных усилий: мы помещаем функцию в пространство имён (для того, чтобы обеспечить её глобальную видимость) и используем WinJS.UI.eventHandler для того, чтобы выполнить маркировку:
WinJS.Namespace.define("CalendarDemo", {
dateselected: WinJS.UI.eventHandler(function (e) {
document.getElementById("message").innerText = JSON.stringify(e.detail) + " selected";
})
});
Опять же, если вы забыли маркировать функцию подобным образом, экземпляр элемента управления не будет создан. (Уберите контейнер WinJS.UI.eventHandler для того, чтобы это увидеть).
Для демонстрации создания элемента управления за пределами разметки, элемент управления справа на Рис. 4.9 создан так, как показано ниже в div-элементе calendar2:
//Как только мы создали данный элемент управления в коде, мы не зависим от WinJS.UI.processAll.
var element = document.getElementById("calendar2");
//Как только мы предоставили элемент, он будет автоматически добавлен в DOM
var calendar2 = new Controls.Calendar(element);
//Так как этот обработчик не участвует в процессе обработки разметки,
calendar2.ondateselected = function (e) {
document.getElementById("message").innerText = JSON.stringify(e.detail) + " selected";
}
Всё готово!
Blend - это отличный дизайнерский инструмент, который позволяет работать с элементами управления напрямую, в монтажной панели, поэтому вам может быть интересно, как пользовательские элементы управления могут участвовать в подобном.
Для начала, так как пользовательские элементы управления это лишь элементы в DOM, Blend работает с ними так же, как и с другими частями DOM. Попытайтесь загрузить пример с календарём в Blend для того, чтобы увидеть это своими глазами.
Далее, элементы управления могут определять, исполняются ли они внутри Blend, в режиме дизайна, если свойство Windows.ApplicationModel.DesignMode.designModeEnabled установлено в true. Один из случаев, когда это весьма полезно, заключается в обработке строк ресурсов. Мы не будем подробно говорить о ресурсах до лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", но это важно знать уже сейчас, что разрешение ресурсов посредством Windows.ApplicationModel.Resources.ResourceLoader, не работает в режиме дизайна в Blend, как это происходит при реальном исполнении приложения. Система напрямую заявляет об этом, выдавая исключение! Таким образом, вы можете использовать в режиме дизайна флаг для того, чтобы предоставить некоторые данные по умолчанию, вместо того, чтобы выполнять поиск данных.
Например, один из первых партнёров, с которым я работал, имел метод для извлечения локализованного URI со своего сервиса поддержки приложения, который не работал в режиме дизайна. Используя флаг режима дизайна, таким образом, мы просто изменили код, приведя его к такому виду:
WinJS.Namespace.define("App.Localization", {
getBaseUri: function () {
if (Windows.ApplicationModel.DesignMode.designModeEnabled) {
return "www.default-base-service.com";
} else {
var resources = new Windows.ApplicationModel.Resources.ResourceLoader();
var baseUri = resources.getString("baseUrl");
return baseUri;
}
}
});
И, наконец, возможно сделать так, чтобы пользовательские элементы управления отображались на закладке Активы (Assets) среди прочих HTML-элементов и элементов управления WinJS. Для того, чтобы это сделать, вам, во-первых, нужен файл OpenAjax Metadata XML (OAM) (http://www.openajax.org/member/wiki/OpenAjax_Metadata_1.0_Specification_Descriptive), который предоставляет всю необходимую информацию для элемента управления, и у вас уже есть достаточно мест, где можно посмотреть подобные файлы. Для того, чтобы их найти, выполните поиск файлов по запросу *._oam.xml в папке Program Files (x86). Вы найдёте кое-что в папке Microsoft Visual Studio 11.0 и в папках с большей глубиной вложенности, в Microsoft SDKs, где располагаются метаданные WinJS. И там и там вы, кроме того, найдёте множество примеров значков размерами 12х12 и 16х16, которые понадобятся для вашего элемента управления.
Если вы посмотрите в папку controls/calendar упражнения CalendarControl к этой лекции, вы найдёте файл calendar_oam.xml и два значка вместе с файлами .js и .css. OAM-файл (который должен иметь окончание имени файла в виде _oam.xml) сообщает Blend о том, как сохранять элемент управления в его панели Активы (Assets), и какой код следует вставить, когда вы перетаскиваете элемент в HTML-файл. Вот содержимое этого файла:
<?xml version="1.0" encoding="utf-8"?>
<!--Используйте символы подчеркивания или точки в id и name, но не пробелы. -->
<widget version="1.0"
spec="1.0"
id="http://www.kraigbrockschmidt.com/scehmas/ProgrammingWin8_JS/Controls/Calendar"
name="ProgWin8_JS.Controls.Calendar"
xmlns="http://openajax.org/metadata">
<author name="Kenichiro Tanaka" />
<!-- title предоставляет имя, которое появляется в панели Активы (Assets) в Blend
(иначе используется widget.name). -->
<title type="text/plain"><![CDATA[Calendar Control]]></title>
<!--обеспечивает подсказку для панели Активы (Assets). -->
<description type="text/plain"><![CDATA[A single month calendar]]></description>
<!-- значок (12x12 и 16x16) предоставляет маленький значок
в панели Активы (Assets). -->
<icons>
<icon src="calendar.16x16.png" width="16" height="16" />
<icon src="calendar.12x12.png" width="12" height="12" />
</icons>
<!-- Этот элемент описывает то, что будет вставлено в .html файл;
закомментируйте всё лишнее -->
<requires>
<!-- Код элемента управления -->
<require type="javascript" src="calendar.js" />
<!-- Код таблицы стилей элемента -->
<require type="css" src="calendar.css" />
<!-- Встроенные скрипты для заголовка документа -->
<require type="javascript"><![CDATA[WinJS.UI.processAll();]]></require>
<!-- Встроенный CSS для раздела стилей в заголовке документа -->
<!--<require type="css"><![CDATA[.control-calendar{}]]></require>-->
</requires>
<!-- Что вставлять в тело элемента управления; убедитесь в том, что это корректный HTML
иначе Blend не позволит вставить его -->
<content>
<![CDATA[
<div class="control-calendar" data-win-control="Controls.Calendar"
data-win-options="{ year: 2012, month: 6 }"></div>
]]>
</content>
</widget>
Когда вы добавите все пять файлов в проект в Blend, вы увидите значок и подпись элемента управления в панели Активы (Assets) (а если поместить указатель мыши над элементом, появится всплывающая подсказка):
Если вы перетащите этот элемент управления на HTML-страницу, вы увидите, как в её различные части добавлено следующее:
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
<script src="calendar.js" type="text/javascript"></script>
<link href="calendar.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="control-calendar" data-win-control="Controls.Calendar" data-win-options="{month:6, year:2012}"></div>
</body>
</html>
Но подождите! Что случилось с вызовом WinJS.UI.processAll(), который отмечен в XML тегом script в заголовке? Blend проверил этот участок кода на предмет того, встречается ли подобный вызов в уже загруженных скриптах. Если это так (как обычно бывает с шаблонами проектов), Blend не станет повторять его. Если он сочтёт нужным включить данный вызов, или если вы зададите здесь другой код, Blend вставит его в тег <script> в заголовке.
Кроме того, ошибки в вашем OAM-файле принудят Blend не вставлять элемент управления, таким образом, вам нужно исправить эти ошибки. Когда вы вносите изменения, Blend не перезагружает метаданные до тех пор, пока вы не перезагрузите проект или не переименуете OAM-файл (сохранив часть _oam.xml). Я нахожу последнее более простым, так как blend не заботит то, как выглядит остальная часть имени файла. В процессе переименования, так же, если вы обнаружите, что элемент управления исчез из панели Активы (Assets), это означает, что имеются ошибки в самой структуре OAM XML, такие, как неподходящие символы в значениях атрибутов. В подобных случаях вам понадобится метод проб и ошибок, и, конечно, вы можете обратиться к уже существующих на вашем компьютере OAM-файлам для того, чтобы разобраться с подробностями.
Кроме того, вы можете сделать ваш элемент управления доступным для всех проектов в Blend. Для того, чтобы сделать это, перейдите в папку Program Files (x86)\Microsoft Visual Studio 11.0\Blend, создайте папку Addins, если её там еще нет, создайте в ней подпапку для вашего элемента управления (используя подходящее уникальное имя), и скопируйте все активы вашего элемента управления туда. Когда вы перезапустите Blend, вы увидите, что ваш элемент управления находится в списке в категории Add-ins в панели Активы (Assets):
Подобным подходом можно воспользоваться, если вы создаете элементы управления для использования их другими разработчиками. Вашей программе установки следует просто расположить ваши активы в папку Addins. Что касается использования подобных элементов управления, когда вы перетаскиваете элемент в HTML-файл, необходимо, чтобы активы, необходимые элементу управления (но не значки и не OAM-файл), были скопированы в корневую папку проекта. Затем вы можете перемещать их так, как вам нужно, меняя ссылки на файлы, конечно.
Как я упоминал во введении к этой лекции, вопросы привязки данных тесно связаны с элементами управления, так как они касаются создания взаимоотношений между свойствами объектов, хранящих данные, и свойствами элементов управления (и стилями в том числе). В этом смысле, элементы управления отражают то, что происходит с данными, а часто это именно то, что разработчик хочет предоставить пользователю приложения.
Я хочу начать этот разговор с общего обзора привязки данных, так как вы можете быть знакомы с этой концепцией в целом, но не понимать достаточно чётко отдельных деталей. Время от времени, на самом деле, особенно когда говоришь с кем-то, кто работал с этим несколько лет, привязка данных кажется окутанной какой-то непроницаемой загадочностью. Я не отношу себя к таким посвященным, поэтому попытаюсь выразить основные понятия обычными словами.
Основная идея привязки данных (data binding) заключается в соединении вместе или "привязывании" свойств двух различных объектов, обычно объекта данных (контекста) и объекта пользовательского интерфейса, о которых обычно говорят как об объекте-источнике и об объекте-приемнике (целевом объекте). Ключевой момент здесь заключается в том, что привязка данных обычно осуществляется для свойств, но не для объектов.
Привязка, так же, может привлекать конверсию значений из одного типа в другой, такую, как конверсию набора различных свойств из источника в единую строку, подходящую для приёмника. Кроме того, к одному и тому же источнику можно прикрепить несколько целевых объектов, или один объект-приёмник соединить с несколькими источниками данных. Эта гибкость может привести к тому, что привязка данных может выглядеть несколько туманным предметом для обсуждений, так как она включает в себя огромное количество возможностей! Тем не менее, в большинстве случаев мы можем обойтись достаточно простыми решениями.
Обычный сценарий привязки данных показан на Рис. 4.10, где у нас есть свойства дух элементов пользовательского интерфейса - span и img, привязанные к свойствам объекта, содержащего данные. Таким образом, здесь три связи: (1) свойство span.innerText привязано к свойству source.name; (2) свойство img.src привязано к свойству source.photoUrl; и (3) свойство span.style.color связано с выходом функции преобразования данных, которая преобразует свойство source.userType в цвет.
(рис 4.10) Обычный сценарий привязки данных между объектом-источником данных и двумя целевыми объектами пользовательского интерфейса, включая две прямых связи и одну связь с участием функции преобразования данных
Как эти связи ведут себя во время выполнения программы, зависит от направления (direction) каждой из связей, которое может быть одним из следующих:
Объект данных (Data Object)
Элемент управления пользовательского интерфейса (UI/Control)
Значение элемента (Item Value)
Инициализация (Initialization)
Нет непрерывного соединения (No ongoing connection)
Объект данных (Data Object)
Элемент управления пользовательского интерфейса (UI/Control)
Значение элемента (Item Value)
Инициализация (Initialization)
Изменения в данных находят отнажение в пользовательском интерфейсе (Changes to data are reflected in UI)
Объект данных (Data Object)
Элемент управления пользовательского интерфейса (UI/Control)
Значение элемента (Item Value)
Инициализация (Initialization)
Изменения в любом месте отражаются и в другом (Changes in either place are reflected in the other)
Сейчас, когда мы узнали о том, что такое привязка данных, мы можем посмотреть, как она может быть реализована в приложениях для Windows 8. Если хотите, вы можете создать любую схему для привязки данных или использовать библиотеку JavaScript стороннего разработчика для работы: всё это, в конечном счете, касается связи свойств объекта-источника данных со свойствами объекта-приёмника.
Если вы похожи на моих предков по отцовской линии, которые не полагались ни на кого, кроме себя, и делали всё сами (бурили скважины, добывали уголь и делали запасные части для машин), вы будете довольны, разрабатывая собственное решение для привязки данных. Но если вы более умеренны по своей природе, как я (благодаря материнской линии), вам понравится, как и мне, если кто-то достаточно умный выполнит какую-то работу за вас. Я благодарен команде разработки WinJS, которая, зная основные нужды привязки данных, создала API WinJS.Binding. Оно поддерживает и единовременную, и одностороннюю привязку, как декларативно, так и программно, вместе с функциями преобразования значений. Сейчас WinJS не предоставляет инструментов для двусторонней привязки, но подобные структуры несложно создать в коде.
Внутри структур WinJS, множественные целевые элементы могут быть привязаны к единственному источнику данных. WinJS.Binding, на самом деле, предоставляет то, что называется шаблонами (templates), обычно - коллекции целевых элементов, которые привязаны к одному и тому же источнику данных. Хотя мы не рекомендуем это делать, однако, можно привязать один целевой элемент к нескольким источникам, но такой конструкцией сложно управлять. Лучший подход в подобной ситуации заключается в том, чтобы заключить разные источники данных в оболочку одного объекта и привязать целевой объект именно к нему.
Лучший способ понять WinJS.Binding - посмотреть, для начала, на то, как мы пишем собственный код для связывания и потом изучить решение, которое предлагает WinJS. Для этого примера мы использует тот же самый сценарий, который показан на Рис. 4.10, где мы имеем объект-источник, привязанный к двум отдельным элементам пользовательского интерфейса, с одним конвертером, который превращает одно из свойств источника в цвет.
Единовременная привязка, как упоминалось выше, это то, что вы делаете, просто присваивая значения свойствам элемента. Вот HTML-код для этого примера:
<!-- Разметка: элемент пользовательского интерфейса, который мы привяжем к объекту-источнику данных --> <section id="loginDisplay1"> <p>You are logged in as <span id="loginName1"></span></p> <img id="photo1"></img> </section>
И объект-источник данных:
var login1 = { name: "liam", id: "12345678",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png", userType: "kid"};
Мы можем связать их следующим образом, использовав, кроме того, функцию-конвертер:
//"Связывание" одного свойства за один раз, с функцией-конвертером, вызываемой напрямую
var name = document.getElementById("loginName1");
name.innerText = login1.name;
name.style.color = userTypeToColor1(login1.userType);
document.getElementById("photo1").src = login1.photoURL;
function userTypeToColor1(type) {
return type == "kid" ? "Orange" : "Black";
}
В итоге мы получаем следующий результат, в котором я бессовестно опубликовал фото моего ребенка, когда он был еще совсем малышом:
Этот код можно найти в Test 1, в упражнении BindingTests для этой лекции. С помощью WinJS мы можем выполнить то же самое, используя декларативный синтаксис и функцию обработки. В разметке, мы используем атрибут data-win-bind для того, чтобы отобразить целевые свойства содержащего их элемента на свойства объекта-источника, который передан функции обработки WinJS.Process.All.
Значение data-win-bind - это строка с парами свойств. Синтаксис каждой пары выглядит как <target property> : <source property> [<converter>], где converter необязателен. Каждый идентификатор свойства может использовать запись с помощью точек, если нужно, и пары свойств разделены двоеточиями, как показано в HTML:
<section id="loginDisplay2"> <p>You are logged in as <span id="loginName2" data-win-bind="innerText: name; style.color: userType Tests.userTypeToColor"> </span> </p> <img id="photo2" data-win-bind="src: photoURL"/> </section>
Обратите внимание на то, что обращение к массиву свойств источника с использованием [ ] не поддерживается, хотя конвертер может это сделать. В случае с целью, если этот объект имеет JavaScript-свойство, на который вы хотите сослаться, используя идентификатор, написанный через дефис, вы можете использовать следующий синтаксис:
<span data-win-bind="this['funky-property']: source"></span>
Похожий синтаксис нужен для атрибутов цели привязки данных, таких, как атрибуты aria-* для реализации специальных возможностей. Так как это не свойства JavaScript, на которые можно сослаться, используя идентификатор с дефисами, необходим специальный конвертер (или инициализатор, как его лучше называть), который называется WinJS.Binding.setAttribute.
<label data-win-bind="this['aria-label']: title WinJS.Binding.setAttribute"></label>
Кроме того, обратите внимание на WinJS.Binding.setAttributeOneTime для единовременной привязки атрибутов.
В любом случае, в итоге мы получаем такой же источник данных, как раньше:
var login2 = { name: "liamb", id: "12345678",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png", userType: "kid"};
Мы превращаем разметку в реальные связи, используя WinJS.Binding.processAll.
//processAll сканирует дерево элементов в поисках data-win-bind, используя заданный объект как контекст данных
WinJS.Binding.processAll(document.getElementById("loginDisplay2"), login2);
Этот код, Test 2 в упражнении, производит тот же результат, что и Test1. Единственное, что нужно добавить - это объявление функции конвертера, которая должна быть глобально доступной и маркирована для обработки. Это можно сделать с помощью пространства имен, которое содержит функцию (опять же, её называют инициализатором, как мы обсудим в разделе "Инициализаторы привязки" ближе к концу этой лекции), созданную с помощью WinJS.Binding.converter:
//Используем пространство имен для экспорта функции из текущего модуля,
//таким образом WinJS.Binding может найти её
WinJS.Namespace.define("Tests", {
userTypeToColor: WinJS.Binding.converter(function (type) {
return type == "kid" ? "Orange" : "Black";
})
});
Как и в случае с конструктором элементов управления WinJS.Class.define, WinJS.Binding.converter автоматически маркирует функцию, которую он возвращает, как безопасную для обработки.
Кроме того, мы можем поместить объект-источник данных и применимый конвертер в одно и то же пространство имен.login и фукнцию userTypeToColor в пространство имен LoginData, в итоге, разметка и код будут выглядеть так:
<span id="loginName3"
data-win-bind="innerText: name; style.color: userType LoginData.userTypeToColor">
</span>
WinJS.Binding.processAll(document.getElementById("loginDisplay3"), LoginData.login);
WinJS.Namespace.define("LoginData", {
login : {
name: "liamb", id: "12345678",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png",
userType: "kid"
},
userTypeToColor: WinJS.Binding.converter(function (type) {
return type == "kid" ? "Orange" : "Black";
})
});
В итоге, для единовременной привязки данных, WinJS.Binding просто даёт вам декларативный синтаксис, который выполняет в точности то, что вы можете сделать самостоятельно в коде, но делает это в немного меньшем объеме кода. Так как это - лишь разметка и функция для обработки, здесь нет ничего волшебного, хотя подобные полезные утилиты по-своему волшебны! На самом деле, код здесь реализует лишь единовременную привязку, без вызова источником данных каких-либо событий при изменении данных. Мы увидим, как сделать это с помощью WinJS.Binding.as совсем скоро, после дополнительной пары замечаний.
Во-первых, WinJS.Binding.processAll, на самом деле, асинхронная функция, которая возвращает promise-объект. Любой обработчик завершения, переданный его методу done, будет вызван при завершении обработки, если у вас есть дополнительный код, который основан на данном состоянии. Во-вторых, вы можете вызвать WinJS.Binding.processAll больше, чем один раз для того же самого целевого элемента, каждый раз задавая различные объекты-источники (контексты данных). Это не заменит существующие привязки, учтите - лишь добавит новые, подразумевая, что вы хотите привязать то же самое целевое свойство к более, чем одному источнику, что, в итоге, приведет к большому беспорядку. Итак, снова, лучше всего собрать эти источники в один объект и привязать целевой объект к нему, используя запись с точкой для идентификации вложенных свойств.
Когда вы задаете в качестве целевых свойства элементов управления WinJS, а не корневого (содержащего их) элемента, имена целевых свойств должны начинаться с winControl. В противном случае вы осуществите привязку к несуществующим свойствам корневого элемента. При использовании winControl, привязываемое свойство служит той же цели, что и задаваемое фиксированное значение в data-win-options. Например, разметка, использованная выше в разделе "Пример: Элемент управления WinJS.UI.Rating (Оценки)" может использовать привязку данных для своих свойств averageRating и userRating, как показано ниже (подразумевая, что myData - это подходящий источник данных):
<div id="rating1" data-win-control="WinJS.UI.Rating"
data-win-options="{onchange: changeRating}"
data-win-bind="{winControl.averageRating: myData.average,
winControl.userRating: myData.rating}">
</div>
Повторюсь, что цель односторонней привязки - это обновление целевого свойства, обычно в элементе управления пользовательского интерфейса, когда изменяются данные в присоединенном свойстве источника данных. Таким образом, односторонняя привязка подразумевает эффективное повторение единовременной привязки каждый раз, когда состояние источника меняется.
В коде, который мы видели выше, если мы меняем login.name после вызова WinJS.Binding.processAll, с элементом управления, выводящим данные, ничего не происходит. Как же нам автоматически обновить выводимую информацию?
Вообще говоря, это требует, чтобы источник данных поддерживал список привязок (bindings), где каждая привязка может описывать свойство источника, свойство целевого объекта и функцию-конвертер. Источник данных, кроме того, нуждается в методах для управления этим списком, наподобие addBinding, removeBinding и так далее. И третье, если привязываемое (или наблюдаемое (observable)) свойство меняется, нужно пройтись по списку привязок и обновить соответствующим образом любые связанные свойства целевых объектов.
Эти требования выглядят достаточно обобщенными. Вы можете представить, что их реализация будет в значительной мере походить на классический шаблонный код. Поэтому, конечно, WinJS предоставляет такую реализацию! В этом контексте источники называются наблюдаемыми объектами (observable objects) и функция WinJS.Binding.as заключает любые объекты в объекты подобной структуры (она возвращает пустой оператор для необъектных сущностей). Наоборот, функция WinJS.Binding.unwrap удаляет эти структуры, если в том есть необходимость. Более того, функция WinJS.Binding.define создаёт конструктор для наблюдаемых объектов вокруг набора свойств (описанных с помощью чего-то вроде пустого объекта, содержащего лишь имена свойств). Подобный конструктор позволяет вам создать экземпляр объекта-источника динамически, как при обработке данных, полученных от онлайнового сервиса.
Давайте посмотрим на код. Вернемся к последнему примеру выше (Test 3), в любое время до или после WinJS.Binding.processAll мы можем взять объект LoginData.Login и сделать его наблюдаемым, как показано ниже:
var loginObservable = WinJS.Binding.as(LoginData.login)
На самом деле, это всё, что нам надо сделать - всё остальное будет таким же, как раньше. Мы можем сейчас изменить связанное значение в объекте loginObservable:
loginObservable.name = "liambro";
Эта команда обновит целевое свойство:
Вот как мы затем создаём и используем многократно используемый класс для наблюдаемого объекта (Test 4 в упражнении BindingTest). Обратите внимание на то, что объект, который мы передаем WinJS.Binding.define содержит имена свойств, но не значения (они будут проигнорированы):
WinJS.Namespace.define("LoginData", {
//...
//LoginClass становится конструктором для прикрепляемого объекта с заданными свойствами
LoginClass: WinJS.Binding.define({name: "", id: "", photoURL: "", userType: "" }),
});
Когда это готово, мы можем создавать экземпляры этого класса, инициализировать необходимые свойства. В данном упражнении, мы используем различные изображения и инициализированный ведущий userType:
var login4 = new LoginData.LoginClass({ name: "liamb",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam08.jpg" });
Осуществляя привязку к этому объекту login, мы видим, что имя пользователя первоначально имеет черный цвет:
//Выполняем связывание
(первоначально имя пользователя выведено черным цветом)
WinJS.Binding.processAll(document.getElementById("loginDisplay"), login4);
Обновление свойства userType в источнике (как показано ниже), приведет к обновлению цвета целевого свойства, что, посредством конвертера, происходит автоматически:
login4.userType = "kid";
Процесс реализации двусторонней привязки достаточно очевиден:
Источник данных должен быть достаточно интеллектуальным для того, чтобы знать, когда новое значение свойства уже является тем же, что и в целевом свойстве, в таком случае он не попытается обновить целевое свойство. Иначе вы попадёте в бесконечный цикл. Код наблюдаемого объекта, который предоставляет WinJS, уже выполняет такую проверку.
Для того, чтобы увидеть пример реализации подобного механизма, обратитесь к примеру "Декларативная привязка" (http://code.msdn.microsoft.com/windowsapps/DeclarativeBinding-bfcb42a5) в SDK, где показано прослушивание события change в текстовых полях и соответствующее обновление свойств в их источниках данных.
Если вы посмотрите на описание WinJS.Binding (http://msdn.microsoft.com/library/windows/apps/br229775.aspx) в документации, вы увидите в этом пространстве имен множество других полезных вещей. Позвольте мне кратко описать их особенности. (Кроме того, обратитесь к примеру "Программная привязка данных" (http://code.msdn.microsoft.com/windowsapps/ProgrammaticBinding-de038b64) для демонстрации некоторых возможностей).
Если вы уже определили класс (из WinJS.Class.define) и хотите сделать его наблюдаемым, используйте WinJS.Class.mix, как показано ниже:
var MyObservableClass = WinJS.Class.mix(MyClass, WinJS.Binding.mixin, WinJS.Binding.expandProperties(MyClass));
WinJS.Binding.mixin содержит стандартную реализацию функций связывания, которые ожидает WinJS. WinJS.Binding.expandProperties создаёт объект, свойства которого соответствуют свойствам данного объекта (те же самые имена), каждое из которых заключено в подходящую для связывания структуру. На самом деле, этот тип операции полезен только тогда, когда выполняют смешивание, и это то, что WinJS.Binding.define делает с теми самыми странными, не имеющими значений объектами, которые мы передаем ему.
Если вы помните из предыдущего материала, одно из требований для наблюдаемых объектов заключается в том, чтобы они содержали методы для управления списком привязок. Реализация подобных методов содержится в объекте WinJS.Binding.observableMixin. Вот эти методы:
bind Сохраняет привязку (имя свойства и функцию для запуска при изменении).
unbind Удаляет привязку, созданную методом bind.
notify Просматривает привязку на предмет свойства и запускает функции, связанные с ним. Это то место, где WinJS проверяет, различаются ли старое и новое значения и где он обрабатывает случаи, когда обновление для того же самого целевого объекта уже выполняется.На это опирается еще один mixin-объект, WinJS.Binding.dynamicObservableMixin (то же, что и WinJS.Binding.mixin), который добавляет методы для управления свойствами объекта-источника:
setProperty Обновляет значение свойства и оповещает прослушивателей о том, что значение изменилось.
updateProperty Похож на setProperty, но возвращает promise-объект, который возвращает результат, когда все прослушиватели будут оповещены (ожидаемый результат - новое значение свойства).
getProperty Возвращает значение свойства в качестве наблюдаемого объекта, что делает возможным осуществлять привязку внутри вложенных объектных структур (obj1.obj2.prop3 и так далее).
addProperty Добавляет новое свойство к объекту, которое автоматически подготовлено к привязке данных.
removeProperty Удаляет свойство из объекта.Зачем нам всё это? Это открывает простор для творчества. Вы можете вызвать WinJS.Binding.bind, например, напрямую для любого наблюдаемого источника, когда вы хотите использовать другую функцию для свойства источника. Это похоже на добавление прослушивателей событий для изменения свойства источника, и у вас может быть столько прослушивателей, сколько хотите. Это полезно для настройки двунаправленной привязки данных, и эта возможность не связана с манипуляцией пользовательским интерфейсом. Функция просто вызывается при изменении свойства. Это можно использовать для автоматической синхронизации серверной службы с объектом данных.
Пример "Декларативная привязка данных" (http://code.msdn.microsoft.com/windowsapps/DeclarativeBinding-bfcb42a5), кроме того, показывает вызов bind с объектом в качестве второго параметра, форма, которая позволяет осуществлять привязку к вложенным членам источника данных. Синтаксис похож на этот bind(rootObject, { property: { sub-property: function(value) { ... } } } - что соответствует объекту-источнику. С подобным объектом в качестве второго параметра, bind убеждается в том, что активированы все функции, назначенные вложенным свойствам. В подобном случае значение, возвращаемое bind - это объект с методом cancel, который очистит эту сложную привязку данных.
Метод notify, в свою очередь, это то, что вы можете вызывать напрямую для инициации уведомлений. Это полезно с дополнительными привязками, которые не обязательно зависят от собственных значений, а только от факта собственного изменения. Основной вариант использования - реализация вычисляемых свойств - тех, которые изменяются в ответ на изменение значений других свойств.
WinJS.Binding, кроме того, имеет несколько интеллектуальных обработчиков множественных изменений одного и того же свойства объекта-источника. После первоначального связывания, дальнейшие уведомления об изменениях асинхронны, и множественные запросы на изменение того же самого свойства объединяются. В итоге, если в нашем примере мы выполним несколько изменений свойства name в быстрой последовательности:
login.name = "Kenichiro"; login.name = "Josh"; login.name = "Chris";
будет отправлено только одно оповещение о последнем значении, которое и станет значением, которое будет отображено на связанном целевом элементе.
И, наконец, имеется еще несколько функций, находящихся в WinJS.Binding:
onetime Функция, которая проходится по заданным целевым (принимающим) свойствам и устанавливает их в значение связанного свойства источника. Эту функцию можно использовать для настоящей единовременной привязки данных, как необходимо при привязке к объектам WinRT. Она так же может быть использован напрямую, как инициализатор, внутри data-win-bind, если источником данных является WinRT-объекта.
defaultBind Функция, которая делает то же самое, что и onetime, но устанавливает одностороннюю привязку между всеми заданными свойствами. Она так же служит как инициализатор по умолчанию для всех взаимодействий в data-win-control, когда специальный инициализатор не задан.
declarativeBind Реальная реализация processAll. (Они идентичны). В дополнение к обычным параметрам (корневой целевой элемент и контекст данных), она так же принимает параметр skipRoot (если он установлен в true, в процессе обработки не производится привязки к корневому элементу, только к его элементам-потомкам, что полезно для объектов шаблонов) и bindingCache (оптимизированное хранение результатов обработки выражения data-win-bind при обработке шаблонов объектов).В наших первых упражнениях мы видели использование функций-конвертеров, которые превращают некоторое количество данных из источника в формат, который ожидает получить свойство-приемник. Но функции, которые вы задаёте в data-win-bind правильнее будет называть инициализаторами (initializer), так как, на самом деле, они вызываются лишь один раз.
Интересно, почему? Разве конвертеры не используются всякий раз, когда связанное свойство источника копируется в целевое свойство? Хорошо, да, но мы, на самом деле, говорим здесь о двух разных функциях. Посмотрите вниманительно на структуру кода для функции userTypeColor, которую мы использовали ранее:
userTypeToColor: WinJS.Binding.converter(function (type) {
return type == "kid" ? "Orange" : "Black";
})
Функция userTypeColor - это инициализатор. Когда она вызывается - однажды и только однажды - она возвращает значение из WinJS.Binding.converter - конвертер, который затем будет использоваться для каждого обновления свойства. Это и есть настоящая функция-конвертер, а не userTypeColor - на самом деле - это структура, которая упаковывает анонимную функцию, заданную в WinJS.Binding.converter.
Если смотреть глубже, WinJS.Binding.converter, на самом деле, использует bind для того, чтобы установить взаимоотношения между свойством-источником и целевым свойством и вставляет анонимную функцию-конвертер в эти взаимоотношения. К счастью, вам не придётся иметь дело с подобными сложными механизмами и вы можете просто предоставить функцию-конвертер, как показано выше.
Тем не менее, если вы хотите увидеть пример подобного низкоуровневого кода, обратитесь снова к примеру "Декларативная привязка данных" (http://code.msdn.microsoft.com/windowsapps/DeclarativeBinding-bfcb42a5), так как он показывает как создать конвертер для сложных объектов напрямую в коде, без использования WinJS.Binding.converter. В данномм случае функция должна быть маркирована как безопасная для обработки, если на неё ссылаются в разметке. Другая функция, WinJS.Binding.initializer, существует для тех же целей; значение, возвращаемое WinJS.Binding.converter проходит через тот же самый метод, прежде чем оно вернется обратно в ваше приложение.
Вы думаете, что мы уже завершили разговор о WinJS.Bindint? Не вполне, друзья! Есть две части этого обширного API, которые ведут нас прямо в следующую лекцию. (И сейчас вы узнаете настоящую причину, по которой я разместил весь этот раздел там, где разместил!) Первая - это WinJS.Binding.List -источник данных-коллекция, подходящий для привязки, который - не удивительно - весьма полезен при работе с элементами управления-коллекциями.
WinJS.Binding.Template, так же, уникальный вид пользовательского элемента управления. При использовании, как вы могли видеть в примере "Декларативная привязка", вы объявляете элемент (обычно div) с data-win-control = "WinJS.Binding.Template". В той же самой разметке вы задаёте содержимое шаблона в качестве элементов-потомков, каждый из которых может иметь собственные атрибуты data-win-bind. Что в этом уникально, так это то, что когда WinJS.UI.process или processAll находят эту разметку, они создают экземпляр шаблона и убирают всё, кроме корневого элемента, из DOM. Так что же в этом хорошего?
Итак, так как шаблон существует, кто угодно может вызвать его метод render для того, чтобы создать копию этого шаблона внутри какого-нибудь другого элемента, используя какой-нибудь контекст данных для обработки любых атрибутов data-win-bind в нём (обычно пропуская корневой элемент, отсюда и параметр skipRoot в методе WinJS.Binding.declarativeBind). Кроме того, вывод шаблона несколко раз в том же самом элементе позволяет создать множество элементов одного уровня, каждый из которых может иметь различные источники данных.
Вот оно что! Теперь мы можем начать рассматривать, какой всё это имеет смысл для элементов управления для вывода коллекций и источников данных-коллекций. Имея источник данных-коллекцию и шаблон, вы можете обойти этот источник и вывести копию шаблона для каждого отдельного элемента в источнике в его собственном элементе. Добавьте немного возможностей навигации или макет, внутри которого содержится элемент, и вуаля! Вы у истоков того, что, как мы узнаем, является элементами управления WinJS.UI.FlipView и WinJS.UI.ListView, когда разберемся со следующей лекцией.
data-win-control, которые обрабатываются с использованием WinJS.UI.process или WinJS.UI.processAll.
new и подходящих конструкторов, таких, как Button или WinJS.UI.Rating.
data-win-options для элементов управления WinJS.
win-backbutton, которые используются для стилизации стандартных HTML-элементов управления для того, чтобы они выглядели как элементы управления, специфичные для Windows.
-ms-* позволяют вам адресовать специфические части этих элементов управления. В случае с элементами управления WinJS, отдельные части стилизуют с использованием классов win-*, которые можно переопределить.
addEventListener. Пользовательские элементы управления, кроме того, могут быть показаны в панели Активы (Assets) в Blend либо для отдельного проекта, либо для всех проектов.
Файлы к данной лекции Вы можете скачать здесь.
Элементы управления - это одни из тех вещей, от которых никуда не деться, особенно в нашей культуре, одержимой технологиями, как та, которая окружает многих из нас. Даже не самые высокотехнологичные устройства, вроде велосипедов и разных садоводческих инструментов, имеют элементы управления. Но это не проблема - это реальная необходимость. Элементы управления - это средства, с помощью которых намерения людей переводятся в область механики и электроники и они созданы для того, чтобы приглашать человека к взаимодействию. Когда я это писал, на самом деле, я сидел в самолёте и разглядывал все элементы управления в поле моего зрения. Маленький мальчик, сидевший в следующем ряду, казалось, занят тем же, и большая кнопка "вызвать стюардессу" просто умоляла о том, чтобы её нажали!
Элементы управления, безусловно, необходимы приложениям для Windows 8, и они приглашают пользователей ткнуть в них, зовут прикасаться, нажимать, перетаскивать их. (Они, кроме того, приглашают пользователей, у которых не совсем чистые руки, а так же - пользователей-малышей. Кто-нибудь уже сделал планшетный компьютер, который можно мыть в посудомоечной машине?). Windows 8, конечно, предоставляет богатый набор элементов управления, написанных с использованием HTML, CSS, и JavaScript. Что наиболее заметно в этой связи, так то, что с самых ранних стадий проектирования, Microsoft решила избегать принуждения HTML/JavaScript-разработчиков к использованию элементов управления, которые не соответствовали бы тому, что уже знают эти разработчики, а именно, разработчики могут использовать HTML-элементы управления, такие, как <button>, стилизованные с помощью CSS и привязанные к JavaScript с использованием функций наподобие addEventListener и свойств, таких, как on<event>.
Конечно, вы можете использовать эти внутренние элементы управления HTML в приложениях для Windows 8, так как эти приложения выполняются на базе тех же подсистем рендеринга HTML/CSS, что и в Internet Explorer. Нет проблем. Здесь даже есть специальные классы, псевдо-классы и псевдо-элементы, которые дают вам точные средства настройки стилей, как мы увидим ниже. Но главный вопрос заключался в том, как реализовать элементы управления, специфичные для Windows 8, такие, как тумблеры (toggle switch) и элементы просмотра списков (list view), чтобы позволить вам работать с ними тем же способом - то есть - объявлять их в разметке, настраивать их внешний вид с помощью CSS и привязывать их к JavaScript-сценариям с помощью addEventListener и свойств on<event>.
В результате всего этого вы, разработчики HTML/JavaScript, можете найти всё это в WinJS, без необходимости заглядывать в WinRT. Другими словами, если вы обратили внимание на большой набор API в пространстве имен Windows.UI.Xaml (что составляет около 40% WinRT), знаете что? Вы можете полностью всё это игнорировать! Вместо этого вы будете использовать элементы управления из WinJS, которые поддерживают декларативную разметку, стилизацию с использованием CSS и так далее, что означает, что элементы управления Windows (и элементы управления, созданные разработчиками, следующие той же модели), в конечном счете, отображаются в DOM вместе со всем остальным, что делает их доступными в таком виде, который вы уже знаете и понимаете.
Рассказ об элементах управления в Windows 8, на самом деле, больше, чем одна лекция. Здесь мы рассмотрим, преимущественно, те элементы управления, которые работают с простыми данными (одиночными значениями) и включаются в макет страницы как элементы DOM. Включение в DOM, на самом деле, это причина того, что вы можете стилизовать элементы и управлять ими (в HTML и JavaScript), используя стандартные механизмы, и основная часть этой лекции посвящена показу внешнего вида параметров стилизации, которые вам доступны. В заключительной части данной лекции мы так же рассмотрим соответствующие темы, касающиеся привязки данных: создания взаимодействия между свойствами объектов, содержащих данные, и свойствами элементов управления (в том числе, со стилями). Таким образом, элементы управления отражают то, что происходит с данными.
Рассказ продолжится в лекции 5, "Коллекции и элементы управления для вывода коллекций", где мы посмотрим на элементы управления для работы с коллекциями - те, которые работают с потенциально большими наборами данных - и рассмотрим дополнительные возможности привязки данных, относящиеся к таким элементам управления. Кроме того, мы обратим особое внимание на мультимедиа-элементы (изображения, аудио, видео) в лекции 4 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript", точно названной "Мультимедиа", так как эти элементы управления имеют множество уникальных особенностей. Похожим образом, те элементы, которые используются, преимущественно, для создания макетов (таких, как сеточный (grid) макет или макет на основе гибких окон (flex box)), как описано в лекции 6, "Макет". И мы, кроме того, рассмотрим множество эл ементов пользовательского интерфейса, которые не включаются, как таковые, в макет - такие, как панели приложения (app bars), всплывающие элементы (flyouts), как мы увидим в лекции 1 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript".
Короче говоря, разобравшись с тем, как построить каркас приложения, наладить электропроводку и канализацию в лекции 3, мы готовы к приятным отделочным работам, вроде установки выключателей, дверных ручек и водопроводных кранов - всего того, что делает приложение по-настоящему готовым к тому, чтобы стать частью жизни пользователя.
Прежде чем мы продолжим, вам полезно будет узнать о двух важных материалах в Центре разработчиков Windows, к которым вы, что весьма вероятно, будете периодически обращаться. Первый - это полный список элементов управления (http://msdn.microsoft.com/library/windows/apps/hh465453.aspx), который содержит описания всех элементов управления, которые вам доступны, как мы подытожим позже в данной лекции. Второй - это всесторонний "Указатель рекомендаций по взаимодействию с пользователем" (http://msdn.microsoft.com/library/windows/apps/hh465424#ui_controls), который описывает лучшие подходы к использованию основных элементов управления и сценарии, в которых они не используются. Это очень ценный ресурс и для вас и для вашего дизайнера.
Снова повторюсь, что когда Microsoft создавала среду для разработчиков, мы стремились к высокой степени единообразия между встроенными элементами управления HTML, элементами WinJS и пользовательскими элементами управления. Я называю всё это "элементы управления", так как все они приводят к похожему опыту взаимодействия пользователя и системы: всё это различные визуальные элементы, с помощью которых пользователь взаимодействует с приложением. В этом смысле каждый такой элемент имеет три части:
Стандартные элементы управления HTML, конечно, уже имеют выделенную для них разметку, позволяющую объявлять их, наподобие <button>, <input>, и <progress>. Элементы управления WinJS и пользовательские элементы управления, лишены преимуществ существующих стандартов, их объявляют, используя некие корневые элементы, обычно - <div> или <span>, с двумя особыми атрибутами data-*: data-win-control и data-win-options. Значение data-win-control задаёт полное имя для публичной функции-конструктора, которая создаёт элемент управления в качестве дочернего элемента по отношению к корневому. Второе, data-win-options - это JSON-строка, содержащая пары ключ-значение, разделенные запятыми: { <key1>: <value1>, <key1>: <value2>, ... }.
data-win-options и ваше приложение, при следующем запуске, завершает работу без видимой причины (и не выдавая исключение), проверьте синтаксические ошибки в строке параметров. Например, подобное поведение характерно для случаев, когда забывают закрывающую фигурную скобку }.Функция-конструктор принимает два параметра: корневой элемент (родитель) и объект с параметрами. Удобная команда WinJS.Class.define позволяет создавать подобные функции, делая весьма удобным процесс определения элементов управления (так поступает и WinJS). Конечно, так как атрибуты data-*, в соответствии со спецификацией HTML5, полностью игнорируются подсистемой рендеринга HTML/CSS, нужны дополнительные усилия для того, чтобы превратить элемент с подобными атрибутами в настоящий элемент управления в DOM. И это, как я уже говорил ранее, цель существования методов WinJS.UI.process и WinJS.UI.processAll. Как мы скоро увидим, эти методы обрабатывают атрибуты с параметрами и передают получившийся объект и корневой элемент конструктору функции, заданной в data-win-control.
Результатом этой простой декларативной разметки, дополненной WinJS.UI.process/processAll является то, что элементы управления WinJS и пользовательские элементы управления являются, как и прочие, обычными DOM-элементами. На них могут ссылаться API для обхода DOM, они могут быть целями для стилизации с использованием полного набора CSS-селекторов (как мы увидим в галерее стилей, ниже). Они могут прослушивать внешние события, как и другие элементы и могут вызывать собственные события, реализуя методы [add/remove]EventListener и свойства on<event>. (WinJS, опять же, предоставляет стандартную реализацию addEventListener, removeEventListener и dispatchEvent для этой цели.)
Давайте посмотрим на элементы управления, которые мы можем использовать для Windows 8-приложений, начав с HTML- и WinJS-элементов. В обоих случаях мы посмотрим на их базовый внешний вид, как создаются их экземпляры, рассмотрим параметры, которые можно к ним применить.
Надеюсь, элементы управления HTML не нуждаются в долгих предварительных пояснениях. Они описаны в справочных материалах по HTML5, как в http://www.w3schools.com/tags/default.asp , и показаны в стандартном "светлом" стиле, как на Рис. 4.1 и Рис. 4.2. (В следующем разделе вы узнаете подробности о таблицах стилей WinJS). Важно отметить, что большинство внедренных объектов не поддерживается, за исключением специфических элементов управления ActiveX, смотрите материал "Перенос веб-приложения" (http://msdn.microsoft.com/library/windows/apps/hh465143.aspx).
Создание или создание экземпляров объектов HTML-элементов управления работает так, как вы можете ожидать. Вы можете объявить их в разметке, используя атрибуты для настройки параметров, краткое описание которых дано в таблице, следующей за Рис. 4.2. Вы, кроме того, можете создавать их программно, из JavaScript, вызывая new с соответствующим конструктором, настраивая свойства и прослушивателей событий так, как вам хочется, и добавлять новые элементы в DOM туда, где они нужны. Приложения для Windows 8 не предлагают в этом вопросе ничего нового.
Для того, чтобы увидеть пример создания и использования этих элементов управления, обратитесь к примеру "Основные элементы управления HTML" (http://code.msdn.microsoft.com/windowsapps/Common-HTML-controls-and-09a72a24) из Windows SDK, из которого получены изображения, приведенные на Рис. 4.1 и Рис. 4.2.
(рис 4.1) Стандартные элементы управления HTML5 в стандартном "светлом" стиле (таблица стилей ui-light.css в WinJS)
(рис 4.2) Стандартные элементы управления HTML5 в стандартном "светлом" стиле (таблица стилей ui-light.css в WinJS)
| Элемент управления | Разметка | Обычные параметры | Содержимое элемента (внутренний текст/HTML) |
|---|---|---|---|
| Button (Кнопка) | <button type="button"> | (обратите внимание, что без задания типа (type), тип по умолчанию - "submit") | Текст кнопки |
| Button (Кнопка) | <input type="button"> <input type="submit"> <input type="reset"> | value (текст кнопки) | n/a |
| Checkbox (Флажок) | <input type="checkbox"> | value, checked | n/a (используйте элемент Label (Метка) вокруг флажка для того, чтобы добавить кликабельный текст) |
| Drop Down List (Выпадающий список) | <select> | size="1" (по умолчанию), multiple, selectedIndex | Множество элементов <option> |
| Email (Поле ввода адреса электронной почты) | <input type="email"> | value (начальный текст) | n/a |
| File Upload (Отправка файла) | <input type="file"> | accept (mime-типы), mulitple | n/a |
| Hyperlink (Гиперссылка) | <a> | href, target | Текст ссылки |
| ListBox (Список) | <select> с размером > 1 | size (число больше, чем 1), multiple, selectedIndex | Множество элементов <option> |
| Multi-line Text (Многострочное текстовое представление) | <textarea> | cols, rows, readonly, data-placeholder (так как в placeholder есть ошибка) | Исходное текстовое содержимое |
| Number (Числовое поле ввода) | <input type="number"> | value (Исходный текст) | n/a |
| Password (Поле ввода пароля) | <input type="password"> | value (Исходный текст) | n/a |
| Phone Number (Поле ввода телефонного номера) | <input type="tel"> | value (Исходный текст) | n/a |
| Progress (Индикатор выполнения) | <progress> | value (начальная позиция), max (самая большая позиция; минимум - 0); отсутствие значений делает его неопределенным | n/a |
| Radiobutton (Переключатель) | <input type="radiobutton"> | value, checked, defaultChecked | Метка переключателя |
| Rich Text (Поле ввода форматированного текста) | <div> | contentEditable="true" | HTML-содержимое |
| Slider (Ползунок) | <input type="range"> | min, max, value (initial position), step (increment) | n/a |
| URL (Поле ввода URL-адреса) | <input type="url"> | value (Исходный текст) | n/a |
Два механизма, которые добавляют что-то HTML-элементам управления - это таблицы стилей WinJS и дополнительные методы, свойства и события, которые подсистема рендеринга Microsoft добавляет к большинству HTML-элементов. Этому посвящены два следующих раздела.
WinJS поставляется с двумя параллельно существующими таблицами стилей, которые предоставляют множество стилей по умолчанию и классов стилей для приложений Магазина Windows: ui-light.css и ui-dark.css. Вы всегда будете использовать либо один, либо второй, так как они взаимоисключающи. Первый предназначен для приложений, которые ориентированы на отображение текста, так как темный текст на светлом фоне обычно легче читается (поэтому данная тема часто используется в приложениях чтения новостей, книг, журналов и так далее, в том числе для изображений в публикующихся книгах!). Тёмная тема, с другой стороны, предназначена для приложений, ориентированных на мультимедийные данные, такие, как просмотрщики изображений и видео, где вы хотите обратить внимание пользователя на подобное содержимое.
Обе таблицы стилей задают множество классов стилей win-*, о которых я обычно думаю как о пакетах стилей, которые эффективно добавляют стили и поведение, основанное на CSS (наподобие псевдо-класса :hover), которые превращают стандартные HTML-элементы управления в их вариант, специфичный для Windows 8. Среди них - win-backbutton для кнопок, win-ring, win-medium, и win-large для кольцевых индикаторов выполнения, win-small для элемента управления выставления оценок, win-vertical для вертикального ползунка, и win-textareal для div, поддерживающего правку содержимого. Если вам нужны детали, поищите по их именам на закладке Правила стилей (Style Rules) в Blend.
Как вы уже, наверное, знаете, есть множество разрабатываемых стандартов для 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* всё еще работает).
| Методы | Описание |
|---|---|
msMatchesSelector | Определяет, удовлетворяет ли элемент управления некоторым условиям. |
ms[Set | Get | Release]PointerCapture | Осуществляет захват, восстановление и освобождение указателя для элемента. |
| Свойства стиля (в element.style) | Описание |
msGrid*, msRow* | Получает или задаёт расположение элемента внутри CSS-сетки. |
| События (добавьте "on" для свойств события) | Описание |
mscontentzoom | Вызывается, когда пользователь меняет масштаб элемента (Ctrl+ +/-, Ctrl +колесо мыши), жесты сжатия и расширения. |
msgesture[change | end | hold | tap | pointercapture] | Жест событий ввода (смотрите лекцию 3 курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript"). |
msinertiastart | Жесты событий ввода (смотрите лекцию 3 курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript"). |
mslostpointercapture | Элемент теряет указатель (ранее установленный msSetPointerCapture. |
mspointer[cancel | down | hover | move | out | over | up] | События ввода указателя (смотрите лекцию 3 курса "Пользовательский интерфейс приложений для Windows 8 с использованием HTML, CSS и JavaScript"). |
msmanipulationstatechanged | Состояние элемента изменилось после манипуляции |
Windows 8 определяет множество элементов управления, которые помогают приложениям удовлетворить требования к дизайну. Как отмечено ранее, они реализованы в WinJS а не в WinRT для приложений, написанных на HTML, CSS и JavaScript. Это позволяет данным элементам управления естественным образом интегрироваться с другими DOM-элементами. Каждый элемент управления определяется как часть пространства имен WinJS.UI с использованием WinJS.Class.define, где имя конструктора совпадает с именем элемента управления. В итоге, полное имя конструктора для элемента управления наподобие Rating (Оценка) выглядит как WinJS.UI.Rating.
Самые простые элементы, которые мы рассмотрим здесь, это DatePicker, Rating, ToggleSwitch, и Tooltip, стиль которых по умолчанию показан на Рис. 4.3.
(рис 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) и следуйте по ссылкам, ведущим к подробным описаниям конкретных элементов.
Полное имя конструктора в 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 объявляются в разметке с атрибутом data-* и их экземпляры не создаются до тех пор, пока не будет вызван WinJS.UI.process(<element>) для отдельного элемента или WinJS.UI.processAll для всех подобных элементов в DOM. Для того, чтобы понять этот процесс, опишем, что делает WinJS.UI.process для отдельного элемента.
data-win-options в объект, содержащий параметры.
data-win-control и вызывает команду new с обнаруженной функцией, передавая сведения о корневом элементе и объекте с параметрами.
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).
WinJS имеет три функции для обхода DOM: WinJS.UI.processAll, WinJS.Binding.processAll (которую мы рассмотрим позже в этой лекции), и WinJS.Resources.processAll (которую мы увидим в лекции 6 Курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой"). Каждая из этих функций ищет специфические атрибуты data-win-* и затем предпринимает дополнительные действия с использованием данного содержимого. Эти действия, однако, могут включать в себя вызов некоторого количества функций различных типов:
Подобные действия представляют риск атак методом внедрения, если функция 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.
Для начинающих, вот разметка для элемента управления 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).
С большинством других простых элементов управления, а именно, 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), вы можете перейти к этому примеру и самостоятельно посмотреть, как это работает.
Прежде чем мы займёмся стилизацией объектов, полезно будет выделить некоторые дополнительные функции в Blend для Visual Studio, которые касаются элементов управления. Как я говорил в Видео 2-2, закладка Активы (Assets) в Blend предоставляет вам быстрый доступ ко всем HTML-элементам и элементам управления WinJS (среди многих других элементов), которые вы можете просто перетащить и поместить на любую страницу, которая отображается на монтажной панели (смотрите Рис. 4.4). Это действие создаст базовую разметку, такую, как div с атрибутом data-win-control для элементов управления WinJS. Затем вы можете перейти на панель Атрибуты HTML (HTML Attributes) (справа) для настройки параметров в разметке (Рис. 4.5).
(рис 4.4) HTML-элементы (слева) и элементы управления WinJS (справа), на закладке Активы (Assets) в Blend
(рис 4.5) Закладка Атрибуты HTML (HTML Attributes) в Blend показывает параметры элемента управления WinJS, их правка повлияет на атрибут data-win-options в разметке
Далее, найдите время для того, чтобы загрузить пример "Основные HTML-элементы управления" (http://code.msdn.microsoft.com/windowsapps/Common-HTML-controls-and-09a72a24) в Blend. Это - отличная возможность для того, чтобы опробовать Интерактивный режим (Interactive mode) Blend, пройдя на страницу и посмотрев на взаимодействие между монтажной панелью и закладкой Динамическая DOM (Live DOM) (Рис. 4.6). Открыв проект, перейдите в интерактивный режим, выбрав в меню Вид > Интерактивный режим (View > Interactive Mode), нажав Ctrl+Shift+I или щёлкнув на крайнюю слева маленькую кнопку в правом верхнем углу монтажной панели. Затем выделите Сценарий 5 (Progress Introduction, Введение в индикаторы выполнения) в выпадающем списке, который откроет страницу, показанную на Рис. 4.6. Затем выйдите из интерактивного режима (с помощью тех же команд) и вы сможете кликать всюду на этой странице. Краткая демонстрация использования интерактивного режима подобным образом дана в файле Video 4-1 среди материалов, которые прилагаются к курсу.
(рис 4.6) Взаимодействие между монтажной панелью и Динамической DOM в Blend
Щелчок по элементу либо в панели динамической DOM, либо на монтажной панели выделяет этот элемент и там, и там. Это сделано для упрощения выделения и стилизации объектов. (Clicking on an element in either live DOM or designer highlight it in the other. This makes for easy selection and styling.)
В примере, посвященном основным HTML-элементам управления, вы увидите, что для встроенных элементов управления в Динамической DOM присутствует лишь единственный элемент, как это и должно быть, в то время как все внутренние детали являются неотъемлемой частью подсистемы рендеринга HTML/CSS. С другой стороны, загрузите вместо этого примера пример "HTML-элемент управления Rating" (http://code.msdn.microsoft.com/windowsapps/Rating-control-sample-4666c750) и разверните элемент div, который содержит один из подобных элементов управления. Здесь вы увидите дополнительные дочерние элементы, которые настраивают этот элемент управления (Рис. 4.7), и вы можете воспользоваться панелями HTML-атрибутов и CSS-свойств, которые находятся справа. Вы можете видеть что-то подобное (с даже большим количеством подробной информации) в Проводнике DOM (DOM Explorer) в Visual Studio, когда приложение исполняется (Рис. 4.8).
(рис 4.7) Развернув структуру элемента управления WinJS в панели Динамическая DOM в Blend, можно увидеть элементы, использованные для его построения
(рис 4.8) Развернув структуру элемента управления WinJS в Проводнике DOM в Visual Studio, так же можно увидеть полные подробности об элементе управления
Сейчас мы подошли к материалу, где мы, преимущественно, будем разглядывать множество красивых картинок: различные способы, которыми могут быть стилизованы элементы управления HTML и WinJS. Как мы уже обсудили, это производится, в любом случае, с помощью CSS, либо в таблице стилей, либо путём присваивания свойств style.*, имея в виду, что приложение имеет полный контроль над внешним видом элементов управления. На самом деле, абсолютно вся разница между HTML-элементами управления в приложениях для Магазина Windows и теми же самыми элементами на веб-страницах, заключается во внешнем виде и только в нём.
Для элементов управления HTML и WinJS, применимы CSS-стандарты, включая псевдо-селекторы наподобие :hover, :active, :checked и так далее, вместе со стилями с префиксами -ms-* для новых стандартов.
Для HTML-элементов управления, кроме того существуют дополнительные -ms-* стили, которые не являются частью CSS3 - для изоляции специфических частей этих элементов управления. То есть, так как составляющие подобных элементов управления не существуют раздельно в DOM, псевдо-селекторы, наподобие ::-ms-check для изоляции значка флажка, или ::-ms-fill-lower для изоляции левой или нижней части ползунка - позволяют вам связать стилизацию с внутренними механизмами системы рендеринга. В противоположность этому, все подобные части элементов управления WinJS могут быть адресованы через DOM, в итоге они просто стилизуются с помощью специальных классов win-*, определенных в таблицах стилей WinJS. То есть, эти элементы управления просто отрисовываются с данными классами стилей. Стили по умолчанию определены в таблицах стилей WinJS, но приложение может переопределить любой аспект этих стилей, приведя элемент управления к любому необходимому вам внешнему виду.
В некоторых случаях, как уже говорилось, определенные win-* классы определяют пакеты стилей для использования с элементами управления HTML, такие, как win-backbutton, win-vertical (для ползунка) и win-ring (для кольцевого индикатора выполнения). Они предназначены для приведения внешнего вида стандартных элементов управления к виду, похожему на вид специальных системных элементов управления.
Кроме того, существует несколько стилей -ms-* общего назначения, которые могут быть применены ко многим элементам управления (и, в целом, к любым элементам), вместе с некоторыми общими win-* классами стилей WinJS. Информация об этом собрана в следующей таблице.
| Стиль или класс | Описание |
|---|---|
-ms-user-select: none | inherit | element | text | auto | Включает или отключает возможность выделения элемента. Установка в none особенно полезна для предотвращения выделения в текстовых элементах. |
-ms-zoom: <percentage> | Оптический зум (увеличение). |
-ms-touch-action: auto | none (и больше) | Производит специальную адаптацию элементов управления для сенсорного взаимодействия, активируя усовершенствованную модель взаимодействия. |
win-interactive | Предотвращает стандартное поведение элементов управления, включенных в элементы FlipView и ListView (смотрите лекцию 5). |
win-swipeable | Устанавливает стили -ms-touch-action для элементов управления внутри ListView, так, что по ним можно провести (для выделения) в одном направлении, что не вызывает прокрутки в другом направлении. |
win-small, win-medium, win-large | Варианты размеров для некоторых элементов управления. |
win-textareal | Устанавливает типичные стили для правки текста. |
Для лучшего изучения вышеупомянутых вопросов, да и других тоже, потратьте некоторое время на следующие материалы: "WinJS CSS-классы для типографики" (http://msdn.microsoft.com/library/windows/apps/hh770582.aspx), "WinJS CSS-классы для HTML-элементов управления" (http://msdn.microsoft.com/en-us/library/windows/apps/Hh770562.aspx), и "CSS-классы для элементов управления WinJS" (http://msdn.microsoft.com/library/windows/apps/hh440966.aspx). Мне так же хочется предоставить вам краткие сведения обо всех других стилях с префиксами поставщиков (или селекторах), которые поддерживаются подсистемой CSS приложений для Магазина Windows. Обратитесь к следующей таблице. Стили с префиксами поставщиков для анимаций, трансформаций и переходов всё еще поддерживаются, хотя они больше не нужны, так как эти стандарты уже приняты. Я составил данный список, так как документация по этим вопросам может быть довольно сложной: вы можете перемещаться по страницам материала "Каскадные таблицы стилей" (http://msdn.microsoft.com/library/windows/apps/Hh996828.aspx) в документах для того, чтобы увидеть, как мало добавлено к CSS, которую вы уже знаете.
| Область | Стили |
|---|---|
| Фоны и границы | -ms-background-position-[x | y] |
| Модель контейнера | -ms-overflow-[x | y] |
| Базовый пользовательский интерфейс | -ms-text-overflow (для вывода многоточий) -ms-user-select (задаёт или возвращает информацию о возможности выделения пользователем текста внутри элемента) -ms-zoom (Оптическое увеличение) |
| Гибкое окно | -ms-[inline-]flexbox (значения для display); -ms-flex и -ms-flex-[align | direction | order | pack | wrap] |
| Градиенты | -ms-[repeating-]linear-gradient, -ms-[repeating-]radial-gradient |
| Сетка | -ms-grid и -ms-grid-[column | column-align | columns | column-span | grid-layer | row | row-align | rows | row-span] |
| Высокая контрастность | -ms-high-contrast-adjust |
| Области | -ms-flow-[from | into] вместе с методом MSRangeCollection |
| Текст | -ms-block-progression, -ms-hyphens и -ms-hypenate-limit-[chars | lines | zone], -ms-text-align-last, -ms-word-break, -ms-word-wrap, -ms-ime-mode, -ms-layout-grid и -ms-layout-grid-[char | line | mode | type], и -ms-text-[autospace | kashida-space | overflow | underline-position] |
| Другое | -ms-writing-mode |
Сейчас пришло время насладиться внешним видом возможностей стилизации приложений для Магазина Winsows. Многое может быть сделано с помощью стандартных стилей, и, затем, можно воспользоваться специальными стилями и классами, как показано в иллюстрациях к этому разделу. Подробности всех этих примеров можно найти в примере "Основные элементы управления HTML" (http://code.msdn.microsoft.com/windowsapps/Common-HTML-controls-and-09a72a24).
Кроме того, взгляните на хороший пример "Применение цветовой темы приложения" (http://code.msdn.microsoft.com/windowsapps/Theme-roller-sample-64b679f2). Этот замечательный пример позволяет вам настроить основной и дополнительный цвета для приложения, увидеть, как эти цвета воздействуют на различные элементы управления, и создаёт около 200 строк отличного CSS-кода, который вы можете скопировать в собственную таблицу стилей. Это сильно поможет вам создавать цветовые темы для приложения, которые поощряются как средства создания индивидуальности приложения в русле рекомендаций по дизайну Windows 8 и позволяют приложению отличаться от системы (Отметим, что элементы управления в элементах интерфейса, предоставляемых системой, наподобие всплывающего элемента для подтверждения при создании дополнительных плиток, будут стилизованы с использованием системных цветов. Приложение не может этим управлять.)
Кнопка (Button)
Текстовая область (Text Area)
Индикатор выполнения (Progress)
Выделение (Select)
Флажок/переключатель (Checkbox/Radiobutton)
Отправка файла (File upload)
Ввод обычного текста (Text input (most forms))
Индикатор выполнения (Progress)
Ввод пароля (Text input (password))
Ползунки (Range/Sliders)
Поле со списком/список (Combo/list box)
Аналогично, здесь показан внешний вид стилизованных элементов управления WinJS, иллюстрации взяты из SDK-примеров "HTML-элементы управления DatePicker и TimePicker" (http://code.msdn.microsoft.com/windowsapps/Date-and-time-picker-sample-0424c7c2), "HTML-элемент управления Rating" (http://code.msdn.microsoft.com/windowsapps/Rating-control-sample-4666c750), "HTML-элемент управления ToggleSwitch" (http://code.msdn.microsoft.com/windowsapps/ToggleSwitch-control-sample-84c0aacb) и "HTML-элемент управления Tooltip" (
http://code.msdn.microsoft.com/windowsapps/Tooltip-control-sample-cb24c2ce).
В случае с элементами управления WinJS DatePicker и TimePicker, применяют стилизацию HTML-элемента select вместе с псевдо-элементами ::-ms-value и ::-ms-expand. Хочу отметить, что примеры нельзя назвать всеобъемлющими, поэтому на иллюстрациях ниже выделены основные моменты:
Элемент управления Rating (Оценки) имеет состояния, которые могут быть стилизованы в дополнение к его звёздочкам и элементу управления в целом. Классы win-* идентифицируют эти индивидуальные особенности. Различные комбинации стилей приведены в таблице
| Класс стиля | Часть |
|---|---|
win-rating | Задаёт стиль всего элемента управления |
win-star | Общий стиль звездочек |
win-empty | Стиль пустых звездочек |
win-full | Стиль заполненных звездочек |
| Классы .win-star | Состояние |
win-average | Управляет отображением средней оценки (пользователь не указал оценку и свойство averageRating не равно нулю) |
win-disabled | Элемент управления заблокирован |
win-tentative | Элемент отображает предварительную оценку |
win-user | Элемент отображает оценку, которую выбрал пользователь |
| Варианты | Классы (селекторы) |
| Среднее количество пустых звезд | .win-star.win-average.win-empty |
| Среднее количество заполненных звезд | .win-star.win-average.win-full |
| Отключить пустые звезды | .win-star.win-disabled.win-empty |
| Отключить заполненные звезды | .win-star.win-disabled.win-full |
| Пустые звезды предварительной оценки | .win-star.win-tentative.win-empty |
| Заполненные звезды предварительной оценки | .win-star.win-tentative.win-full |
| Пустые звезды пользовательской оценки | .win-star.win-user.win-empty |
| Заполненные звезды пользовательской оценки | .win-star.win-user.win-full |
В случае с элементом управления ToggleSwitch (Тумблер), классы win-* идентифицируют его части; состояния задаются неявно. Обратите внимание на то, что часть win-switch - это обычный HTML-ползунок (<input type="range">), поэтому для него вы можете использовать все псевдо-элементы.
И, наконец, элемент управления Tooltip (Подсказка), win-tooltip - это отдельный класс для элементов-подсказок в целом. Элемент может содержать любой другой HTML-код, к которому применяется CSS с использованием обычных селекторов:
У этой подсказки скруглённые углы (This tooltip has rounded corner.)
Эта подсказка овальная (This tooltip is oval.)
<input type="range">) всегда выводит числовые значения; это не средство для отображения других форм текста, такого, как Low, Medium или High. Для реализации чего-то подобного, вы можете рассматривать элемент управления WinJS.UI.Rating с тремя значениями, используя свойство tooltipStrings для настройки подсказки.
::-ms-tooltip для ползунка воздействует только на видимость (с display: none ); он не используется для обычной стилизации подсказки. Он может быть полезен для того, чтобы скрыть подсказку, предоставляемую по умолчанию, если вы хотите реализовать собственный пользовательский интерфейс.
input (различные значения атрибута type ), о которых я не упоминал. Это потому, что данные типы не отличаются специфическим поведением и выводятся как обычное текстовое поле. Те из них, которые определяются особым образом, могут так же выводиться как обычные текстовые поля, но они могут влиять, например, на конфигурацию экранной клавиатуры, которая отображается на сенсорных устройствах (смотрите лекцию 3 Курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript") и обеспечивать особые средства для проверки вводимых данных (например, числовой тип принимает лишь цифры).
data-win-selectable (http://msdn.microsoft.com/library/windows/apps/hh440973.aspx), установленный в true, задаёт возможность выделения элемента таким же образом, как все элементы input и contentEditable .
width и height для элемента управления, попробуйте использовать вместо них style.width и style.height.
<button> и <input type="button">. Выглядят они одинаково, но первая - это блочный тег, который может выводить внутри себя HTML, в то время, как второй - это встроенный тег, который может отображать только текст. button, это имя по умолчанию для <input type="submit">, которая имеет собственную семантику, так что обычно, для уверенности, пользуются <button type="button">.
WinJS.UI.Tooltip подвергается обрезке, вы можете переопределить стиль max-width в классе win-tooltip, который установлен в 30 ем в таблице стилей WinJS. Просмотр стиля на закладке Blend Правила стиля (Style Rules) - это быстрый способ увидеть значения по умолчанию.
meter не поддерживается приложениями для Магазина Windows.
focus в JavaScript). Для того, чтобы отключить этот прямоугольник, выводимый по умолчанию для элементов управления, используйте <selector>:focus { outline: none; } в CSS.
window.getComputedStyle (http://msdn.microsoft.com/library/windows/apps/Hh702516.aspx) для получения объекта currentStyle (http://msdn.microsoft.com/library/windows/apps/hh453398.aspx), который содержит стили, примененные для элемента или для псевдо-элемента. Это весьма полезно, особенно при отладке, так как псевдо-элементы, наподобие ::-ms-thumb для HTML-ползунка, никогда не появляются в DOM, в итоге, их стилизация не доступна ни через свойство элемента style, ни посредством инструментов наподобие Blend. Вот пример получения цвета фона для подсказки (thumb) ползунка:
var styles = window.getComputedStyle(document.getElementById("slider1"), "::-ms-thumb");
styles.getPropertyValue("background-color");Как ни обширен набор элементов управления HTML и WinJS, всегда будет что-то, что вам нужно, но чего система делать не умеет. "Есть ли здесь элемент управления для календаря?", - это вопрос, который мне часто приходится слышать. "А как насчёт элементов для вывода графиков?". Всё это не включено в Windows 8, и несмотря на пожелания обратного, это означает, что вы, или другие разработчики, нуждаются в создании пользовательских элементов управления.
К счастью, всё, что мы уже узнали, особенно об элементах управления WinJS, применимо к пользовательским элементам управления. На самом деле, элементы управления WinJS полностью реализованы с использованием той же модели, которую вы можете использовать напрямую, и если вы посмотрите код WinJS, вы сможете найти множество примеров подобной реализации.
Возвращаясь к уже пройденному материалу, отметим, что элементы управления - это лишь декларативная разметка (создающая элементы в DOM), плюс - применимый CSS, методы, свойства и события, доступные из JavaScript. Для создания подобного элемента управления в модели WinJS, воспользуйтесь следующей стандартной последовательностью шагов:
WinJS.Namespace.define для обеспечения области видимости имён и для того, чтобы не включать дополнительные идентификаторы в глобальное пространство имен. (Не добавляйте элементы управления в пространство имен WinJS). Помните, что вы можете вызывать WinJS.Namespace.define множество раз для добавления новых членов, таким образом, приложение обычно имеет единое пространство имен для всех его пользовательских элементов управления.
WinJS.Class.define (или derive), присваивая возвращаемое значение имени, которое вы хотите использовать в атрибутах data-win-control. Его полное имя будет выглядеть как <namespace>.<constructor>.
<constructor>(element, options)):element равен null или undefined, создайте div для использования вместо него.
element корневым элементом, содержащим элемент управления, убедитесь, что установили element.winControl=this и this.element=element для того, чтобы ваше решение соответствовало шаблону WinJS.WinJS.Class.define, второй аргумент - это объект, который содержит ваши публичные методы и свойства (доступные в созданном экземпляре объекта). Третий аргумент - это объект со статическими методами и свойствами (которые доступны путём обращения к имени класса, без необходимости вызывать new).
WinJS.Class.mix) ваш класс результатами из WinJS.Utilities.createEventProperties(<events>), где <events> это массив имён событий (без префиксов on). Это создаст свойства on<event> в вашем классе для каждого имени из списка.
WinJS.Utilities.eventMixin, которая похожа на описываемую (но без setOptions) и полезна для создания объектов, которые не являются элементами управления, не присутствуют в DOM, но нуждаются в вызове событий. Здесь реализация не принимает участие в восходящей (bubbling) и нисходящей (tunneling) маршрутизации событий DOM.win-* классы для того, чтобы ваши элементы управления соответствовали общему подходу к стилизации.
WinJS.Namespace.define для одного и того же пространства имён аддитивен, таким образом, вы можете заполнить одно пространство имён элементами управления, определенными в различных файлах.Вы можете решить использовать WinJS.UI.Pages, если всё, что вам нужно - это фрагмент HTML/CSS/JavaScript, подходящий для повторного использования, для которого нет особой необходимости во множестве методов, свойств и событий. WinJS.UI.Pages, на самом деле, реализован как пользовательский элемент управления. Точно так же, если то, что вам нужно - это участок HTML, подходящий для повторного использования, с которым вы хотите связать некоторые данные во время выполнения программы, обратитесь к WinJS.Binding.Template, о котором мы поговорим далее. Это не элемент управления - в том виде, как мы его здесь описали - он не поддерживает события, например, но он может быть в точности тем, что вам нужно.
Важно напомнить, что всё в WinJS, вроде WinJS.Class.define и WinJS.UI.DOMEventMixin - это лишь вспомогательные функции для широко распространённых шаблонов. Вам не нужно безальтернативно использовать их, так как в итоге, пользовательский элемент управления - это лишь элемент DOM, такой же, как и все остальные и вы можете создавать такие элементы и управлять ими так, как вы того хотите. Утилиты WinJS лишь ускоряют и упрощают выполнение основных задач.
Примеры пользовательских элементов управления
Вот пара примеров, которые помогут увидеть эти рекомендации в действии. Первый - это то, что Крис Траверс, один из инженеров WinJS, который оказал мне неоценимую помощь с этим курсом, назвал "самым тупым элементом управления, который можно себе представить". Однако, он чётко показывает самые главные структуры:
WinJS.Namespace.define("AppControls", {
HelloControl: WinJS.Class.define(function (element, options) {
element.winControl = this;
this.element = element;
if (options.message) {
element.innerText = options.message;
}
})
});
После того, как элемент определен, вы можете использовать следующую разметку, чтобы WinJS.UI.process/processAll создали экземпляр объекта элемента управления (как встроенного элемента, так как мы использует span в качестве корневого элемента)
<span data-win-control="AppControls.HelloControl" data-win-options="{ message: 'Hello, World'}">
</span>
Обратите внимание на то, что код определения элемента должен быть исполнен перед вызовами WinJS.UI.process/processAll, то есть, чтобы функция конструктора, упомянутая в data-win-control, уже существовала в этот момент.
Для того, чтобы увидеть более полно реализованный элемент управления, вы можете взглянуть на пример "HTML SemanticZoom для пользовательских элементов управления" (http://code.msdn.microsoft.com/windowsapps/SemanticZoom-for-custom-4749edab). Мой друг Кеничиро Танака, из отделения Microsoft в Токио, кроме того, создал элемент управления, показанный на Рис. 4.9 и предоставил упражнение CalendarControl для этой лекции. (Обратите внимание на то, что этот пример лишь частично восприимчив к локализованным параметрам календаря, его не собирались делать полнофункциональным).
Следуя указаниям, данным выше, этот элемент управления определен с использованием WinJS.Class.define внутри пространства имен Control (calendar.js, строки 4-10 показанные здесь [комментарии опущены]).
WinJS.Namespace.define("Controls", {
Calendar : WinJS.Class.define(
function (element, options) {
this.element = element || document.createElement("div");
this.element.className = "control-calendar";
this.element.winControl = this;
Оставшаяся часть конструктора (строки 12 - 63) создают дочерние элементы, которые определяют элемент управления, обеспечивая то, что каждый участок имеет собственное имя класса, будучи помещенным в пространство имен класса control-calendar, помещенного в корневой элемент, что позволяет независимо стилизовать отдельные части. Таблица стилей по умолчанию, применяемая здесь - это calendar.css; некоторые параметры переназначены в default.css, это отличает два элемента управления, приведенных на Рис. 4.9.
(рис 4.9) Внешний вид элемента управления Calendar (календарь)
Внутри конструктора вы, кроме того, можете видеть, что элемент управления подключает собственные обработчики событий для элементов-потомков, таких, как кнопки предыдущий/следующий и каждая ячейка с датой. В последнем случае, щелчок по ячейке использует dispatchEvent для того, чтобы вызвать событие элемента управления dateselected.
Строки 63 - 127 определяют членов элемента управления. Здесь присутствуют два внутренних метода, _setClass и _update, за которыми следуют два публичных метода nextMonth и prevMonth, и три публичных свойства - year, month и date. Эти свойства можно установить посредством строки data-win-options в разметке, или напрямую через объект элемента управления, как мы скоро увидим.
В конце calendar.js вы можете увидеть два вызова WinJS.Class.mix для добавления свойств для событий (здесь лишь одно свойство), и методов стандартных событий DOM, таких, как addEventListener, removeEventListener и dispatchEvent вместе с setOptions:
WinJS.Class.mix(Controls.Calendar, WinJS.Utilities.createEventProperties("dateselected"));
WinJS.Class.mix(Controls.Calendar, WinJS.UI.DOMEventMixin);
Очень хорошо то, что всё это весьма просто добавлять. WinJS, спасибо!WinJS.Class.mix принимает различное число аргументов, таким образом вы вполне можете объединить два вышеприведенных вызова в один.
Между calendar.js и calendar.css у нас есть объявление элемента управления. В default.html и default.js мы можем увидеть, как используется элемент управления. На Рис. 4.9 элемент управления слева объявлен в разметке и его экземпляр создан благодаря вызову WinJS.UI.processAll в default.js.
<div id="calendar1" class="control-calendar" aria-label="Calendar 1"
data-win-control="Controls.Calendar"
data-win-options="{ year: 2012, month: 5, ondateselected: CalendarDemo.dateselected}">
</div>
Вы можете видеть, как мы используем полное имя конструктора, так же, как и обработчика событий, который мы назначаем событию ondateselected. Но помните, что функции, на которые мы ссылаемся в разметке, как на эти, должны быть маркированы для строгой обработки. Конструктор автоматически маркирован с помощью WinJS.Class.Define, но обработчик события требует дополнительных усилий: мы помещаем функцию в пространство имён (для того, чтобы обеспечить её глобальную видимость) и используем WinJS.UI.eventHandler для того, чтобы выполнить маркировку:
WinJS.Namespace.define("CalendarDemo", {
dateselected: WinJS.UI.eventHandler(function (e) {
document.getElementById("message").innerText = JSON.stringify(e.detail) + " selected";
})
});
Опять же, если вы забыли маркировать функцию подобным образом, экземпляр элемента управления не будет создан. (Уберите контейнер WinJS.UI.eventHandler для того, чтобы это увидеть).
Для демонстрации создания элемента управления за пределами разметки, элемент управления справа на Рис. 4.9 создан так, как показано ниже в div-элементе calendar2:
//Как только мы создали данный элемент управления в коде, мы не зависим от WinJS.UI.processAll.
var element = document.getElementById("calendar2");
//Как только мы предоставили элемент, он будет автоматически добавлен в DOM
var calendar2 = new Controls.Calendar(element);
//Так как этот обработчик не участвует в процессе обработки разметки,
calendar2.ondateselected = function (e) {
document.getElementById("message").innerText = JSON.stringify(e.detail) + " selected";
}
Всё готово!
Blend - это отличный дизайнерский инструмент, который позволяет работать с элементами управления напрямую, в монтажной панели, поэтому вам может быть интересно, как пользовательские элементы управления могут участвовать в подобном.
Для начала, так как пользовательские элементы управления это лишь элементы в DOM, Blend работает с ними так же, как и с другими частями DOM. Попытайтесь загрузить пример с календарём в Blend для того, чтобы увидеть это своими глазами.
Далее, элементы управления могут определять, исполняются ли они внутри Blend, в режиме дизайна, если свойство Windows.ApplicationModel.DesignMode.designModeEnabled установлено в true. Один из случаев, когда это весьма полезно, заключается в обработке строк ресурсов. Мы не будем подробно говорить о ресурсах до лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", но это важно знать уже сейчас, что разрешение ресурсов посредством Windows.ApplicationModel.Resources.ResourceLoader, не работает в режиме дизайна в Blend, как это происходит при реальном исполнении приложения. Система напрямую заявляет об этом, выдавая исключение! Таким образом, вы можете использовать в режиме дизайна флаг для того, чтобы предоставить некоторые данные по умолчанию, вместо того, чтобы выполнять поиск данных.
Например, один из первых партнёров, с которым я работал, имел метод для извлечения локализованного URI со своего сервиса поддержки приложения, который не работал в режиме дизайна. Используя флаг режима дизайна, таким образом, мы просто изменили код, приведя его к такому виду:
WinJS.Namespace.define("App.Localization", {
getBaseUri: function () {
if (Windows.ApplicationModel.DesignMode.designModeEnabled) {
return "www.default-base-service.com";
} else {
var resources = new Windows.ApplicationModel.Resources.ResourceLoader();
var baseUri = resources.getString("baseUrl");
return baseUri;
}
}
});
И, наконец, возможно сделать так, чтобы пользовательские элементы управления отображались на закладке Активы (Assets) среди прочих HTML-элементов и элементов управления WinJS. Для того, чтобы это сделать, вам, во-первых, нужен файл OpenAjax Metadata XML (OAM) (http://www.openajax.org/member/wiki/OpenAjax_Metadata_1.0_Specification_Descriptive), который предоставляет всю необходимую информацию для элемента управления, и у вас уже есть достаточно мест, где можно посмотреть подобные файлы. Для того, чтобы их найти, выполните поиск файлов по запросу *._oam.xml в папке Program Files (x86). Вы найдёте кое-что в папке Microsoft Visual Studio 11.0 и в папках с большей глубиной вложенности, в Microsoft SDKs, где располагаются метаданные WinJS. И там и там вы, кроме того, найдёте множество примеров значков размерами 12х12 и 16х16, которые понадобятся для вашего элемента управления.
Если вы посмотрите в папку controls/calendar упражнения CalendarControl к этой лекции, вы найдёте файл calendar_oam.xml и два значка вместе с файлами .js и .css. OAM-файл (который должен иметь окончание имени файла в виде _oam.xml) сообщает Blend о том, как сохранять элемент управления в его панели Активы (Assets), и какой код следует вставить, когда вы перетаскиваете элемент в HTML-файл. Вот содержимое этого файла:
<?xml version="1.0" encoding="utf-8"?>
<!--Используйте символы подчеркивания или точки в id и name, но не пробелы. -->
<widget version="1.0"
spec="1.0"
id="http://www.kraigbrockschmidt.com/scehmas/ProgrammingWin8_JS/Controls/Calendar"
name="ProgWin8_JS.Controls.Calendar"
xmlns="http://openajax.org/metadata">
<author name="Kenichiro Tanaka" />
<!-- title предоставляет имя, которое появляется в панели Активы (Assets) в Blend
(иначе используется widget.name). -->
<title type="text/plain"><![CDATA[Calendar Control]]></title>
<!--обеспечивает подсказку для панели Активы (Assets). -->
<description type="text/plain"><![CDATA[A single month calendar]]></description>
<!-- значок (12x12 и 16x16) предоставляет маленький значок
в панели Активы (Assets). -->
<icons>
<icon src="calendar.16x16.png" width="16" height="16" />
<icon src="calendar.12x12.png" width="12" height="12" />
</icons>
<!-- Этот элемент описывает то, что будет вставлено в .html файл;
закомментируйте всё лишнее -->
<requires>
<!-- Код элемента управления -->
<require type="javascript" src="calendar.js" />
<!-- Код таблицы стилей элемента -->
<require type="css" src="calendar.css" />
<!-- Встроенные скрипты для заголовка документа -->
<require type="javascript"><![CDATA[WinJS.UI.processAll();]]></require>
<!-- Встроенный CSS для раздела стилей в заголовке документа -->
<!--<require type="css"><![CDATA[.control-calendar{}]]></require>-->
</requires>
<!-- Что вставлять в тело элемента управления; убедитесь в том, что это корректный HTML
иначе Blend не позволит вставить его -->
<content>
<![CDATA[
<div class="control-calendar" data-win-control="Controls.Calendar"
data-win-options="{ year: 2012, month: 6 }"></div>
]]>
</content>
</widget>
Когда вы добавите все пять файлов в проект в Blend, вы увидите значок и подпись элемента управления в панели Активы (Assets) (а если поместить указатель мыши над элементом, появится всплывающая подсказка):
Если вы перетащите этот элемент управления на HTML-страницу, вы увидите, как в её различные части добавлено следующее:
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
<script src="calendar.js" type="text/javascript"></script>
<link href="calendar.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="control-calendar" data-win-control="Controls.Calendar" data-win-options="{month:6, year:2012}"></div>
</body>
</html>
Но подождите! Что случилось с вызовом WinJS.UI.processAll(), который отмечен в XML тегом script в заголовке? Blend проверил этот участок кода на предмет того, встречается ли подобный вызов в уже загруженных скриптах. Если это так (как обычно бывает с шаблонами проектов), Blend не станет повторять его. Если он сочтёт нужным включить данный вызов, или если вы зададите здесь другой код, Blend вставит его в тег <script> в заголовке.
Кроме того, ошибки в вашем OAM-файле принудят Blend не вставлять элемент управления, таким образом, вам нужно исправить эти ошибки. Когда вы вносите изменения, Blend не перезагружает метаданные до тех пор, пока вы не перезагрузите проект или не переименуете OAM-файл (сохранив часть _oam.xml). Я нахожу последнее более простым, так как blend не заботит то, как выглядит остальная часть имени файла. В процессе переименования, так же, если вы обнаружите, что элемент управления исчез из панели Активы (Assets), это означает, что имеются ошибки в самой структуре OAM XML, такие, как неподходящие символы в значениях атрибутов. В подобных случаях вам понадобится метод проб и ошибок, и, конечно, вы можете обратиться к уже существующих на вашем компьютере OAM-файлам для того, чтобы разобраться с подробностями.
Кроме того, вы можете сделать ваш элемент управления доступным для всех проектов в Blend. Для того, чтобы сделать это, перейдите в папку Program Files (x86)\Microsoft Visual Studio 11.0\Blend, создайте папку Addins, если её там еще нет, создайте в ней подпапку для вашего элемента управления (используя подходящее уникальное имя), и скопируйте все активы вашего элемента управления туда. Когда вы перезапустите Blend, вы увидите, что ваш элемент управления находится в списке в категории Add-ins в панели Активы (Assets):
Подобным подходом можно воспользоваться, если вы создаете элементы управления для использования их другими разработчиками. Вашей программе установки следует просто расположить ваши активы в папку Addins. Что касается использования подобных элементов управления, когда вы перетаскиваете элемент в HTML-файл, необходимо, чтобы активы, необходимые элементу управления (но не значки и не OAM-файл), были скопированы в корневую папку проекта. Затем вы можете перемещать их так, как вам нужно, меняя ссылки на файлы, конечно.
Как я упоминал во введении к этой лекции, вопросы привязки данных тесно связаны с элементами управления, так как они касаются создания взаимоотношений между свойствами объектов, хранящих данные, и свойствами элементов управления (и стилями в том числе). В этом смысле, элементы управления отражают то, что происходит с данными, а часто это именно то, что разработчик хочет предоставить пользователю приложения.
Я хочу начать этот разговор с общего обзора привязки данных, так как вы можете быть знакомы с этой концепцией в целом, но не понимать достаточно чётко отдельных деталей. Время от времени, на самом деле, особенно когда говоришь с кем-то, кто работал с этим несколько лет, привязка данных кажется окутанной какой-то непроницаемой загадочностью. Я не отношу себя к таким посвященным, поэтому попытаюсь выразить основные понятия обычными словами.
Основная идея привязки данных (data binding) заключается в соединении вместе или "привязывании" свойств двух различных объектов, обычно объекта данных (контекста) и объекта пользовательского интерфейса, о которых обычно говорят как об объекте-источнике и об объекте-приемнике (целевом объекте). Ключевой момент здесь заключается в том, что привязка данных обычно осуществляется для свойств, но не для объектов.
Привязка, так же, может привлекать конверсию значений из одного типа в другой, такую, как конверсию набора различных свойств из источника в единую строку, подходящую для приёмника. Кроме того, к одному и тому же источнику можно прикрепить несколько целевых объектов, или один объект-приёмник соединить с несколькими источниками данных. Эта гибкость может привести к тому, что привязка данных может выглядеть несколько туманным предметом для обсуждений, так как она включает в себя огромное количество возможностей! Тем не менее, в большинстве случаев мы можем обойтись достаточно простыми решениями.
Обычный сценарий привязки данных показан на Рис. 4.10, где у нас есть свойства дух элементов пользовательского интерфейса - span и img, привязанные к свойствам объекта, содержащего данные. Таким образом, здесь три связи: (1) свойство span.innerText привязано к свойству source.name; (2) свойство img.src привязано к свойству source.photoUrl; и (3) свойство span.style.color связано с выходом функции преобразования данных, которая преобразует свойство source.userType в цвет.
(рис 4.10) Обычный сценарий привязки данных между объектом-источником данных и двумя целевыми объектами пользовательского интерфейса, включая две прямых связи и одну связь с участием функции преобразования данных
Как эти связи ведут себя во время выполнения программы, зависит от направления (direction) каждой из связей, которое может быть одним из следующих:
Объект данных (Data Object)
Элемент управления пользовательского интерфейса (UI/Control)
Значение элемента (Item Value)
Инициализация (Initialization)
Нет непрерывного соединения (No ongoing connection)
Объект данных (Data Object)
Элемент управления пользовательского интерфейса (UI/Control)
Значение элемента (Item Value)
Инициализация (Initialization)
Изменения в данных находят отнажение в пользовательском интерфейсе (Changes to data are reflected in UI)
Объект данных (Data Object)
Элемент управления пользовательского интерфейса (UI/Control)
Значение элемента (Item Value)
Инициализация (Initialization)
Изменения в любом месте отражаются и в другом (Changes in either place are reflected in the other)
Сейчас, когда мы узнали о том, что такое привязка данных, мы можем посмотреть, как она может быть реализована в приложениях для Windows 8. Если хотите, вы можете создать любую схему для привязки данных или использовать библиотеку JavaScript стороннего разработчика для работы: всё это, в конечном счете, касается связи свойств объекта-источника данных со свойствами объекта-приёмника.
Если вы похожи на моих предков по отцовской линии, которые не полагались ни на кого, кроме себя, и делали всё сами (бурили скважины, добывали уголь и делали запасные части для машин), вы будете довольны, разрабатывая собственное решение для привязки данных. Но если вы более умеренны по своей природе, как я (благодаря материнской линии), вам понравится, как и мне, если кто-то достаточно умный выполнит какую-то работу за вас. Я благодарен команде разработки WinJS, которая, зная основные нужды привязки данных, создала API WinJS.Binding. Оно поддерживает и единовременную, и одностороннюю привязку, как декларативно, так и программно, вместе с функциями преобразования значений. Сейчас WinJS не предоставляет инструментов для двусторонней привязки, но подобные структуры несложно создать в коде.
Внутри структур WinJS, множественные целевые элементы могут быть привязаны к единственному источнику данных. WinJS.Binding, на самом деле, предоставляет то, что называется шаблонами (templates), обычно - коллекции целевых элементов, которые привязаны к одному и тому же источнику данных. Хотя мы не рекомендуем это делать, однако, можно привязать один целевой элемент к нескольким источникам, но такой конструкцией сложно управлять. Лучший подход в подобной ситуации заключается в том, чтобы заключить разные источники данных в оболочку одного объекта и привязать целевой объект именно к нему.
Лучший способ понять WinJS.Binding - посмотреть, для начала, на то, как мы пишем собственный код для связывания и потом изучить решение, которое предлагает WinJS. Для этого примера мы использует тот же самый сценарий, который показан на Рис. 4.10, где мы имеем объект-источник, привязанный к двум отдельным элементам пользовательского интерфейса, с одним конвертером, который превращает одно из свойств источника в цвет.
Единовременная привязка, как упоминалось выше, это то, что вы делаете, просто присваивая значения свойствам элемента. Вот HTML-код для этого примера:
<!-- Разметка: элемент пользовательского интерфейса, который мы привяжем к объекту-источнику данных --> <section id="loginDisplay1"> <p>You are logged in as <span id="loginName1"></span></p> <img id="photo1"></img> </section>
И объект-источник данных:
var login1 = { name: "liam", id: "12345678",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png", userType: "kid"};
Мы можем связать их следующим образом, использовав, кроме того, функцию-конвертер:
//"Связывание" одного свойства за один раз, с функцией-конвертером, вызываемой напрямую
var name = document.getElementById("loginName1");
name.innerText = login1.name;
name.style.color = userTypeToColor1(login1.userType);
document.getElementById("photo1").src = login1.photoURL;
function userTypeToColor1(type) {
return type == "kid" ? "Orange" : "Black";
}
В итоге мы получаем следующий результат, в котором я бессовестно опубликовал фото моего ребенка, когда он был еще совсем малышом:
Этот код можно найти в Test 1, в упражнении BindingTests для этой лекции. С помощью WinJS мы можем выполнить то же самое, используя декларативный синтаксис и функцию обработки. В разметке, мы используем атрибут data-win-bind для того, чтобы отобразить целевые свойства содержащего их элемента на свойства объекта-источника, который передан функции обработки WinJS.Process.All.
Значение data-win-bind - это строка с парами свойств. Синтаксис каждой пары выглядит как <target property> : <source property> [<converter>], где converter необязателен. Каждый идентификатор свойства может использовать запись с помощью точек, если нужно, и пары свойств разделены двоеточиями, как показано в HTML:
<section id="loginDisplay2"> <p>You are logged in as <span id="loginName2" data-win-bind="innerText: name; style.color: userType Tests.userTypeToColor"> </span> </p> <img id="photo2" data-win-bind="src: photoURL"/> </section>
Обратите внимание на то, что обращение к массиву свойств источника с использованием [ ] не поддерживается, хотя конвертер может это сделать. В случае с целью, если этот объект имеет JavaScript-свойство, на который вы хотите сослаться, используя идентификатор, написанный через дефис, вы можете использовать следующий синтаксис:
<span data-win-bind="this['funky-property']: source"></span>
Похожий синтаксис нужен для атрибутов цели привязки данных, таких, как атрибуты aria-* для реализации специальных возможностей. Так как это не свойства JavaScript, на которые можно сослаться, используя идентификатор с дефисами, необходим специальный конвертер (или инициализатор, как его лучше называть), который называется WinJS.Binding.setAttribute.
<label data-win-bind="this['aria-label']: title WinJS.Binding.setAttribute"></label>
Кроме того, обратите внимание на WinJS.Binding.setAttributeOneTime для единовременной привязки атрибутов.
В любом случае, в итоге мы получаем такой же источник данных, как раньше:
var login2 = { name: "liamb", id: "12345678",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png", userType: "kid"};
Мы превращаем разметку в реальные связи, используя WinJS.Binding.processAll.
//processAll сканирует дерево элементов в поисках data-win-bind, используя заданный объект как контекст данных
WinJS.Binding.processAll(document.getElementById("loginDisplay2"), login2);
Этот код, Test 2 в упражнении, производит тот же результат, что и Test1. Единственное, что нужно добавить - это объявление функции конвертера, которая должна быть глобально доступной и маркирована для обработки. Это можно сделать с помощью пространства имен, которое содержит функцию (опять же, её называют инициализатором, как мы обсудим в разделе "Инициализаторы привязки" ближе к концу этой лекции), созданную с помощью WinJS.Binding.converter:
//Используем пространство имен для экспорта функции из текущего модуля,
//таким образом WinJS.Binding может найти её
WinJS.Namespace.define("Tests", {
userTypeToColor: WinJS.Binding.converter(function (type) {
return type == "kid" ? "Orange" : "Black";
})
});
Как и в случае с конструктором элементов управления WinJS.Class.define, WinJS.Binding.converter автоматически маркирует функцию, которую он возвращает, как безопасную для обработки.
Кроме того, мы можем поместить объект-источник данных и применимый конвертер в одно и то же пространство имен.login и фукнцию userTypeToColor в пространство имен LoginData, в итоге, разметка и код будут выглядеть так:
<span id="loginName3"
data-win-bind="innerText: name; style.color: userType LoginData.userTypeToColor">
</span>
WinJS.Binding.processAll(document.getElementById("loginDisplay3"), LoginData.login);
WinJS.Namespace.define("LoginData", {
login : {
name: "liamb", id: "12345678",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam07.png",
userType: "kid"
},
userTypeToColor: WinJS.Binding.converter(function (type) {
return type == "kid" ? "Orange" : "Black";
})
});
В итоге, для единовременной привязки данных, WinJS.Binding просто даёт вам декларативный синтаксис, который выполняет в точности то, что вы можете сделать самостоятельно в коде, но делает это в немного меньшем объеме кода. Так как это - лишь разметка и функция для обработки, здесь нет ничего волшебного, хотя подобные полезные утилиты по-своему волшебны! На самом деле, код здесь реализует лишь единовременную привязку, без вызова источником данных каких-либо событий при изменении данных. Мы увидим, как сделать это с помощью WinJS.Binding.as совсем скоро, после дополнительной пары замечаний.
Во-первых, WinJS.Binding.processAll, на самом деле, асинхронная функция, которая возвращает promise-объект. Любой обработчик завершения, переданный его методу done, будет вызван при завершении обработки, если у вас есть дополнительный код, который основан на данном состоянии. Во-вторых, вы можете вызвать WinJS.Binding.processAll больше, чем один раз для того же самого целевого элемента, каждый раз задавая различные объекты-источники (контексты данных). Это не заменит существующие привязки, учтите - лишь добавит новые, подразумевая, что вы хотите привязать то же самое целевое свойство к более, чем одному источнику, что, в итоге, приведет к большому беспорядку. Итак, снова, лучше всего собрать эти источники в один объект и привязать целевой объект к нему, используя запись с точкой для идентификации вложенных свойств.
Когда вы задаете в качестве целевых свойства элементов управления WinJS, а не корневого (содержащего их) элемента, имена целевых свойств должны начинаться с winControl. В противном случае вы осуществите привязку к несуществующим свойствам корневого элемента. При использовании winControl, привязываемое свойство служит той же цели, что и задаваемое фиксированное значение в data-win-options. Например, разметка, использованная выше в разделе "Пример: Элемент управления WinJS.UI.Rating (Оценки)" может использовать привязку данных для своих свойств averageRating и userRating, как показано ниже (подразумевая, что myData - это подходящий источник данных):
<div id="rating1" data-win-control="WinJS.UI.Rating"
data-win-options="{onchange: changeRating}"
data-win-bind="{winControl.averageRating: myData.average,
winControl.userRating: myData.rating}">
</div>
Повторюсь, что цель односторонней привязки - это обновление целевого свойства, обычно в элементе управления пользовательского интерфейса, когда изменяются данные в присоединенном свойстве источника данных. Таким образом, односторонняя привязка подразумевает эффективное повторение единовременной привязки каждый раз, когда состояние источника меняется.
В коде, который мы видели выше, если мы меняем login.name после вызова WinJS.Binding.processAll, с элементом управления, выводящим данные, ничего не происходит. Как же нам автоматически обновить выводимую информацию?
Вообще говоря, это требует, чтобы источник данных поддерживал список привязок (bindings), где каждая привязка может описывать свойство источника, свойство целевого объекта и функцию-конвертер. Источник данных, кроме того, нуждается в методах для управления этим списком, наподобие addBinding, removeBinding и так далее. И третье, если привязываемое (или наблюдаемое (observable)) свойство меняется, нужно пройтись по списку привязок и обновить соответствующим образом любые связанные свойства целевых объектов.
Эти требования выглядят достаточно обобщенными. Вы можете представить, что их реализация будет в значительной мере походить на классический шаблонный код. Поэтому, конечно, WinJS предоставляет такую реализацию! В этом контексте источники называются наблюдаемыми объектами (observable objects) и функция WinJS.Binding.as заключает любые объекты в объекты подобной структуры (она возвращает пустой оператор для необъектных сущностей). Наоборот, функция WinJS.Binding.unwrap удаляет эти структуры, если в том есть необходимость. Более того, функция WinJS.Binding.define создаёт конструктор для наблюдаемых объектов вокруг набора свойств (описанных с помощью чего-то вроде пустого объекта, содержащего лишь имена свойств). Подобный конструктор позволяет вам создать экземпляр объекта-источника динамически, как при обработке данных, полученных от онлайнового сервиса.
Давайте посмотрим на код. Вернемся к последнему примеру выше (Test 3), в любое время до или после WinJS.Binding.processAll мы можем взять объект LoginData.Login и сделать его наблюдаемым, как показано ниже:
var loginObservable = WinJS.Binding.as(LoginData.login)
На самом деле, это всё, что нам надо сделать - всё остальное будет таким же, как раньше. Мы можем сейчас изменить связанное значение в объекте loginObservable:
loginObservable.name = "liambro";
Эта команда обновит целевое свойство:
Вот как мы затем создаём и используем многократно используемый класс для наблюдаемого объекта (Test 4 в упражнении BindingTest). Обратите внимание на то, что объект, который мы передаем WinJS.Binding.define содержит имена свойств, но не значения (они будут проигнорированы):
WinJS.Namespace.define("LoginData", {
//...
//LoginClass становится конструктором для прикрепляемого объекта с заданными свойствами
LoginClass: WinJS.Binding.define({name: "", id: "", photoURL: "", userType: "" }),
});
Когда это готово, мы можем создавать экземпляры этого класса, инициализировать необходимые свойства. В данном упражнении, мы используем различные изображения и инициализированный ведущий userType:
var login4 = new LoginData.LoginClass({ name: "liamb",
photoURL: "http://www.kraigbrockschmidt.com/images/Liam08.jpg" });
Осуществляя привязку к этому объекту login, мы видим, что имя пользователя первоначально имеет черный цвет:
//Выполняем связывание
(первоначально имя пользователя выведено черным цветом)
WinJS.Binding.processAll(document.getElementById("loginDisplay"), login4);
Обновление свойства userType в источнике (как показано ниже), приведет к обновлению цвета целевого свойства, что, посредством конвертера, происходит автоматически:
login4.userType = "kid";
Процесс реализации двусторонней привязки достаточно очевиден:
Источник данных должен быть достаточно интеллектуальным для того, чтобы знать, когда новое значение свойства уже является тем же, что и в целевом свойстве, в таком случае он не попытается обновить целевое свойство. Иначе вы попадёте в бесконечный цикл. Код наблюдаемого объекта, который предоставляет WinJS, уже выполняет такую проверку.
Для того, чтобы увидеть пример реализации подобного механизма, обратитесь к примеру "Декларативная привязка" (http://code.msdn.microsoft.com/windowsapps/DeclarativeBinding-bfcb42a5) в SDK, где показано прослушивание события change в текстовых полях и соответствующее обновление свойств в их источниках данных.
Если вы посмотрите на описание WinJS.Binding (http://msdn.microsoft.com/library/windows/apps/br229775.aspx) в документации, вы увидите в этом пространстве имен множество других полезных вещей. Позвольте мне кратко описать их особенности. (Кроме того, обратитесь к примеру "Программная привязка данных" (http://code.msdn.microsoft.com/windowsapps/ProgrammaticBinding-de038b64) для демонстрации некоторых возможностей).
Если вы уже определили класс (из WinJS.Class.define) и хотите сделать его наблюдаемым, используйте WinJS.Class.mix, как показано ниже:
var MyObservableClass = WinJS.Class.mix(MyClass, WinJS.Binding.mixin, WinJS.Binding.expandProperties(MyClass));
WinJS.Binding.mixin содержит стандартную реализацию функций связывания, которые ожидает WinJS. WinJS.Binding.expandProperties создаёт объект, свойства которого соответствуют свойствам данного объекта (те же самые имена), каждое из которых заключено в подходящую для связывания структуру. На самом деле, этот тип операции полезен только тогда, когда выполняют смешивание, и это то, что WinJS.Binding.define делает с теми самыми странными, не имеющими значений объектами, которые мы передаем ему.
Если вы помните из предыдущего материала, одно из требований для наблюдаемых объектов заключается в том, чтобы они содержали методы для управления списком привязок. Реализация подобных методов содержится в объекте WinJS.Binding.observableMixin. Вот эти методы:
bind Сохраняет привязку (имя свойства и функцию для запуска при изменении).
unbind Удаляет привязку, созданную методом bind.
notify Просматривает привязку на предмет свойства и запускает функции, связанные с ним. Это то место, где WinJS проверяет, различаются ли старое и новое значения и где он обрабатывает случаи, когда обновление для того же самого целевого объекта уже выполняется.На это опирается еще один mixin-объект, WinJS.Binding.dynamicObservableMixin (то же, что и WinJS.Binding.mixin), который добавляет методы для управления свойствами объекта-источника:
setProperty Обновляет значение свойства и оповещает прослушивателей о том, что значение изменилось.
updateProperty Похож на setProperty, но возвращает promise-объект, который возвращает результат, когда все прослушиватели будут оповещены (ожидаемый результат - новое значение свойства).
getProperty Возвращает значение свойства в качестве наблюдаемого объекта, что делает возможным осуществлять привязку внутри вложенных объектных структур (obj1.obj2.prop3 и так далее).
addProperty Добавляет новое свойство к объекту, которое автоматически подготовлено к привязке данных.
removeProperty Удаляет свойство из объекта.Зачем нам всё это? Это открывает простор для творчества. Вы можете вызвать WinJS.Binding.bind, например, напрямую для любого наблюдаемого источника, когда вы хотите использовать другую функцию для свойства источника. Это похоже на добавление прослушивателей событий для изменения свойства источника, и у вас может быть столько прослушивателей, сколько хотите. Это полезно для настройки двунаправленной привязки данных, и эта возможность не связана с манипуляцией пользовательским интерфейсом. Функция просто вызывается при изменении свойства. Это можно использовать для автоматической синхронизации серверной службы с объектом данных.
Пример "Декларативная привязка данных" (http://code.msdn.microsoft.com/windowsapps/DeclarativeBinding-bfcb42a5), кроме того, показывает вызов bind с объектом в качестве второго параметра, форма, которая позволяет осуществлять привязку к вложенным членам источника данных. Синтаксис похож на этот bind(rootObject, { property: { sub-property: function(value) { ... } } } - что соответствует объекту-источнику. С подобным объектом в качестве второго параметра, bind убеждается в том, что активированы все функции, назначенные вложенным свойствам. В подобном случае значение, возвращаемое bind - это объект с методом cancel, который очистит эту сложную привязку данных.
Метод notify, в свою очередь, это то, что вы можете вызывать напрямую для инициации уведомлений. Это полезно с дополнительными привязками, которые не обязательно зависят от собственных значений, а только от факта собственного изменения. Основной вариант использования - реализация вычисляемых свойств - тех, которые изменяются в ответ на изменение значений других свойств.
WinJS.Binding, кроме того, имеет несколько интеллектуальных обработчиков множественных изменений одного и того же свойства объекта-источника. После первоначального связывания, дальнейшие уведомления об изменениях асинхронны, и множественные запросы на изменение того же самого свойства объединяются. В итоге, если в нашем примере мы выполним несколько изменений свойства name в быстрой последовательности:
login.name = "Kenichiro"; login.name = "Josh"; login.name = "Chris";
будет отправлено только одно оповещение о последнем значении, которое и станет значением, которое будет отображено на связанном целевом элементе.
И, наконец, имеется еще несколько функций, находящихся в WinJS.Binding:
onetime Функция, которая проходится по заданным целевым (принимающим) свойствам и устанавливает их в значение связанного свойства источника. Эту функцию можно использовать для настоящей единовременной привязки данных, как необходимо при привязке к объектам WinRT. Она так же может быть использован напрямую, как инициализатор, внутри data-win-bind, если источником данных является WinRT-объекта.
defaultBind Функция, которая делает то же самое, что и onetime, но устанавливает одностороннюю привязку между всеми заданными свойствами. Она так же служит как инициализатор по умолчанию для всех взаимодействий в data-win-control, когда специальный инициализатор не задан.
declarativeBind Реальная реализация processAll. (Они идентичны). В дополнение к обычным параметрам (корневой целевой элемент и контекст данных), она так же принимает параметр skipRoot (если он установлен в true, в процессе обработки не производится привязки к корневому элементу, только к его элементам-потомкам, что полезно для объектов шаблонов) и bindingCache (оптимизированное хранение результатов обработки выражения data-win-bind при обработке шаблонов объектов).В наших первых упражнениях мы видели использование функций-конвертеров, которые превращают некоторое количество данных из источника в формат, который ожидает получить свойство-приемник. Но функции, которые вы задаёте в data-win-bind правильнее будет называть инициализаторами (initializer), так как, на самом деле, они вызываются лишь один раз.
Интересно, почему? Разве конвертеры не используются всякий раз, когда связанное свойство источника копируется в целевое свойство? Хорошо, да, но мы, на самом деле, говорим здесь о двух разных функциях. Посмотрите вниманительно на структуру кода для функции userTypeColor, которую мы использовали ранее:
userTypeToColor: WinJS.Binding.converter(function (type) {
return type == "kid" ? "Orange" : "Black";
})
Функция userTypeColor - это инициализатор. Когда она вызывается - однажды и только однажды - она возвращает значение из WinJS.Binding.converter - конвертер, который затем будет использоваться для каждого обновления свойства. Это и есть настоящая функция-конвертер, а не userTypeColor - на самом деле - это структура, которая упаковывает анонимную функцию, заданную в WinJS.Binding.converter.
Если смотреть глубже, WinJS.Binding.converter, на самом деле, использует bind для того, чтобы установить взаимоотношения между свойством-источником и целевым свойством и вставляет анонимную функцию-конвертер в эти взаимоотношения. К счастью, вам не придётся иметь дело с подобными сложными механизмами и вы можете просто предоставить функцию-конвертер, как показано выше.
Тем не менее, если вы хотите увидеть пример подобного низкоуровневого кода, обратитесь снова к примеру "Декларативная привязка данных" (http://code.msdn.microsoft.com/windowsapps/DeclarativeBinding-bfcb42a5), так как он показывает как создать конвертер для сложных объектов напрямую в коде, без использования WinJS.Binding.converter. В данномм случае функция должна быть маркирована как безопасная для обработки, если на неё ссылаются в разметке. Другая функция, WinJS.Binding.initializer, существует для тех же целей; значение, возвращаемое WinJS.Binding.converter проходит через тот же самый метод, прежде чем оно вернется обратно в ваше приложение.
Вы думаете, что мы уже завершили разговор о WinJS.Bindint? Не вполне, друзья! Есть две части этого обширного API, которые ведут нас прямо в следующую лекцию. (И сейчас вы узнаете настоящую причину, по которой я разместил весь этот раздел там, где разместил!) Первая - это WinJS.Binding.List -источник данных-коллекция, подходящий для привязки, который - не удивительно - весьма полезен при работе с элементами управления-коллекциями.
WinJS.Binding.Template, так же, уникальный вид пользовательского элемента управления. При использовании, как вы могли видеть в примере "Декларативная привязка", вы объявляете элемент (обычно div) с data-win-control = "WinJS.Binding.Template". В той же самой разметке вы задаёте содержимое шаблона в качестве элементов-потомков, каждый из которых может иметь собственные атрибуты data-win-bind. Что в этом уникально, так это то, что когда WinJS.UI.process или processAll находят эту разметку, они создают экземпляр шаблона и убирают всё, кроме корневого элемента, из DOM. Так что же в этом хорошего?
Итак, так как шаблон существует, кто угодно может вызвать его метод render для того, чтобы создать копию этого шаблона внутри какого-нибудь другого элемента, используя какой-нибудь контекст данных для обработки любых атрибутов data-win-bind в нём (обычно пропуская корневой элемент, отсюда и параметр skipRoot в методе WinJS.Binding.declarativeBind). Кроме того, вывод шаблона несколко раз в том же самом элементе позволяет создать множество элементов одного уровня, каждый из которых может иметь различные источники данных.
Вот оно что! Теперь мы можем начать рассматривать, какой всё это имеет смысл для элементов управления для вывода коллекций и источников данных-коллекций. Имея источник данных-коллекцию и шаблон, вы можете обойти этот источник и вывести копию шаблона для каждого отдельного элемента в источнике в его собственном элементе. Добавьте немного возможностей навигации или макет, внутри которого содержится элемент, и вуаля! Вы у истоков того, что, как мы узнаем, является элементами управления WinJS.UI.FlipView и WinJS.UI.ListView, когда разберемся со следующей лекцией.
data-win-control, которые обрабатываются с использованием WinJS.UI.process или WinJS.UI.processAll.
new и подходящих конструкторов, таких, как Button или WinJS.UI.Rating.
data-win-options для элементов управления WinJS.
win-backbutton, которые используются для стилизации стандартных HTML-элементов управления для того, чтобы они выглядели как элементы управления, специфичные для Windows.
-ms-* позволяют вам адресовать специфические части этих элементов управления. В случае с элементами управления WinJS, отдельные части стилизуют с использованием классов win-*, которые можно переопределить.
addEventListener. Пользовательские элементы управления, кроме того, могут быть показаны в панели Активы (Assets) в Blend либо для отдельного проекта, либо для всех проектов.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.