Файлы к данной лекции Вы можете скачать здесь.
Одной из первых реакций у новых пользователей, не знакомых с Windows 8 и с приложениями для Магазина Windows, может быть следующая: "Где же меню? А лента? Как я могу указать приложению, чтобы оно сделало что-то с элементами, которые я выбрал из списка?". Это – вполне естественная реакция, которая возникает до тех пор, пока пользователи не привыкнут к расположению командных элементов управления, что дает еще одно значение, хотя и мирское, изречению: "Блаженны не видевшие и уверовавшие".
Следуя принципу дизайна "прежде всего содержимое, и лишь затем внешнее оформление", элементы пользовательского интерфейса, которые существуют исключительно для вызова некоторых действий, и, таким образом, не содержат значимого содержимого, попадают в категорию "внешнего оформления". Как таковые, они обычно скрыты до тех пор, пока не понадобятся, как команды системного уровня наподобие панели чудо-кнопок. Пользователь показывает желание воспользоваться подобными командами посредством соответствующего жеста. Это – скольжение от верхнего или нижнего краев экрана, щелчок правой кнопки мыши, или комбинация клавиш Win+Z, которые вызывают командные панели приложения, расположенные сверху или снизу. Скольжение от левого края экрана, щелчок мышью по верхнему левому углу экрана, или сочетание клавиш Win+Tab позволяет переключаться между приложениями. И скольжение от правого края экрана, щелчок мышью по верхнему правому или нижнему правому углам экрана, или сочетание клавиш Win+C открывает панель чудо-кнопок. (Сочетания клавиш Win+Q, Win+H, Win+i активируют, соответственно, чудо кнопки Поиск, Общий доступ и Параметры напрямую). Приложение реагирует на различные чудо-кнопки посредством реализации отдельных контрактов, как мы увидим в следующих лекциях.
Команды приложения, в свою очередь, обычно реализуются посредством панели приложения – элемента управления WinJS.UI.AppBar. Во многом, панель приложения – это эквивалент меню и ленты в приложениях для Магазина Windows, так как вы можете создать в них любые виды пользовательского интерфейса и даже отобразить элементы меню. Меню, которые поддерживает элемент управления WinJS.UI.Menu, могут, так же, всплывать из заданного места на основном экране приложения, как, например, меню прикрепленное к заголовку.
Панель приложения и меню – это конкретные экземпляры более общего элемента управления WinJS.UI.Flyout, который используется напрямую для вывода сообщений, или для действий, которые пользователь может отменить или проигнорировать. Подобные всплывающие элементы можно закрыть щелчком мыши или прикосновением за их пределами. (Это – что-то вроде нажатия на кнопку "Отмена"). Для показа важных сообщений, которые требуют какого-либо действий, то есть – тех, где пользователь должен выбрать из некоторого набора параметров, приложения могут использовать WinJS.UI.MessageDialog. Диалоговые окна – это всем известная концепция из мира настольных приложений, она уже долгое время используется для сбора самой разной информации и настройки параметров приложения. В дизайне приложений для Магазина Windows, однако, диалоговые окна используются лишь для задавания вопросов и получения простых ответов, или для того, чтобы сообщить пользователю некие важные сведения. Настройка параметров приложений реализуется посредством чудо-кнопки Параметры, как мы увидим в лекции 2.
В том, что касается всех этих команд, важно то, что они не включаются в макет страницы: они, вместо этого "всплывают" и располагаются над текущей страницей. Это означает, что мы, к счастью, можем не беспокоиться об их воздействии на макет… с одним маленьким исключением, которое я пока буду держать в секрете.
Для начала, тем не менее, вернемся назад и подумаем о командах приложения в целом, и о том, где лучше всего эти команды размещать.
Размещение команд имеет важное значение в дизайне приложений. В отличие от руководства, или, скорее, их отсутствия – для командных элементов управления настольных приложений, что приводило к беспорядку, центр разработчиков Windows предлагает два довольно обширных материала на эту тему: "Проектирование командного интерфейса для приложений Магазина Windows" (http://msdn.microsoft.com/library/windows/apps/hh761499). Эти материалы обязательны для прочтения для дизайнеров, работающих над приложением, так как они описывают различные виды командного интерфейса и рассказывают о том, как получить одобрение у профессионалов в области дизайна для Windows 8. Это так же отличные материалы для разработчиков, так как они могут дать вам некоторое понимание того, чего вы можете ожидать от ваших дизайнеров. Рассмотрим эти руководства, в качестве вводного тура по различным возможностям:




И, наконец, другие сообщения об ошибках, которые не требуют реакции пользователя, могут быть отображены либо на полотне приложения, либо посредством всплывающих элементов. Смотрите материал "Создание макета пользовательского интерфейса: Ошибки" (http://msdn.microsoft.com/library/windows/apps/hh465304.aspx#errors) для того, чтобы узнать дальнейшие подробности. Позже мы рассмотрим некоторые примеры.
Если говорить о нижней панели приложения, то важно организовать команды в наборы, так как это упрощает их реализацию, как мы увидим в следущем разделе. Для того, чтобы получить полную информацию по этому вопросу, я рекомендую обратиться к материалам "Руководство по панелям приложения" (http://msdn.microsoft.com/library/windows/apps/hh465302.aspx) и "Проектирование командного интерфейса для приложений Магазина Windows" (http://msdn.microsoft.com/library/windows/apps/hh761499.aspx). Они содержат множество подробностей о размещении, разделении и группировке. Вот выдержки из этих руководств:

Панель приложения доступна всегда, в любых состояниях просмотра, в том числе – в прикрепленном. Рекомендована в прикрепленном режиме (и иногда в портретном) ограничивать число команд до 10, чтобы они уместились в один или два ряда.
Знайте, так же, что панель приложения не ограничена круглыми командными кнопками. Вы можете создать все, что захотите, обычно именно так реализуются верхние панели приложений. Работая с любым собственным макетом, убедитесь, что ваши элементы имеют размер, подходящий для сенсорного взаимодействия. Больше об этом можно найти в материалах "Руководство по панелям приложения" (http://msdn.microsoft.com/library/windows/apps/hh465302.aspx) и "Проектирование взаимодействия с сенсорным экраном" (http://msdn.microsoft.com/library/windows/apps/hh465415.aspx) в разделе "Целевые объекты сенсорного ввода Windows 8". Там можно увидеть и небольшую картинку с изображением вышеупомянутых пальцев баскетболиста.
Как отмечено выше, Microsoft рекомендует, чтобы ввод пользовательских даных для входа в программу, управление учетными данными, лицензионные соглашения, условия использования программы были доступны посредством чудо-кнопки Параметры. При этом программа добавляет необходимые команды на панель чудо-кнопки Параметры, которая появляется при активации этой чудо-кнопки. Эти команды затем вызывают вспомогательные страницы с необходимыми элементами управления для каждой функции. Конечно, иногда вход в систему или лицензионные соглашения требуют специальной обработки. Например, если ваше приложение требует ввода данных пользователя или принятия лицензионного соглашения при старте, подобные элементы управления можно отобразить на первой странице приложения или предоставить их посредством интерфейса средства выбора учетных даных (Credential Picker UI) (подробнее об этом – в лекции 3 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой"). Если пользователь вводит учетные данные и/или принимает лицензионное соглашение, приложение может продолжить работу. В противном случае приложению следует показать страницу, которая указывает на то, что учетные данные или принятие соглашения необходимы для того, чтобы пользователь мог с его помощью сделать что-нибудь более интересное, чем вывод сообщений об ошибках.
Если ввод учетных данных рекомендован, но не является необходимым, возможно, нужен лишь для включения дополнительных возможностей, вы можете разместить соответствующие элементы управления прямо на полотне. Когда пользователь вводит учетные данные, вы можете заменить эти элементы управления на сведения об учетной записи (имя пользователя и изображение, например, как на Начальном экране Windows). Если, с другой стороны, ввод учетных данных необязателен, соответствующие элементы управления лучше всего размещать в Параметрах приложения.
Во всех случаях, команды для просмотра лицензионного соглашения, для управления учетными записями или профилями, для входа в программу или выхода из нее, следует размещать среди средств чудо-кнопки Параметры. Другие команды на панели приложения или на полотне могут активировать Параметры программным образом, как мы увидим в лекции 2.
После размещения самых важных команд на полотне приложения, большинство команд будет размещено в панели приложения. Повторюсь, панель приложения можно вызвать разными способами – жестом скольжения от верхнего или нижнего края экрана, комбинацией клавиш Win+Z, щелчком правой кнопки мыши. Когда вы выполните одно из этих действий, Windows ищет элемент управления панели приложения на текущей странице и запускает его – вам не нужно самостоятельно обрабатывать события ввода.
Совет. Для того чтобы предотвратить появление панели приложения, вы можете использовать один из двух подходов. Первый заключается в том, чтобы полностью отключить появление панели приложения в ответ на любой из жестов, для этого нужно установить свойство winControl.disabled элемента панели в значение true. Второй применим тогда, когда вы, например, не хотите, чтобы панель приложения появлялась при щелчке правой кнопкой мыши по конкретному элементу (например, по полотну). Для этого можно прослушивать событие
contextmenu (то есть – щелчок правой кнопки мыши) для этого элемента и вызвать eventArgs.preventDefault() в обработчике события.
В приложениях, написанных на HTML и JavaScript, элемент управления панели приложения реализован как элемент управления WinJS:
WinJS.UI.AppBar
(http://msdn.microsoft.com/library/windows/apps/br229670.aspx). Для первой демонстрации нам достаточно тех возможностей, которые представлены в шаблоне проекта Приложение таблицы в Visual Studio/Blend, где стандартная панель приложения включена в default.html (изначально закомментирована):
<div id="appbar" data-win-control="WinJS.UI.AppBar">
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}">
</button>
</div>
Восхитительный результат этой разметки, с использованием таблицы стилей ui-dark.css, показан ниже:
Так как панель приложения объявлена в default.html, которая является контейнером для всех других элементов управления страниц, та же панель приложения будет использована для всех остальных страниц приложения. Используя такой подход, вы можете объявить все команды на одной панели приложения и присваивать командам различные классы, что позволит вам легко показывать и скрывать наборы команд для соответствующих страниц. Это так же позволяет централизованно управлять теми командами, которые актуальны для нескольких страниц, и вы можете подключить к ним обработчики событий в основном коде активации приложения (например, в default.js).
Кроме того, вы можете объявить панель приложения в разметке отдельных элементов управления страниц. До тех пор, пока панель приложения находится в DOM, жесты Windows активируют ее на каждой странице. В проекте Приложение таблицы, например, вы можете переместить вышеприведенную разметку из default.html в groupedItems.html, groupDetail.html и itemDetail.html с любыми модификациями для каждой страницы. Это может быть особенно полезным, если у страниц приложения немного общих команд.
В подобных случаях, метод ready каждой страницы должен позаботиться о том, чтобы привязать команды на панелях приложения этих страниц к обработчикам. Обратите внимание на то, что вы можете добавлять обрабочтики внутри метода страницы ready даже для общей панели приложения. Это – вопрос вызова addEventListener для нужного дочернего элемента в панели приложения.
Посмотрим теперь как все это работает с помощью примера "HTML-элемен управления AppBar" (http://code.msdn.microsoft.com/windowsapps/App-bar-sample-a57eeae9). (Дополнительные материалы к этой лекции так же содержат модифицированную версию этого примера). Начнем с основ панели приложения и со стандартной конфигурации, ориентированной на команды. Посмотрим, как показывать меню для некоторых из этих команд, и затем посомтрим, как можно создать собственный макет, как это делается для верхней панели приложения.
Подсказка. Чисто технически, вы можете объявить столько панелей приложения, сколько вам нужно, на любой странице, и все они будут представлены в DOM. Однако, последней из них, которая будет обрабатываться, будет та, что расположена на самом верху по оси Z по умолчанию, и, таким образом, она одна будет получать события. Windows не делает попыток комбинирования панелей приложения, в итоге, потому что элементы управления страниц вставляются в середину главной страницы наподобие default.html, панель приложения в default.html, которая объявляется после элемента управления страницы, появится над панелью приложения, объявленной на странице. В то же время, если панель приложения элемента управления страницы больше, чем в default.html, ее часть может быть видимой. В итоге: объявляйте панели приложения или на главной странице, или на элементах управления страниц, но не и там и там одновременно.
Как я уже говорил, может быть объявлена одна панель приложениян на все приложение, например, на странице-контейнере default.html, или собственную панель приложения можно объявить для каждого элемента управления страницы. Пример, посвященный элементу управления HTML AppBar, реализует последний вариант, так как он в нем показаны очень разные панели приложения, реализующие различные сценарии.
Сценарий 1 в примере (html/create-appbar.html) объявляет панель приложения с четырьмя командами и разделителем:
<div id="createAppBar" data-win-control="WinJS.UI.AppBar" data-win-options="">
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdAdd',
label:'Add', icon:'add', section:'global', tooltip:'Add item'}">
</button>
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdRemove',
label:'Remove', icon:'remove', section:'global', tooltip:'Remove item'}">
</button>
<hr data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{type:'separator', section:'global'}" />
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdDelete',
label:'Delete', icon:'delete', section:'global', tooltip:'Delete item'}">
</button>
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdCamera',
label:'Camera', icon:'camera', section:'selection', tooltip:'Take a picture'}">
</button>
</div>
В приложении это выглядит так, как показано ниже, с использованием таблицы стилей ui-light.css. Здесь мы так же можем видеть всплывающую подсказку, прямоугольник фокуса ввода и эффект, применяемый при зависании указателя мыши над командой Add (Добавить) (для того, чтобы все это увидеть, я разместил указатель мыши над этой командой):
В разметке, элемент управления панель приложения объявлен так же, как другие элементы управления WinJS (это становится привычкой!), с использованием элемента-контейнера (это div) с data-win-control="WinJS.UI.AppBar". Каждая страница в этом примере загружается с помощью WinJS.UI.Pages.render, которая удобна тем, что вызывает WinJS.UI.processAll для создания экземпляра панели приложения. (Можно, как и для других элементов управления, программно создавать панель приложения, используя оператор new).
Этот пример не предусматривает каких-либо специальных параметров для панели приложения в data-win-options, но имеется ряд возможностей:
disabled, если установлено в значение true, создается изначально отключенная панель приложения. Значение по умолчанию – false.layout может принимать значение "commands" (по умолчанию) или "custom", как мы увидим в разделе "Пользовательские панели приложений и панели навигации" ниже.placement может быть либо "top", либо "bottom" (по умолчанию). Далее мы используем значение "top" для навигационной панелиsticky изменяет поведение панели приложения, когда она исчезает с экрана, если в ней нет необходимости. Со значением по умолчанию, установленным в false, панель приложения исчезает, когда вы щелкнете мышью или коснетесь экрана за ее пределами. Если параметр установлен в true, панель приложения будет оставаться видимой до тех пор, пока вы не измените параметр на false, и пользователь не коснется экрана за ее пределами, или вы программно не скроете ее с помощью метода hide.Если вам нужна постоянно видимая панель приложения с пользовательским макетом, отображаемая в верхней части экрана, вы можете воспользоваться размеркой, подобной этой:
<div id="navBar" data-win-control="WinJS.UI.AppBar"
data-win-options="{layout:'custom', placement:'top', sticky: true}">
Обратите внимание на то, что две панели приложения на странице, с различными значениями параметра placement не мешают друг другу. Кроме того, свойство sticky для каждого из расположений панелей действует независимо. Таким образом, если вам нужно реализовать верхнюю навигационную панель в масштабах всего приложения, вы можете объявить ее в default.htm (или в другой подобной странице) и объявить нижние панели приложения на каждом элементе управления страницы. Опять же, это всего лишь элементы в DOM!
Как вы можете видеть, элемент управления панели приложения может содержать любое количество дочерних элементов для своих команд, каждый из которых должен быть элементом управления ) внутри элемента button или hr, или экземпляр панели приложения не будет создан.
Свойства и параметры команд панели приложения перечислены ниже:
id Идентификатор элемента, который вы можете использовать с командой document.getElementById или с методом панели приложения getCommandById для подключения обработчиков события click.type Может принимать несколько значений: "button" (по умолчанию), "separator"(с его помощью создаются вертикальные разделители), "flyout" (используется для создания всплывающих меню, которые описывает свойство flyout; смотрите дальше раздел "Командные меню"), и "toggle" (создающий кнопку, которая может быть во включенном либо выключенном состоянии). В последнем случае, свойство selected команды может быть использовано для установки начального значения и получения состояния кнопки во время выполнения программы.label Задает текст, который отображается ниже командной кнопки. Его используют всегда вместо того, чтобы задавать текстовое содержимое самой кнопки, так как подобный текст не выравнивается соответствующим образом в элементе управления. (Попробуйте это сделать и увидите сами!). Кроме того, обратите внимание на то, что это свойство, наряду со свойством tooltip , которое описано ниже, часто локализуют, используя атрибут data-win-res . Мы рассмотрим это в лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", но уже сейчас вы можете посмотреть файл html/localize-appbar.html в примере Сценарий 8 для того, чтобы увидеть, как это работает.tooltip Содержит текст всплывающей подсказки для команды (обычно локализованный), в качестве значения по умолчанию используется текст, заданный в label. Обратите внимание на то, что это – обычный текст. Использование полнофункционального HTML- элемента управления WinJS.UI.Tooltip не поддерживается.\uE109 . Перечисление, фактически, просто предоставляет понятные имена для кодов символов – от \uE100 до \uE1E9 . Но вы не ограничены этими символами. Вы можете использовать любые escape-коды в формате ‘\uXXXX’ из шрифта Segoe UI Symbol (Обратите внимание на одиночные кавычки). Так же вы можете воспользоваться другими шрифтами или использовать собственные изображения, как показано в разделе "Пользовательские значки" section Управляет расположением команд. Для языков, где тексты расположены слева направо (таких, как английский), значение по умолчанию "selection" располагает команды (контекстно-зависимые) в левой части панели приложения. Свойство "global" располагает команды (глобальные для приложения) с правой стороны. В языках, где пишут справа налево (таких, как еврейский, арабский), стороны меняются местами. Эти простые варианты выбора приводят к единообразному расположению этих двух категорий команд (значение по умолчанию – "selection" ). Это притягивает взгляд пользователей к одной стороне панели, если они ищут постоянные команды, и к другой, если ищут команды, зависящие от контекста. Обратите внимание на то, что команды в каждом из разделов располагаются слева направо (или справа налево) в том порядке, в котором они появляются в разметке.onClick Может быть использовано для декларативного задания обработчика события click . Помните, что любая функция, упомянутая в разметке, должна быть помечена как безопасная для обработки (смотрите Главу 4 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript"). Обработчик события click может быть так же задан программно, с помощью addEventListener , в таком случае маркировка не нужна.disabled Отключает команду, если установлено в true . Значение по умолчанию – false .extraClass Задает один или большее количество классов CSS, которые прикреплены к команде. Они могут использоваться для индивидуальной стилизации командных элементов управления и для создания наборов команд, которые можно легко отображать и скрывать, как показано в разделе "Отображение, скрытие, включение и обновление команд" ниже.section Управляет расположением команд. Для языков, где тексты расположены слева направо (таких, как английский), значение по умолчанию "selection" располагает команды (контекстно-зависимые) в левой части панели приложения. Свойство "global" располагает команды (глобальные для приложения) с правой стороны. В языках, где пишут справа налево (таких, как еврейский, арабский), стороны меняются местами. Эти простые варианты выбора приводят к единообразному расположению этих двух категорий команд (значение по умолчанию – "selection" ). Это притягивает взгляд пользователей к одной стороне панели, если они ищут постоянные команды, и к другой, если ищут команды, зависящие от контекста. Обратите внимание на то, что команды в каждом из разделов располагаются слева направо (или справа налево) в том порядке, в котором они появляются в разметке.onClick Может быть использовано для декларативного задания обработчика события click . Помните, что любая функция, упомянутая в разметке, должна быть помечена как безопасная для обработки (смотрите Главу 4 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript"). Обработчик события click может быть так же задан программно, с помощью addEventListener , в таком случае маркировка не нужна.disabled Отключает команду, если установлено в true . Значение по умолчанию – false .extraClass Задает один или большее количество классов CSS, которые прикреплены к команде. Они могут использоваться для индивидуальной стилизации командных элементов управления и для создания наборов команд, которые можно легко отображать и скрывать, как показано в разделе "Отображение, скрытие, включение и обновление команд" ниже.Если вы хотите создавать команды во время выполнения приложения, вы можете сделать это, присвоив свойству панели приложения commands JSON-массив
AppBarCommand в любое время, когда панель приложения не видна (то есть, когда ее свойство hidden имеет значение true). Массив подобных дескрипторов из Сценария 1 нашего примера может выглядеть так (вы можете найти этот код в модифицированном примере к этой лекции; смотрите файл js/create_appbar.js):
var appbar = document.getElementById("createAppBar").winControl;
//Set the app bar commands property to populate it
var commands = [
{ id: 'cmdAdd', label: 'Add', icon: 'add', section: 'global', tooltip: 'Add item' },
{ id: 'cmdRemove', label: 'Remove', icon: 'remove', section: 'global', tooltip: 'Remove item' },
{ type: 'separator', section: 'global' },
{ id: 'cmdDelete', label: 'Delete', icon: 'delete', section: 'global', tooltip: 'Delete item' },
{ id: 'cmdCamera', label: 'Camera', icon: 'camera', section: 'selection',tooltip: 'Take a picture' }
];
appbar.commands = commands;
Когда создается панель приложения, происходит перебор массива commands и создание элементов управления WinJS.UI.AppBarCommand для каждого элемента. Если параметр type не задан или если он установлен в "button", "flyout" или "toggle", тогда создается командный элемент button. При параметре type, установленном в значение "separator" создается элемент hr. Обратите внимание на то, что вам следует локализовать поля label, tooltip, и, возможно, icon в каждом описании команды, вместо того, чтобы использовать встроенный текст, как показано здесь, в примерах кода.
Так же вы можете использовать подобный массив непосредственно в декларативной разметке, но подобная конструкция не поддается локализации и, таким образом, не рекомендуется (хотя я, в модифицированном примере, включил комментарии, которые показывают, как это делается). В то же время, так как значение commands в разметке – это обычная строка, вы можете присваивать данное значение посредством механизмов привязки данных к атрибутам, наподобие этого в элементе панели приложения:
data-win-bind="{ winControl.commands: myData.commands }"
Однако подобный подход не работает для атрибута data-win-res (как мы увидим в лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", и как, кроме того, показано в Сценарии 8 текущего примера), так как строка ресурса не может быть конвертирована в JSON как часть поиска ресурса. Попытка выполнить подобный трюк, в любом случае, принесет больше сложностей, чем преимуществ, поэтому лучше использовать либо декларативную форму описания в HTML, либо программное подключение локализованных массивов команд во время выполнения приложения.
Так же следует помнить о том, что commands – это редкий пример свойства только для записи (write-only property). Вы можете записать в него данные, но не можете получить массив из панели приложения. Панель приложения использует этот массив только для задания собственной конфигурирации и массив отбрасывается сразу после того, как все элементы созданы в DOM. Во время выполнения приложения, однако, вы можете использовать метод панели приложения getCommandById для получения конкретного командного элемента.
Говоря о командных элементах, об элементах управления AppbarCommand панели приложения (о тех, которые не являются разделителями), все это – лишь элементы button , и, таким образом, они реагируют на обычные события. Так как каждому командному элементу присвоен заданный вами id, вы можете использовать getElementById , как обычно, перед добавлением addEventListener . В Сценарии 1 примера, посвященного элементу управления HTML AppBar, например, подобный код можно увидеть в методе страницы ready :
document.getElementById("cmdAdd").addEventListener("click", doClickAdd, false);
document.getElementById("cmdRemove").addEventListener("click", doClickRemove, false);
document.getElementById("cmdDelete").addEventListener("click", doClickDelete, false);
document.getElementById("cmdCamera").addEventListener("click", doClickCamera, false);
Хотя такой подход работает, он относительно неэффективен, так как каждый вызов document.getElementById обходит всю DOM. Я рекомендую, вместо этого, использовать метод панели приложения getCommandById. Вот изменения, которые я внес в модифицированный пример, который можно найти в дополнительных материалах к этой лекции:
//Использование метода getCommandById панели приложения избавляет от необходимости обходить всю DOM для каждой кнопки
var appbar = document.getElementById("createAppBar").winControl;
appbar.getCommandById("cmdAdd").addEventListener("click", doClickAdd, false);
appbar.getCommandById("cmdRemove").addEventListener("click", doClickRemove, false);
appbar.getCommandById("cmdDelete").addEventListener("click", doClickDelete, false);
appbar.getCommandById("cmdCamera").addEventListener("click", doClickCamera, false);
Конечно, если вы зададите обработчик для свойства onclick каждой команды в разметке (эти обработчики должны иметь свойство supportedForProcessing, установленное в значение true), вы полностью устраните необходимость в подобных действиях!
Так же должно быть очевидно то, что можно подключать обработчики событий, подобные этим, в любом месте приложения, и вы можете, конечно, прослушивать любые другие события, особенно, когда задаете собственный макет панели приложения с иным пользовательским интфрейсом. Кроме того, знайте, что событие click удобно объединяет обработку касаний, нажатий мыши, событий ввода данных с клавиатуры, таким образом, в данной области вам не нужно выполнять дополнительные действия. В случае с клавиатурой, кстати, панель приложения позволяет вам перемещаться между командами с помощью клавиши Tab. Нажатие на Enter или на клавишу пробела активирует обработчик click.
В дополнение к методу панели приложения getCommandById , который мы только что рассмотрели, панель приложения имеет несколько других методов и событий. Во-первых, вот методы:
show отображает панель приложения, если ее свойство disabled установлено в значение false . В противном случае вызов игнорируется.hide скрывает панель приложения.showCommands, hideCommands и showOnlyCommands используются для управления наборами команд, как описано в следующем разделе "Показ, скрытие и обновление команд".Что касается событий, то их всего четыре, что обычно для элементов управления WinJS, которые накладываются на пользовательский интерфейс (то есть, для тех, которые не включаются в состав макета приложения):
beforeshow происходит перед тем, как всплывающий элемент станет видимым. Для панели приложения, это время, когда вы можете задать свойство commands в зависимости от состояния приложения в данный момент, активировать или деактивировать отдельные команды.aftershow происходит сразу после того, как всплывающий элемент станет видимым. Для панели приложения, если ее свойство sticky установлено в значение true , вы можете использовать это приложение для подстройки макета приложения, если у вас есть элементы для прокрутки содержимого, которые могут быть, в противном случае, частично перекрыты (смотрите ниже).beforehide происходит перед тем, как всплывающий элемент будет скрыт. Для панели приложения, вы можете использовать это событие для скрытия вспомогательныхз элементов пользовательского интерфейса, созданных вместе с панелью приложения и для перенастройки макета вокруг панели приложения с включенным режимом sticky .afterhide происходит сразу после того, как всплывающий элемент будет скрыт. Для панели приложения, это может быть временем перенастройки макета приложения, если в этом есть необходимость.Вы можете найти пример использования метода show вместе с событиями aftershow и beforeshow в Сценарии 4 примера, посвященного элементу управления HTML AppBar.
Проблемы с макетом приложения, названные выше (и то, что я держал в секрете во введении к этой лекции) возникают потому, что панель приложения накладывается на нижную часть страницы и перекрывает ее. Если эта страница содержит элемент управления для прокрутки содержимого, панель приложения со свойством sticky , установленным в true , для тех пользователей, которые применяют мышь, частично перекрывает вертикальную полосу прокрутки, а горизонтальную полосу прокрутки делает полностью недоступной. Если вы используете залипающую (sticky) панель приложения на подобных страницах, тогда, тогда – так как политика Магазина Windows не поддерживают ущемление прав пользователей мышей, вам следует использовать aftershow для уменьшения высоты элементов прокрутки с помощью значения панели приложения offsetHeight или clientHeight , таким образом, оставляя полосы прокрутки доступными. Когда панель приложения скрывается и происходит событие afterhide , вы можете снова перенастроить макет. Всегда используйте то значение, которое имеется во время выполнения приложения, такое, как clientHeight в данных вычислениях, так как оно учитывает масштабирование разрешения и потому, что высота панели приложения может меняться в зависимости от содержащихся на ней команд и режима просмотра.
Для того чтобы показать это, в Сценарии 6 имеется горизонтально прокручиваемый элемент управления ListView, который, в обычном состоянии, занимает почти всю страницу. Полоса прокрутки отображается вдоль самой нижней части элемента при использовании мыши. Если вы выделяете элемент, панель приложения переводится в режим залипания и отображается (смотрите функцию doSelectItem в файле js/appbar-listview.js):
appBar.sticky = true; appBar.show();
Вызов метода show приводит к вызову событий beforeshow и aftershow . Для подстройки макета подходит событие aftershow , которое позволяет работать с реальным значением высоты панели приложения. В примере это событие обрабатывается в функции doAppBarShow (так же в файле js/appbar-listview.js):
function doAppBarShow() {
var listView = document.getElementById("scenarioListView");
var appBar = document.getElementById("scenarioAppBar");
var appBarHeight = appBar.offsetHeight;
// Сделать полосу прокрутки видиой, если панель приложения в режиме залипания
if (appBar.winControl.sticky) {
var listViewTargetHeight = "calc(100% - " + appBarHeight + "px)";
var transition = { property: 'height', duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 0.1)",
to: listViewTargetHeight
};
WinJS.UI.executeTransition(listView, transition);
}
}
Примечание. Пример из Центра разработчиков Windows использует beforeshow вместо aftershow , в результате, иногда полученная высота панели приложения равняется нулю и макет не подстраивается соответствующим образом. Для того чтобы гарантировать то, что панель приложения имеет подходящую высоту для подобных вычислений, используйте событие aftershow , как показано в модифицированном примере, включенном в состав дополнительных материалов к этой лекции.
Здесь вы можете видеть, что значение appBar.offsetHeight просто вычитается из значения height элемента управления ListView с применением анимированного перехода. (Смотрите Главу 5). Операция выполняется в обратном порядке в doAppBarHide , где высота ListView просто сбрасывается до 100% с использованием похожей анимации. В этом случае обработчик события совсем не зависит от высоты панели приложения, поэтому мы можем использовать либо событие beforehide, либо – afterhide . Если, с другой стороны, вам нужно знать размер панели приложения для настройки вашего макета, используйте событие beforehide .
В качестве эксперимента, запустите Сценарий 7 из SDK-примера. Обратите внимание на то, как нижняя часть полосы прокрутки текстовой области перекрывается залипающей панелью приложения. Попытайтесь взять код из Сценария 6 для того, чтобы используя события aftershow и beforehide настроить высоту текстовой области и сохранить видимость полосы прокрутки. И не считайте это оценочным испытанием ваших возможностей: в дополнительных материалах к этой лекции есть решение этой задачи.
В предыдущем разделе я упоминал об использовании события beforeshow для настройки свойства commands панели приложения таким образом, чтобы панель содержала команды, соответствующие текущей странице и состоянию страницы. Это может включать установку свойства disabled для некоторых команд, которые, например, зависят от состояния выделения. Это можно реализовать с помощью массива commands, в разметке, или, опять же, используя метод панели приложения getCommandById:
appbar.getCommandById("cmdAdd").disabled = true;
Позвольте мне повторить, что команды, которые появляются на панели приложения, уникальны для каждой страницы. Нет необходимости пытаться поддерживать неизменную структуру панели приложения для всех страниц. Таким образом, если команды не нужны на какой-либо странице, не беспокойтесь о том, чтобы их отображать. Что важнее, так это то, постоянный состав панели приложения для отдельно взятой страницы, так как это по-настоящему неудачная идея – появляющиеся и исчезающие команды, зависящие от состояния страницы. Это приведет к тому, что пользователи будут размышлять о том, как привести страницу в нужное состояние для того, чтобы появились какие-либо команды!
Говоря об изменениях, вполне допустимо изменять или обновлять команды во время выполнения программы, что может устранить необходимость создания множества команд, которые вы можете поочередно показывать или скрывать. Так как каждая команда в панели приложения – это лишь DOM-элемент, вы можете, на самом деле, выполнять любые изменения, какие хотите, в любое время. Пример этого показан в Сценарии 3 примера, где панель приложения изначально создается с кнопкой Play (html/custom-icons.html):
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmdPlay', label:'Play', icon:'play', tooltip:'Play this song'}">
</button>
Обработчик события click этой кнопки использует функцию doCluckPlay в js/custom-icons.js для переключения между состояниями:
var isPaused = true;
function doClickPlay() {
var cmd = document.getElementById('cmdPlay');
if (!isPaused) {
isPaused = true; // поставлено на паузу cmd.winControl.icon = 'play';
cmd.winControl.label = 'Play'; cmd.winControl.tooltip = 'Play this song';
} else {
isPaused = false; // проигрывается cmd.winControl.icon = 'pause';
cmd.winControl.label = 'Pause';
cmd.winControl.tooltip = 'Pause this song';
}
}
Вы можете использовать что-то подобное для закрепления или открепления дополнительных плиток (secondary tiles), как мы увидим в лекции 2 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой". И, снова, кнопки – это лишь элементы в DOM и обновление любых их свойств, включая стили, обновит элемент на экране, как только вы вернете управление в поток пользовательского интерфейса.
Использование beforeshow для целей настройки команд, безусловно, эффективно, но вы можете достичь тех же результатов другими способами. Стратегия, которой вы пользуетесь, зависит от архитектуры вашего приложения и от ваших собственных предпочтений. С точки зрения пользователя, до тех пор, пока нужные команды доступны ему в нужное время, неважно, как это реализовано в программе!
Размышления в рамках выбранного подхода особенно важны при работе с режимом прикрепленного просмотра, так как рекомендовано, чтобы в подобном режиме использовалось десять или меньшее количество команд, таким образом, панель приложения вместит их в одном или в двух рядах. Это означает, что вам нужно подумать о том, как настроить панель приложения для различных состояний просмотра, возможно, комбинируя несколько команд во всплывающем меню отдельных кнопок.
Один из подходов заключается в том, чтобы каждая страница приложения объявляла и поддерживала собственную панель приложения, что подразумевает создание панелей приложения "на лету" в методе страниц ready. Это делает взаимодействие между содержимым страницы и панелью приложения весьма четким и ограниченным рамками страницы. Недостаток такого подхода заключается в том, что общие команды – те, которые появляются более чем на одной странице – приходится объявлять множество раз, что ухудшает возможности по управлению ими и способствует привлечению небольшой неоднородности, которая приходит как муравьи на сахар. Тем не менее, если содержимое разных страниц сильно отличается и у вас есть лишь несколько общих команд, такой подход может быть правильным выбором. Это, так же, необходимо, если ваша страница использует несколько страниц верхнего уровня, вместо одной страницы с элементом управления страницы, как мы обсуждали в
лекции 3курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", так как каждая HTML-страница верхнего уровня, в любом случае, должна объявить свою собственную панель приложения.
Для приложений, использующих элементы управления страниц, применяется другой подход, который заключается в объявлении одной панели приложения на странице верхнего уровня и в установке ее свойства command внутри метода ready каждого из подобных элементов управления. Недостатком здесь является то, что так как command – это свойство только для записи, вы не можете объявить общие команды в HTML и позже добавить команды, характерные для отдельной страницы, только если вы не идете сложным путем создания каждого отдельного дочернего элемента AppBar в каждом методе ready . Подобный код утомительно писать и поддерживать.
К счастью, есть и третий подход, который позволяет вам объявить одну панель приложения на странице верхнего уровня, которая содержит все необходимые команды, для всех страниц, и затем выборочно показывать определенные наборы таких команд в каждом из методов ready отдельных страниц. Это – цель существования методов панели приложения showCommands, hideCommands и showOnlyCommands .
Все эти методы прпнимают массив командных элементов, которые могут быть либо объектами AppBarCommand , либо id команд. Метод showCommands делает эти командные элементы управления видимыми и может быть вызван несколько раз с различными установками для получения итогового набора. С другой стороны, метод hideCommands скрывает заданные команды в панели приложения, опять же, с возможностью многократного вызова. Основы использования этих методов показаны в Сценарии 4 нашего примера.
Метод showOnlyCommands объединяет два вышеописанных метода, делая заданные команды видимыми и скрывая остальные. Если вы задаете панель приложения со всеми командами, вы можете использовать метод showOnlyCommands в методе ready каждой страницы для того, чтобы быстро и просто настроить вид панели приложения. Хитрость заключается в получении массива, подходящего для передачи в метод. Вы можете, конечно, вручную написать код для команд, занести данные в специальный массив, как в Сценарии 4 примера сделано для showCommands и hideCommands . Однако, если вы думаете, что это хороший пример Плохой идеи, то знайте, что я тоже так думаю! Использование подобного массива подразумевает, что любые изменения, которые вы вносите в панель приложения, должны произойти и в HTML-файле, и в JS-файле, а это означает, что любой, кто будет поддерживать ваш код в будущем, без сомнения, станет осыпать вас проклятиями!
Лучший путь к счастью и долгой жизни, таким образом – программно получать необходимые массивы из DOM, используя свойство extraClass для каждой команды для того, чтобы эффективно задавать наборы команд. Это позволяет вам вызывать querySelectorAll для получения тех команд, которые принадлежат опреденному набору.
Рассмотрим нижеприведенное определение панели приложения, где, для краткости, я опустил свойства вроде label, icon и section , так же, как и любые другие классы стилей:
<div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="{
commands:[
{id:'home', extraClass: 'menuView gameView scoreView'},
{id:'play', extraClass: 'menuView gameView scoreView'},
{id:'rules', extraClass: 'menuView gameView scoreView'},
{id:'scores', extraClass: 'menuView gameView scoreView'},
{id:'newgame', extraClass: 'gameView gameSnapView'},
{id:'resetgame', extraClass: 'gameView gameSnapView'},
{id:'loadgame', extraClass: 'gameView gameSnapView'},
{id:'savegame', extraClass: 'gameView gameSnapView'},
{id:'hint', extraClass: 'gameView gameSnapView'},
{id:'timer', extraClass: 'gameView gameSnapView'},
{id:'pause', extraClass: 'gameView gameSnapView'},
{id:'home2', extraClass: 'gameSnapView'},
{id:'replaygame', extraClass: 'scoreView'},
{id:'resetscores', extraClass: 'scoreView'}
]}" >
</div >
В свойствах extraClass мы задаем четыре различных набора: menuView, gameView, gameSnapView, и scoreView. Когда все находится там, где нужно, простой вызов querySelectorAll предоставляет тот массив, который нам нужен для showOnlyCommands. Общая функция, наподобие следующей, может быть, затем, использована из метода ready каждой страницы (или из любого другого места) для активации командных элементов для нужного экрана программы:
function updateAppBar(view) {
var appbar = document.getElementById("appbar").winControl;
var commands = appbar.element.querySelectorAll(view);
appbar.showOnlyCommands(commands);
}
При таком подходе, заслуга создания которого принадлежит моему колеге Джесси МакГата, панель приложения полностью определяется в едином месте, что серьезно упрощает управление ей и ее поддержку.
Свойство команд extraClass может, конечно, быть использовано для целей стилизации, так же, как и для управления наборами команд. Это очень просто: любые классы, которые заданы в extraClass, добавляются к элементам AppBarCommand, созданных для панели приложения.
Существуют семь классов WinJS для стилизации, которые используются панелью приложения, как описано в нижеприведенной таблице, где первые два применяются к панели приложения в целом, а другие пять – к отдельным командным элементам:
| CSS-класс (панель приложения) | Описание |
|---|---|
win-appbar |
Задает стиль контейнера панели приложения. Обычно этот стиль используется в качестве корневого для более специфических селекторов. |
win-commandlayout |
Задает стиль макета команд панели приложения. Обычно приложения не модифицируют этот стиль |
| CSS-класс (команды) | Описание |
win-command |
Задает стиль всех элементов AppBarCommand. |
win-commandicon |
Задает стиль контейнера элемента для AppBarCommand. |
win-commandimage |
Задает стиль изображения для AppBarCommand. |
win-commandring |
Задает стиль круглой рамки, которая выводится вокруг изображения на AppBarCommand. |
win-label |
Стилизует подпись AppBarCommand. |
Подсказка. Для того чтобы помочь себе в настройке стилей панели приложения в Blend сделайте ее залипающей (sticky ), или добавьте вызов метода show в методе ready страницы, или в обработчике события activated . Это позволит сделать панель приложения видимой в Blend, работать с ней, в противном случае возникают сложностью с открытием панели в процессе разработки.
Обычно нет необходимости в прямом переопределении стилей win-appbar или win-commandlayout. Вместо этого следует создавать селекторы для пользовательских классов, связанных с этими стилями и затем стилизовать то, что вам нужно. Здесь можно использовать и псевдоселекторы, наподобие button:hover, button:active и так далее.
В Сценарии 2 примера, посвященного элементу управления HTML AppBar, показано много подобных селекторов в действии, в данном случае – для установки синего фона панели приложения и команд и зеленого цвета переднего плана (несколько отвратительное сочетание, однако, показывающее все, что требуется от примера).
В качестве основы, Сценарии 2 (html/custom-color.html) добавляет CSS-класс customColor к панели приложения:
<div id="customColorAppBar" data-win-control="WinJS.UI.AppBar" class="customColor" ...>
В css/custim-color.css он затем стилизует селекторы на основе .win-appbar.customColor. Следующие правила, например, задают фоновый цвет, цвет текста подписи и цвет круглой рамки вокруг команды для состояний :hover и :active:
.win-appbar.customColor {
background-color: rgb(20, 20, 90);
}
.win-appbar.customColor .win-label {
color: rgb(90, 200, 90);
}
.win-appbar.customColor button:hover .win-commandring,
.win-appbar.customColor button:active .win-commandring {
background-color: rgba(90, 200, 90, 0.13);
border-color: rgb(90, 200, 90);
}
Все эти настройки стилей, кстати, применимы только к стандартному макету, основанному на командах. Если вы используете пользовательский макет, панель приложения просто содержит в себе все элементы, которые вы хотите на нее добавить, с любыми классами стилей, таким образом, вы можете управлять стилизацией как при работе с любыми другими HTML-элементами
Ранее мы видели, что свойство icon элемента типа AppBarCommand обычно является одним из символов шрифта Segoe UI Symbol. Хотя для большинства нужд это подходит, время от времени может возникнуть необходимость в использовании символов из разных шрифтов (некоторые из нас просто не могут не пользоваться Wingdings!) или в отображении пользовательских изображений. Панель приложения поддерживает и то и другое.
Для использования различных шрифтов для всей панели приложения, просто добавьте класс к панели приложения и создайте правило, основанное на win-appbar :
win-appbar.customFont {
font-family: "Wingdings";
}
Для изменения шрифта определенной командной кнопки, добавьте класс к ее свойству extraClass (такому, как customButtonFont ) и создать правило со следующим селектором (как в Сценарии 1 модифицированного примера):
button.customButtonFont .win-commandimage {
font-family: "Wingdings";
}
Для использования собственных изображений, выполните следующие шаги для масштаба разрешения 100%:
Для других масштабов разрешений, умножьте размеры на 1.4 (140%) и 1.8 (180%) и используйте в именах файлов суффиксы .scale-140 и .scale-180.
Для использования пользовательского значка, установите свойство icon командного элемента в значение базового URI изображения (без суффиксов .scale-1x0). Например, icon: 'url(images/icon.png)' .
Сценарий 3 примера, посвященного элементу управления HTML AppBar показывает использование пользовательских значков для кнопки Accept:
Значок хранится в файле, который называется accept.png, который выглядит примерно так, как показано ниже. Я настроил яркость и контраст, добавил рамку, чтобы вы могли четко увидеть каждую ячейку:
Объявление кнопок панели приложения, таким образом, выглядит так, как показано ниже (некоторые свойства опущены для краткости):
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmdAccept', label:'Accept', icon:'url(images/accept.png)' }">
Обратите внимание на то, что хотя в примере нет различных значков для разных масштабов разрешения, он содержит варианты для тем с высокой контрастностью, это важное условие обеспечения соглашений по специальным возможностям, о которых мы поговорим в лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой". По этой причине элемент button включает style="-ms-high-contrast-adjust:none" для того, чтобы переопределить автоматическую настройку для тем с высокой контрастностью.
Следующий аспект панели приложения, который нуждается в пояснениях, касается тех команд, свойство type который установлено в значение flyout. В данном случае свойство flyout командного элемента должно идентифицировать объект WinJS.UI.Flyout или на элемент управления WinJS.UI.Menu (он является всплывающим элементом). Как мы упоминали ранее, всплывающие элементы или всплывающие меню наподобие этих используются тогда, когда в панели приложения нужно расположить слишком много связанных команд, или когда вам нужны другие типы элементов управления, которые не вполне подходят для их непосредственного расположения в панели приложения. Если вы пытаетесь воспользоваться кнопками с подписями "Еще", "Дополнительно", "Другие возможности", так как вы не можете решить, как организовать команды, это верный признак того, что приложение получается слишком сложным. Изыщите способы упрощения целей приложения, в итоге панель приложения не превратится в хранилище случайных элементов управления.
Мы подробнее поговорим о всплывающих элементах ниже в этой лекции, а сейчас посмотрим, как использовать их в панели приложения, как показано в Сценарии 6 примера "Всплывающие элементы управления HTML" (http://code.msdn.microsoft.com/windowsapps/Flyout-sample-258757b3) (обратите внимание, это не пример про панель приложения!):
В файле html/appbar-flyout.html этого примера мы видим, что кнопка панели приложения задана следующим образом:
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'respondButton', label:'Respond', icon:'edit', type:'flyout', flyout:'respondFlyout'}"
>
Элемент respondFlyout, который здесь идентифицируется, определен ранее, в html/appbar-flyout.html. Обратите внимание на то, что подобный элемент должен быть объявлен раньше, чем панель приложения для того, чтобы его экземпляр был создан до создания панели приложения:
<div id="respondFlyout" data-win-control="WinJS.UI.Menu"
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'alwaysSaveMenuItem',
label:'Always save drafts', type:'toggle', selected:'true'}">
</button>
<hr data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'separator', type:'separator'}" />
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'replyMenuItem', label:'Reply'}">
</button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'replyAllMenuItem', label:'Reply All'}">
</button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'forwardMenuItem', label:'Forward'}">
</button>
</div>
Вас не должно уже удивить то, что меню – это лишь еще один элемент управления WinJS, WinJS.UI.Menu, дочерние элементы которого определяютс содержимое меню. Все эти элементы, опять же, лишь элементы в DOM. Их события click подключаются к обработчикам в js/appbar-flyout.js с уже известным вам addEventListener. (В примере использована команда document.getElementById для получения элементов при вызове addEventListener. Эффективнее будет использовать метод панели приложения getCommandById, как в модифицированном примере работы с панелью приложения).
Каждый элемент меню, как вы можете видеть, это объект WinJS.UI.MenuCommand, мы так же рассмотрим подробности позднее, а сейчас вы можете видеть, что эти элементы имеют свойства id, label, type – это очень похоже на объекты WinJS.UI.AppBarCommand.
Вот и почти все, что нужно сделать, добавлю лишь, что когда элемент меню выбирают, нужно скрыть меню и, возможно, так же панель приложения (если она не находится в режиме залипания). Это показано в примере, в файле hs/appbar-flyout.js, в функции, которая называется hideFlyoutAndAppBar:
function hideFlyoutAndAppBar() {
document.getElementById("respondFlyout").winControl.hide();
document.getElementById("appBar").winControl.hide();
Все это время мы изучали стандартный макет команд панели приложения, что, конечно, является самым простым способом использования этого элемента управления. Однако, стандартных возможностей хватает не всегда. Возможно, вы захотите расположить на панели приложения больше интересных элементов управления, в особенности это касается пользовательских элементов управления (таких, как средство для выбора цвета). Для подобных целей вы можете установить свойство панели приложения layout в значение ‘custom’ и задать любой HTML-макет для панели приложения, стилизовать его с помощью CSS и подключить обработчики любых необходимых событий в JavaScript.
Пользовательские макеты обычно используют для создании верхней панели навигации – то есть, панели приложения со свойством placement установленным в значение ‘top’ – так как командные кнопки – это обычно не тот пользовательский интерфейс, который вам нужен. Мы видели пример подобной панели приложения ранее, в приложении Weather, и навигационная панель в Internet Explorer устроена по тому же принципу:
Наш старый друг – пример элемента управления HTML AppBar (http://code.msdn.microsoft.com/windowsapps/App-bar-sample-a57eeae9) предоставляет пример пользовательского макета в Сценарии 5. В html/custom-layout.html мы видим разметку для пользовательской верхней панели приложения, содержащей произвольные элементы:
<div id="customLayoutAppBar" data-win-control="WinJS.UI.AppBar" aria-label="Navigation Bar"
data-win-options="{layout:'custom', placement:'top'}">
<header aria-label="Navigation bar" role="banner">
<button id="cmdBack" class="win-backbutton" aria-label="Back"
</button>
<div class="titleArea">
<h1 class="win-type-xx-large" tabindex="0">
Page Title </h1>
</div>
</header>
</div>
Правда, результат этого примера странноват – он создает навигационную панель с обычным заголовком страницы и кнопкой "Назад", где каждый элемент управления может иметь прямоугольник выделения. Я не советую следовать подобным образцам дизайна!
Как я упоминал в подразделе "Некоторые советы и секреты" (в разделе "Стилизация элементов управления), в лекции 4 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", вы можете подавить вывод прямоугольника фокуса с помощью правила <selector>:focus { outline: none; } в CSS. Для того, чтобы убрать его с кнопки "Назад", например, вы можете добавить следующее правило стиля в css/custom-layout.css
.win-appbar header .win-backbutton {
margin-left: 39px;
margin-top: 59px;
outline: none;
}
Снова обратите внимание на то, как это правило и другие правила в css/custom-layout.ss используют класс win-appbar как базовый селектор, но только потому, что он используется для стилизации других обычных классов, таких, как header или win-backbutton. Если вы используете особые классы в панели приложения или в навигационной панели, вы можете совсем не пользоваться селектором win-appbar.
Для того, чтобы реализовать навигационную панель, напоминающую таковую в Internet Explorer или в приложении Weather, вы можете использовать элемент управления ListView вместе с шаблонами элементов или пользовательскими функциями вывода элементов, где вы подключаете события itemInvoked к WinJS.Navigation.navigate и так далее. Опять же, здесь нет ничего особенно сложного: панель приложения с пользовательским макетом – это лишь всплывающий контейнер для других HTML-элементов.
Возвращаясь к дискуссии о том, где размещать командные элементы управления, напомним, что всплывающий элемент управления WinJS.UI.Flyout – используется для целей подтверждения действий, сбора информации и получения ответов на вопросы при реакции на действия пользователя. Меню – элемент управления WinJS.UI.Menu – это, в свою очередь, особый вид всплывающего элемента, который содержит элементы управления WinJS.UI.MenuCommand вместо произвольного HTML-кода. На самом деле, WinJS.UI.Menu является, благодаря использованию WinJS.Class.define, прямым наследником WinJS.UI.Flyout , поэтому у них много общего. Что касается всплывающих элементов, они так же имеют общие черты с панелью приложения. (Классы панели приложения и всплывающего элемента являются наследниками базового класса WinJS.UI._Overlay , который является внутренним классом WinJS).
Совет. В дополнение к объекту ).
Прежде чем мы рассмотрим подробности, взглянем на некоторые изображения из примера "Всплывающий элемент управления HTML" (http://code.msdn.microsoft.com/windowsapps/Flyout-sample-258757b3), где мы уже видели меню, которое всплывает по команде элемента управления панели приложения. Элементы управления WinJS.UI.Flyout, использованные в сценариях 1-4 показаны на рис. 1.1. Обратите внимание на использование разного содержимого во всплывающих элементах и на то, что всплывающий элемент всегда расположен около элемента управления, который вызвал его, например, у кнопок Buy, Login, Format output text и около текста гиперссылки Lorem ipsum. Эти примеры показывают, что всплывающий элемент может содержать простое сообщение с кнопкой (Сцераний 1, для предупреждений и подтверждений), может содержать поля для ввода информации или изменения параметров (Сценарии 2 и 3), и может иметь заголовок (Сценарий 4). Сценарий 5, в свою очередь, содержит пример всплывающего меню заголовка WinJS.UI.Menu, которое мы увидим немного позже.
Существуют две ключевые характеристики всплывающего элемента управления, включая меню. Первая заключается в том, что всплывающий элемент может быть скрыт программно, как панель приложения, когда активирован соответствующий элемент управления внутри него. Подобное поведение характерно для кнопки Complete order в Сценарии 1, для кнопки Login в Сценарии 2.
Вторая характеристика, так же актуальная для панели приложения, это закрытие неактивных всплывающих окон (light dismiss): щелчок мышью или прикосновение к области вне элемента управления скрывает его, как при нажатии клавиши Escape, что означает, что такое закрытие аналогично нажатию на кнопку Cancel (Отмена) или на кнопку закрытия в традиционном диалоговом окне. Преимущество такого подхода заключается в том, что нам не нужны видимые кнопки для этой цели, что помогает упрощению пользовательского интерфейса. В то же время, обратите внимание на то, что в Сценарии 3 рис. 1.1 нет кнопки OK или другого элемента управления, подтверждающего изменения, которые вы могли внести во всплывающем элементе. При рассматриваемой модели взаимодействия, изменения немедленно вступают в силу, таким образом ,закрытие всплывающего элемента не возвращает их к исходному состоянию и не отменяет. Если вас такое поведение не устраивает, вы можете разместить что-то вроде кнопки "Применить изменения" во всплывающем элементе и не вносить изменения до тех пор, пока кнопка не будет нажата. В таком случае, закрытие всплывающего элемента отменит изменения.
(рис 1.1) Примеры всплывающих элементов управления из примера, который посвящен всплывающим элементам HTML
Я снова предлагаю вам почитать "Руководство и контрольный список для всплывающих элементов" (http://msdn.microsoft.com/library/windows/apps/hh465341.aspx), который в подробностях рассматривает разные варианты дизайна и размещения, которые возможны для этого элемента управления. В нем так же говорится о том, где не следует использовать этот элемент управления: например, для вывода сведений об ошибках, не связанных с действиями пользователя (вместо этого используйте окна сообщений), для основных команд (используйте панель приложения), для контекстных меню, связанных с выделением текста, и для элементов пользовательского интерфейса, которые являются частью рабочего процесса и которые следует размещать на полотне приложения. Описываемые принципы так же предполагают использование всплывающих элементов небольших размеров, целенаправленное их использование (без лишних элементов управления), и их расположение около объекта, который их вызывает. Посмотрим теперь, как все это работает в коде.
Примечание. В дополнение к приложениям, которые могут отображать собственные элементы WinJS.UI.Flyout, некоторые системные API (например, при создании дополнительных плиток) создают системные всплывающие элементы. В подобных случаях приложение получает событие blur, что ведет к закрытию панелей приложения. Для предотвращения подобного поведения, переведите панель приложения в режим залипания (sticky) при вызове этих API.
Большинство свойств, методов и событий элемента управления WinJS.UI.Flyout – это те, что мы уже рассматривали для панели приложения. Методы show и hide управляют его видимостью, свойство hidden показывает состояние видимости, в соответствующие моменты вызываются события beforeshow, aftershow, beforehide , и afterhide . Событие afterhide обычно используют для определения момента закрытия элемента.
Как и панель приложения, всплывающий элемент имеет свойство placement, но оно имеет другие значения, которые имеют смысл лишь в контексте свойств всплывающего элемента alignment и anchor . На самом деле, все три свойства – это необязательные параметры для метода show, так как они определяют позицию, в которой всплывающий элемент отобразится на экране. Значения placement и alignment могут быть установлены на сам элемент управления, так как они не обязательны при вызове show . (Заметим, что если вы не установите anchor в методе show , это свойство уже должно быть установлено на элемент управления, иначе show выдаст исключение.)
Свойство anchor идентифицирует элемент управления, который активирует всплывающий элемент, или любую другую операцию, которая может открыть всплывающий элемент (как в случае с подтверждением некоторого действия). Свойство placement, указывает, как всплывающий элемент должен располагаться по отношению к положению, заданному свойством anchor: 'top', 'bottom', 'left', 'right' , или 'auto' (по умолчанию). Обычно placement задают только тогда, когда всплывающий элемент может перекрыть важное содержимое. В противном случае вы рискуете тем, что всплывающий элемент будет уменьшен для того, чтобы занять доступное ему место. Содержимое всплывающего элемента при этом останется того же размера, что означает, - вот незадача! – что появятся полосы прокрутки! Таким образом, если только у вас нет достойной причины и справки от врача, используйте тип размещения 'auto' , так всплывающий элемент будет размещен там, где он может быть показан в полном размере. В том же духе, помните, что в прикрепленном режиме просмотра у вас есть лишь 320 пикселей по горизонтали, что означает, что всплывающий элемент, который вы показываете в данном режиме просмотра следует делать такого или меньшего размера.
Свойство alignment, в свою очередь, когда используется со свойством placement , установленным в 'top' или 'bottom' , определяет, как всплывающий элемент выравнивается с anchor: 'left', 'right' , или 'center' (по умолчанию). Само содержимое всплывающего элемента выравнивается посредством CSS как и любые HTML-элементы.
Если вам нужно стилизовать сам всплывающий элемент управления, вы можете задать стили в классе win-flyout , наподобшие шрифтов, выравнивания по умолчанию, полей и так далее. Что касается других классов стилей WinJS, родственных этому, используйте win-flyout как базу для более конкретных селекторов, если только вы не хотите стилизовать каждый всплывающий элемент в приложении. Обычно, на самом деле, вы так же исключаете win-menu из правила, таким образом, на всплывающие элементы меню такая стилизация не действует. Например, большинство сценариев в примере, который посвящен всплывающим элементам управления HTML, которые мы рассмотрим в дальнейшем, имеют правила наподобие такого:
.win-flyout:not(.win-menu) button,
.win-flyout:not(.win-menu) input[type="button"] {
margin-top: 16px;
margin-left: 20px;
float: right;
И, наконец, если по какой-либо причине вам нужно знать, когда загружается всплывающий элемент, прослушивайте DOMNodeInserted в document.body:
document.body.addEventListener("DOMNodeInserted", insertionHandler, false);
Всплывающий элемент управления создается так же, как любой другой элемент управления WinJS, с помощью атрибутов data-win-control и data-win-options, и обрабатывается WinJS.UI.process/processAll. Всплывающие элементы с относительно постоянным содержимым элементов обычно объявляют в разметке, где вы можете использовать привязку данных для некоторых свойств элементов во всплывающем элементе. Всплывающие элементы с динамическим содержимым, с другой стороны, могут быть созданы прямо из кода с использованием команды вида new WinJS.UI.Flyout(<element>, <options>) , и вы можете, безусловно, менять его дочерние элементы в любое время. Все это – лишь часть DOM! (Не повторяю ли я собственные слова?)
Как я говорил раньше (видимо, я повторяюсь) элемент управления WinJS.UI.Flyout может содержать произвольный HTML, стилизованный, как всегда, с помощью CSS. Всплывающий элемент для Сценария 1 в примере выглядит следующим образом в html/confirm-action.html (слегка сокращенный вид):
<div id="confirmFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{Confirm purchase flyout}">
<div>Your account will be charged $252. </div>
<button id="confirmButton">Complete Order</button>
</div>
Всплывающий элемент для входа в систему из Сценария 2 похож, и он даже использует HTML-форму для прикрепления к кнопке Login события нажатия на кнопку Enter на клавиатуре:
<div id="loginFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{Login flyout}" >
<form onsubmit="return false;" >
<p >
<label for="username" gt;Username <br / ></label >
<span id="usernameError" class="error" > </span >
<input type="text" id="username" / >
</p >
<p >
<label for="password">Password<br / ></label >
<span id="passwordError" class="error" ></span >
<input type="password" id="password" / >
</p >
<button id="submitLoginButton" >Login</button >
</form >
</div >
Всплывающий элемент отображается путем вызова его метода show . В Сценарии 1, например, событие кнопки click прикреплено к функции showConfirmFlyout (js/confirm-action.js), где кнопка Buy задается как элемент прикрепления. Обработка нажатия на кнопку Complete Order так же производится посредством обработчика события click, прикрепленного к этому элементу, и здесь мы вызовем метод hide для программного закрытия всплывающего элемента. Наконец, событие afterhide используется для выявления факта закрытия элемента:
var bought;
var page = WinJS.UI.Pages.define("/html/confirm-action.html", {
ready: function (element, options) {
document.getElementById("buyButton").addEventListener("click", showConfirmFlyout, false);
document.getElementById("confirmButton").addEventListener("click",
confirmOrder, false);
document.getElementById("confirmFlyout").addEventListener("afterhide",
onDismiss, false);
}
function showConfirmFlyout() {
bought = false;
var buyButton = document.getElementById("buyButton");
document.getElementById("confirmFlyout").winControl.show(buyButton);
// Когда кнопка Buy нажата, скрыть всплывающий элемент, так как пользователь завершил работу с ним.
function confirmOrder() {
bought = true;
document.getElementById("confirmFlyout").winControl.hide();
}
// При закрытии элемента, определяет, был ли он закрыт потому, что пользователь нажал
// Кнопку Byu. Если нет, элемент был закрыт автоматически.
function onDismiss() {
if (!bought) {
// (В примере сообщение о закрытии отображается на полотне)
}
Работа с элементами управления для входа в систему в Сценарии 2, в основном – это то же самое, с некоторыми дополнениями в виде кода, который позволяет проверить, были ли заполнены поля для ввода имени пользователя и пароля. Если это не так, обработчик нажатия кнопки Login показывает встроенное в элемент сообщение об ошибке и устанавливает фокус ввода на соответствующее поле:
Так как всплывающий элемент в Сценарии 2 немного больше, значение по умолчанию ‘auto’ для свойства placement (как в имитаторе) приводит к появлению элемента ниже кнопки, которая его вызывает. Выше этой кнопки не так много места. Поэтому попытаемся установить свойство placement в значение ‘top’ в вызове show:
function showLoginFlyout() {
// ...
document.getElementById("loginFlyout").winControl.show(loginButton, "top");
}
После этого вы можете увидеть, как во всплывающем элементе появляются полосы прокрутки, так как его размер слишком мал:
Каким словом я раньше это называл? "Незадача"?
Двигаемся дальше, к Сценарию 3, в котором всплывающий элемент определен в разметке, где он содержит элементы управления label, select, input. В JavaSctipt, однако, он прослушивает события изменения этих элеменов и применяет полученные значения к элементам вывода данных на полотне приложения:
var page = WinJS.UI.Pages.define("/html/change-settings.html", {
ready: function (element, options) {
// ...
document.getElementById("textColor").addEventListener("change", changeColor, false);
document.getElementById("textSize").addEventListener("change", changeSize, false);
}
});
// Изменение цвета текста
function changeColor() {
document.getElementById("outputText").style.color =
document.getElementById("textColor").value;
// Изменение размера текста
function changeSize() {
document.getElementById("outputText").style.fontSize =
document.getElementById("textSize").value + "pt";
Если бы у этого всплывающего элемента была бы кнопка Применить (Apply) вместо его немедленной реакции на изменения, ее обработчик click получал бы текущее выделение и значение из ползунка и использовал бы их для того, чтобы выполнить действия, аналогичные тем, что производятся в функциях changeColor и changeSize.
И, наконец, в Сценарии 4 мы видим всплывающий элемент с заголовком, который является просто элементом в разметке, выводящим крупный текст. Сам по себе всплывающий элемент не имеет выделенного описания для заголовков:
<div id="moreInfoFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{More info flyout}">
<div class="win-type-x-large">Lorem Ipsum</div>
<div>
Lorem Ipsum is text used as a placeholder by designers...
</div>
</div>
Смысл этого последнего примера заключается в том, чтобы показать, что в отличие от традиционных диалоговых окон настольных приложения, всплывающие элементы не часто нуждаются в заголовках, так как у них уже есть контекст внутри приложения. Диалоговые окна в настольных приложениях нуждаются в заголовках, так как они могут относиться к разным приложениям.
Элемент управления WinJS.UI.Menu отличает от более общего элемента WinJS.UI.Flyout то, что меню ожидает, что все его дочерние элементы будут объектами типа WinJS.UI.MenuCommand , так же, как стандартный макет панели приложения ожидает объекты AppBarCommand (и его экземпляр не будет создан, если вы объявите у него какие-нибудь другие дочерние элементы). На самом деле, у меню есть общие характеристики и с панелью приложения, и со всплывающим элементом:
show и hide .getCommandById, showCommands, hideCommands , и showOnlyCommands , вместе со свойством commands , что означает, что вы можете использовать те же подходы для управления командами меню, которые обсуждались в подразделе "Отображение, скрытие, включение и обновление команд" в разделе, посвященном панели приложения, в том числе – задание команд меню используя JSON-массив вместо описания отдельных элементов.beforeshow, aftershow, beforehide , и afterhide .anchor, alignment , и placement .Меню поддерживает два стиля, влияющих на его отображение – win-menu и win-command – который можно использовать для создания более конкретных селекторов, как мы видели, для всего меню или для отдельных текстовых команд.
Объекты MenuCommand так же очень похожи на объекты AppBarCommand. У них много одинаковых свойств: id, label, type ('button', 'toggle', 'flyout' , и 'separator' ), disabled, extraClass, flyout, hidden, onclick , и selected . У команд меню нет значков, разделов и всплывающих подсказок, но из type вы можете видеть, что элементы меню могут быть кнопками (включая полностью текстовые элементы), элементами с возможностью установки флагов, разделителями и, кроме того, другими всплывающими элементами. В последнем случае, вторичное меню заменит первое вместо того, чтобы отображаться рядом с ним, а чтобы быть полностью честным, я должен все же видеть вторичное меню, используемое в реальном приложении. Тем не менее, такая возможность поддерживается.
Мы уже видели, как использовать всплывающие меню с помощью команд панели приложения, что показано в Сценарии 6 примера, посвященного всплывающему элементу управления HTML (посмотрите раздел "Командные меню" выше). Другая типичная сфера использования – реализация того, что выглядит как выпадающее меню заголовочного элемента, что показано в Сценарии 5. Здесь (html/header-menu.html), показан стандартный дизайн помещения символа перевернутой угловой скобки (#xe099) в конце заголовка:
<header aria-label="Header content" role="banner"> <button class="win-backbutton" aria-label="Back"></button> <div class="titlearea win-type-ellipsis"> <button class="titlecontainer"> <h1> <span class="pagetitle">Music</span> <span class="chevron win-type-x-large" > #xe099 </span> </h1> </button> </div> </header>
Обратите внимание на то, что весь заголовок помещен в элемент управления button (кнопка), ее обработчик click может отображать меню с помощью метода show:
document.querySelector(".titlearea").addEventListener("click", showHeaderMenu, false);
function showHeaderMenu() {
var title = document.querySelector("header .titlearea");
var menu = document.getElementById("headerMenu").winControl;
menu.anchor = title; menu.placement = "bottom";
menu.alignment = "left";
menu.show();
}
Всплывающий элемент (определенный как headerMenu в html/header-menu.html) появляется, когда вы щелкаете в любом месте заголовка (а не только на угловой скобке, которая является обычным символом в тексте заголовка):
Отдельные команды меню – это простые элементы button, поэтому вы можете присоединить к ним обработчики click если вам это нужно. Как и в случае с панелью приложения, лучше всего использовать метод элемента управления меню getCommandById для обнаружения этих элементов, так как это гораздо эффективнее, чем document.getElementById (что, к сожалению, используется в примере из SDK).
Для того чтобы увидеть в действии вторичное меню, попытайтесь добавить следующий элемент secondaryMenu в html/header-menu.thml перед элементом headerMenu и добавить элемент button в headerMenu, свойство flyout которого ссылается на secondaryMenu:
<div id="secondaryMenu" data-win-control="WinJS.UI.Menu">
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'command1', label:'Command 1'}"></button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'command2', label:'Command 2'}"></button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'command3', label:'Command 3'}"></button>
</div>
<div id="headerMenu" data-win-control="WinJS.UI.Menu">
<!-- ... -->
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'showFlyout', label:'Show secondary menu',
type:'flyout', flyout:'secondaryMenu'}">
</button>
</div>
Кроме того, перейдите в css/header-menu.css и установите стиль width элемента #headerMeny в значение 200px. С этими изменениями первое меню будет выглядеть так, как показано ниже, изменение цвета заголовка – результат действия эффекта зависания над ним указателя мыши:
Когда вы выберете пункт Show secondary meny (Показать вторичное меню), первое меню будет закрыто и откроется меню второго уровня:
Другой пример использования всплывающих элементов в заголовке можно найти в примере "Адаптивный макет с использованием CSS" (http://code.msdn.microsoft.com/windowsapps/Adaptive-layout-with-sample-062e7fe2), который мы видели в лекции 6 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript". Он реализован так же, как показано выше, с добавлением механизмов, которые изменяют содержимое страницы в ответ на выделение.
Помимо всплывающих меню, которые мы уже видели, существуют и контекстные меню, описанные в материале "Руководство и контрольный список для контекстных меню" (http://msdn.microsoft.com/library/windows/apps/hh465308.aspx). Они используются для команд, которые напрямую связаны с выделением разного рода, таким, как команды буфера обмена для текста, они вызываются щелчком правой кнопки мыши по элементу, прикосновением или клавишей вызова контекстного меню на клавиатуре. Текстовые элементы управления и гиперссылки обеспечивают данную возможность по умолчанию. Контекстные меню так же хорошо подходят для предоставления команд для объектов, которые не могут быть выделены (таких, как части беседы в программе мгновенного обмена сообщениями), команды панели приложения не могут быть контекстно чувствительными к подобным элементам. Они так же рекомендованы для совершения действий, которые нельзя выполнить путем непосредственного взаимодействия с объектом. Однако, не используйте их для фона страницы – именно для этого существует панель приложения, она будет появляться автоматически при щелчке правой кнопкой мыши или соответствующем жесте.
Совет. Если вы обрабатываете событие нажатий правой кнопки мыши на элемент, убедитесь в том, что для этого элемента отключено стандартное поведение, которое подразумевает отображение панели приложения. Таким образом, используйте событие щелчка правой кнопки мыши разумно, так как пользователи привыкли пользоваться правой кнопкой для вызова панели приложения. Обратите так же внимание на то, что вы можете программно открыть панель приложения, используя ее метод show.
Пример "Контекстное меню" (http://code.msdn.microsoft.com/windowsapps/Context-menu-sample-40840351) предоставляет нам некоторый контекст – я знаю, что это неудачная игра слов! В любом случае, вам нужно лишь прослушивать событие HTML contextmenu для соответствующего элемента. Вам не нужно заботиться особым образом об обработке касаний или о событиях клавиатуры. Сценарий 1 примера, например, имеет невыделяемый элемент attachment, для которого прослушивается вышеупомянутое событие (html/scenario1.html):
document.getElementById("attachment").addEventListener("contextmenu", attachmentHandler, false);
В обработчике события, вы создаете объект ) (это объект из WinRT, не из WinJS!), заполняет его объектами (они содержат подпись элемента и обработчик события) или объектами ), и затем вызывает метод меню showAsync (js/scenario1.js):
function attachmentHandler(e) {
var menu = new Windows.UI.Popups.PopupMenu();
menu.commands.append(new Windows.UI.Popups.UICommand("Open with", onOpenWith));
menu.commands.append(new Windows.UI.Popups.UICommand("Save attachment",
onSaveAttachment));
menu.showAsync({ x: e.clientX, y: e.clientY }).done(function (invokedCommand) {
if (invokedCommand === null) {
// Команда - это null если ни одна команда не была активирована.
}
});
Обратите внимание на то, что результат
Объект PopupMenu так же поддерживает метод, который называется showForSelectionAsync, первым аргументом которого является объект ), который описывает область выделения. Опять же, меню размещается выше прямоугольника выделения с выравниванием по центру. Это показано в Сценарии 2 примера в js/scenario2.js:
//В обработчике contextmenu
menu.showForSelectionAsync(getSelectionRect()).then(function (invokedCommand) {
// ...
function getSelectionRect() {
var selectionRect = document.selection.createRange().getBoundingClientRect();
var rect = {
x: getClientCoordinates(selectionRect.left),
y: getClientCoordinates(selectionRect.top),
width: getClientCoordinates(selectionRect.width),
height: getClientCoordinates(selectionRect.height)
};
return rect;
};
Сценарий так же показывает, что вы можете использовать обработчик события contextmenu с текстом для переопределения команд по умолчанию, которые предоставляют соответствующие элементы управления.
Последнее замечание, касающееся контекстных меню: так как они создаются с помощью API WinRT, а не с помощью элементов управления WinJS, меню не существуют в DOM, они не известны DOM, что объясняет использование других конструкций WinRT – как point (Точка) и Rect (Прямоугольник). Подобное утверждение так же справедливо для окон сообщений, которые станут последней темой этой лекции.
Последняя тема этой лекции, касающаяся командного интерфейса, посвящена диалоговым окнам сообщений. Как и контекстные меню, эти всплывающие элементы относятся к WinRT а не к WinJS, для работы с ними используется API Windows.UI.MessageDialog (http://msdn.microsoft.com/library/windows/apps/windows.ui.popups.messagedialog.aspx). Это, опять же, означает, что окна сообщений просто появляются поверх текущей страницы и не присутствуют в DOM. Диалоговые окна автоматически затеняют текущую страницу приложения и блокируют события ввода от приложения до тех пор, пока пользователь не отреагирует на окно сообщения.
В материале "Руководство и контрольный список для окон сообщений" (http://msdn.microsoft.com/library/windows/apps/hh738363.aspx) показаны варианты их использования:
Интерфейс диалоговых окон очень прост. Вы создаете объект диалогового окна сообщения командой new Windows.UI.Popups.MessageDialog. Конструктор принимает в качесве обязательного параметра строку с содержимым сообщения и с необязательным вторым строковым параметром, содержащим заголовок окна. Окно сообщения имеет свойства content и title, которые вы можете использовать независимо друг от друга. В любом случае, строковые параметры поддерживают лишь обычный текст.
После этого вы конфигурируете диалоговое окно с помощью его свойств commands, options, defaultCommandIndex (команда, связанная с клавишей Enter) и cancelCommandIndex (команда, связанная с клавишей Escape).
Значения параметра options берутся из перечисления Windows.UI.Popups.MessageDialogOptions, которое содержит лишь два члена: none (значение по умолчанию, для случаев, когда особое поведение не требуется), и acceptUserInputAfterDelay (что приводит к тому, что окно сообщения некоторое время не воспринимает пользовательский ввод для того, чтобы предотвратить кликджекинг (clickjaking); это существенно, преимущественно, для интернет-браузеров, которые загружают произвольное веб-содержимое и практически не требуется большинству приложений).
Свойство command содержит до трех объектов типа тех же самых, которые используются в контекстных меню. Каждая из команд содержит свойства id, label и invoked, к которому вы подключаете обработчик для команды. Обратите внимание на то, работа свойств defaultCommandIndex и cancelCommandIndex основана на индексах массива commands, а не на id этих команд. Кроме того, если вы не добавите собственные команды, диалоговое окно сообщения отобразится с единственной командой по умолчанию – Закрыть (Close).
И, наконец, когда диалоговое окно настроено, вы отображаете его, вызывая его метод showAsync. Как и в случае с контекстным меню, в результате выбранный пользователем объект UICommand, который передается обработчику завершения, который вы предоставили для метода done promise-объекта. Обычно вам не нужно получать этот результат, так как выбранная команда имеет связанный с ней обработчик invoked, где вы и выполняете действия, соответствующие команде.
Примечание. Если в то время, когда активно окно сообщения, активируются чудо-кнопки Поиск, Общий доступ, Устройства или Параметры, или если приложение активируется для обслуживания контракта, диалоговое окно сообщения будет закрыто и без выбора какой-либо команды в нем. Будет осуществлен вызов обработчика завершения для showAsync, с результатом, установленным на команду по умолчанию. Знайте об этом, если вы используете обработчик завершения для обработки подобных команд.
Пример "Диалоговое окно сообщения" (http://code.msdn.microsoft.com/windowsapps/Message-dialog-sample-00c928f5) – это один из самых простых примеров во всем SDK! Он показывает разные варианты использования API окон сообщений. Сценарий 1 показывает окно сообщения с заголовком и двумя командными кнопками, вторая командная кнопка (с индексом 1) установлена как кнопка по умолчанию. Вот как это выглядит:
Сценарий 2 показывает вывод команды по умолчанию – Закрыть (Close) с сообщением и без заголовка:
Сценарий 3 похож на Сценарий 1, но использует обработчик завершения метода showAsync().done для обработки команды, выбранной пользователем. Вы можете использовать этот пример для того, чтобы увидеть эффект активации чудо-кнопок при открытом диалоговом окне.
И, наконец, Сценарий 4 делает командой по умолчанию первую команду и отмечает вторую команду в качестве команды отмены, то есть, той команды, которая закрывает окно либо при ее выборе, либо при нажатии на клавишу клавиатуры Escape.
И это действительно все, что относится к окнам сообщений!
Для того, чтобы завершить эту лекцию и свести воедино большую часть того, о чем мы говорили, внесем некоторые изменения в "Here My Am!". В последний раз мы работали над этой программой, улучшая обработку различных ошибок. В ее теперешнем состоянии программа ведет себя не очень хорошо. А именно, есть сложности в следующих областях:
Приложение "Here My Am!" (7) для этой лекции учитывает эти вопросы. Во-первых, я добавил изображение, которое должно выводиться при ошибке, в html/map.html (изображение находится по адресу html/maperror.png), таким образом, если не удается загрузить скрипт карт Bing, вместо карты будет выведено изображение (в этом случае стиль отображения none будет удален):
<img id="errorImage" style="display: none; width: 100%; height: 100%;" src="maperror.png" />
Кроме того, я добавил к изображению обработчик события click, который перезагружает содержимое iframe с document.location.reload(true). Когда это сделано, я могу убрать исключения, которые возникли ранее, когда карта не могла быть создана, предотвращая аварийное завершение приложения. Вот как выглядит ситуация, когда карта не может быть создана:
Для того, чтобы протестировать такой вариант развития событий, вам нужно отключиться от Интернет, деинсталлировать приложение (для очистки кэшированных скриптов карты; в противном случае карта, все равно, будет загружаться!) и снова запустить приложение. Ошибка должна произойти в начале метода init в файле html/map.html, что приведет к появлению изображения путем удаления стиля по умолчанию display: none и подключения обработчика click . Затем снова подключитесь к Интернету и нажмите на изображение. Карта должна перезагрузиться, но если проблема с соединением сохранилась, снова появится изображение, сигнализирующее об ошибке.
Вторая проблема – добавление возможности обновлять наше местоположение – просто решаема с помощью панели приложения. Я добавил такой элемент управления на страницу default.html с одной командой:
<div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="">
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmdRefreshLocation', label:'Refresh location',
icon:'globe', section:'global', tooltip:'Refresh your location'}">
</button>
</div>
Эта команда подключена внутри pages/home/home.js в методе ready элемента управления страницы:
var appbar = document.getElementById("appbar").winControl;
appbar.getCommandById("cmdRefreshLocation").addEventListener("click", this.tryRefresh.bind(this));
Здесь обработчик tryRefresh, так же в элементе управления страницы, скрывает панель приложения и вызывает другой новый метод, refreshPosition, куда я поместил код, который получает сведения о местоположении и обновляет карту:
tryRefresh: function () {
//Скрыть панель приложения и попробовать снова
var appbar = document.getElementById("appbar").winControl.hide();
this.refreshPosition();
},
Мне так же понадобилось настроить функцию pinLocation в html/map.html. Без команды обновления местоположения, эта функция вызывалась лишь однажды, при запуске приложения. Так как теперь она может быть вызвана много раз, нам нужно удалить любые существующие метки на карте, прежде чем добавлять метку для нового местоположения. Это делается путем вызова map.entities.pop перед уже существующим вызовом map.entities.push, который отмечает новое местоположение.
Панель приложения теперь выглядит так, как показано ниже, и мы можем, при необходимости, обновить местоположение. (Если вы запускаете программу не на мобильном устройстве, попытайтесь перетащить первую метку на новое место и затем запустить обновление для того, чтобы увидеть, как метка вернулась на прежнее место).
Что касается третьей проблемы – дать пользователю знать о том, что приложение пытается получить данные о местоположении – мы можем показать маленькое всплывающее сообщение сразу же после попытки вызова getGeopositionAsync из WinRT. Всплывающий элемент, определенный в pages/home/home.html (в нашем элементе управления страницы) центрирован вдоль нижней части области карты:
<div id="retryFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{Trying geolocation}"
data-win-options="{anchor: 'map', placement: 'bottom', alignment: 'center'}">
<div class="win-type-large">Attempting to obtain geolocation...</div>
</div>
Функция refreshFunction которую мы только что добавили в pages/home/home.js является отличным местом для отображения всплывающего окна сразу перед вызовом getGeopositionAsync:
refreshPosition: function () {
document.getElementById("retryFlyout").winControl.show();
var gl = new Windows.Devices.Geolocation.Geolocator();
gl.getGeopositionAsync().done(function (position) {
//...
//Всегда скрывать всплывающий элемент document.getElementById("retryFlyout").winControl.hide();
//...
}, function (error) {
//...
//Всегда скрывать всплывающий элемент
document.getElementById("retryFlyout").winControl.hide();
});
},
Обратите внимание на то, что мы скрываем всплывающий элемент внутри обработчиков ошибки и завершения, поэтому сообщение остается видимым во время проведения асинхронной операции. Если мы разместим одну команду, скрывающую всплывающий элемент, за пределами этих обработчиков, сообщение будет отображаться лишь очень короткое время перед закрытием, а это – не то, что нам нужно. Когда мы это написали, у пользователя будет достаточно времени для того, чтобы увидеть сообщение вдоль нижней части карты (если только он не решит закрыть его):
Последняя задача заключается в том, чтобы сообщить пользователю о том, что при попытке получить сведения о местоположении произошла ошибка. Мы можем сделать это с помощью еще одного всплывающего элемента с кнопкой Retry (Повторить), или во встроенном сообщении, как показано ниже. Нам не следует пользоваться в этом случае диалоговым окном сообщения, так как сообщение появляется в ответ на действие пользователя, которым он запускает процесс обновления данных. Окно сообщения допустимо использовать при запуске программы, но с помощью встроенного сообщения, скомбинированного со всплывающим элементом мы уже выполнили все, что нужно.
Для встроенного сообщения, я добавил плавающий элемент div, который позиционирован примерно на трети расстояния от верхнего края карты. Он определен в defined/home/home.html, как показано, в качестве элемента одного уровня с iframe:
<div id="locationSection" class="subsection" aria-label="Location section"> <h2 class="group-title" role="heading">Location</h2> <iframe id="map" class="graphic" src="ms-appx-web:///html/map.html" aria-label="Map"></iframe> <div id="floatingError" class="win-type-x-large">Unable to obtain geolocation;<br /> use the app bar to try again.</div> </div>
Стиль для правила #floatingError в pages/home/home.css задает внешний вид и расположение элемента:
#floatingError {
display: none; float: left;
-ms-grid-column: 1;
-ms-grid-row: 2;
-ms-grid-row-align: start;
width: 100%;
text-align: center;
background-color: rgba(128, 0, 0, 0.5);
margin-top: 20%;
}
Так как этот элемент размещен в той же ячейке сетки, что и карта со стилем float, это выглядит как хорошо выглядящее наложение:
Это сообщение появится, если пользователь отклонил запрос на доступ приложения к информации о местоположении, который ему задавали при запуске программы, или позволил доступ, но позже, используя чудо-кнопку Параметры, запретил пользоваться этой возможностью. Вы можете использовать эти варианты для тестирования появления сообщения. Возможно так же, если вы запускаете приложение в первый раз и при этом нет соединения с Интернетом, чтобы это сообщение выводилось поверх изображения, сообщающего об ошибке загрузки карты. Именно поэтому я расположил сообщение об ошибке сервиса получения местоположения ближе к верхней части, в итоге, оно не перекрывает сообщение об ошибке загрузке карты. Но, если вы успешно запустили приложение и затем связь оборвалась, карта должна создаваться, так как скрипты карт Bing кэшируются.
С параметром display: none в CSS, сообщение об ошибке первоначально скрыто, как это и должно быть. Если мы попадаем в обработчик ошибки для getGeolocationAsync , мы устанавливаем style.display в значение block , что отображает элемент:
document.getElementById("floatingError").style.display = "block";
Мы снова скрываем сообщение внутри функции tryRefresh, которая вызывается посредством команды на панели приложения, таким образом, сообщение остается скрытым, если ошибка повторяется:
tryRefresh: function () {
document.getElementById("floatingError").style.display = "none";
//...
},
Так же мы можем добавить окно сообщения, если мы обнаружим, что сетевое соединение потеряно, и, таким образом, не можем обновить местоположение. Как мы увидим в лекции 3 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", это можно сделать с помощью события ). В подобном случае вполне можно воспользоваться окном сообщения, так как происходящее не связано с непосредственными действиями пользователя.
Кроме того, стоит отметить, что если бы мы использовали элемент управления из Bing Maps SDK в данном приложении, скрипт, который мы загружаем из удаленного источника, хранился бы в пакете нашего приложения, таким образом, устраняя причину первой ошибки. Мы внесем это изменение в следующую версию программы
Windows.UI.Popups.PopupMenu ) так же можно использовать для команд, связанных с содержимым. WinJS.UI.Flyout используется для подтверждений и других вопросов, которые отображаются в результате действий пользователя. Они так же могут просто выводить сообщения, собирать дополнительную информацию или предоставлять элементы управления для изменения настроек некоторых частей страниц. Неактивные всплывающие элементы могут быть скрыты, то есть, щелчок или прикосновение за пределами элемента или нажатие на клавишу Escape приводит к их скрытию, что равносильно нажатию на кнопку Отмена (Cancel) в ответ на вопрос, который может быть задан в подобном элементе.Windows.UI.Popups.MessageDialog ) используются для задавания вопросов, на которые пользователь должен ответить, или вывода информации, с которой он обязательно должен ознакомиться, прежде чем приложение сможет продолжить работу. Окно сообщения блокирует приложение. Окна сообщений лучше всего использовать для вывода сведений об ошибках или условиях, которые воздействуют на приложение в целом. Сообщения об ошибках, которые относятся к содержимому странице, следует показывать на странице.showCommand , hideCommand и showOnlyCommand , вместе со свойством команд extraClass , упрощают описание панели приложения в одном месте приложения и выборочный показ конкретных наборов команд с использованием querySelectorAll с классом, который представляет данный набор.WinJS.UI.Menu .Файлы к данной лекции Вы можете скачать здесь.
Одной из первых реакций у новых пользователей, не знакомых с Windows 8 и с приложениями для Магазина Windows, может быть следующая: "Где же меню? А лента? Как я могу указать приложению, чтобы оно сделало что-то с элементами, которые я выбрал из списка?". Это – вполне естественная реакция, которая возникает до тех пор, пока пользователи не привыкнут к расположению командных элементов управления, что дает еще одно значение, хотя и мирское, изречению: "Блаженны не видевшие и уверовавшие".
Следуя принципу дизайна "прежде всего содержимое, и лишь затем внешнее оформление", элементы пользовательского интерфейса, которые существуют исключительно для вызова некоторых действий, и, таким образом, не содержат значимого содержимого, попадают в категорию "внешнего оформления". Как таковые, они обычно скрыты до тех пор, пока не понадобятся, как команды системного уровня наподобие панели чудо-кнопок. Пользователь показывает желание воспользоваться подобными командами посредством соответствующего жеста. Это – скольжение от верхнего или нижнего краев экрана, щелчок правой кнопки мыши, или комбинация клавиш Win+Z, которые вызывают командные панели приложения, расположенные сверху или снизу. Скольжение от левого края экрана, щелчок мышью по верхнему левому углу экрана, или сочетание клавиш Win+Tab позволяет переключаться между приложениями. И скольжение от правого края экрана, щелчок мышью по верхнему правому или нижнему правому углам экрана, или сочетание клавиш Win+C открывает панель чудо-кнопок. (Сочетания клавиш Win+Q, Win+H, Win+i активируют, соответственно, чудо кнопки Поиск, Общий доступ и Параметры напрямую). Приложение реагирует на различные чудо-кнопки посредством реализации отдельных контрактов, как мы увидим в следующих лекциях.
Команды приложения, в свою очередь, обычно реализуются посредством панели приложения – элемента управления WinJS.UI.AppBar. Во многом, панель приложения – это эквивалент меню и ленты в приложениях для Магазина Windows, так как вы можете создать в них любые виды пользовательского интерфейса и даже отобразить элементы меню. Меню, которые поддерживает элемент управления WinJS.UI.Menu, могут, так же, всплывать из заданного места на основном экране приложения, как, например, меню прикрепленное к заголовку.
Панель приложения и меню – это конкретные экземпляры более общего элемента управления WinJS.UI.Flyout, который используется напрямую для вывода сообщений, или для действий, которые пользователь может отменить или проигнорировать. Подобные всплывающие элементы можно закрыть щелчком мыши или прикосновением за их пределами. (Это – что-то вроде нажатия на кнопку "Отмена"). Для показа важных сообщений, которые требуют какого-либо действий, то есть – тех, где пользователь должен выбрать из некоторого набора параметров, приложения могут использовать WinJS.UI.MessageDialog. Диалоговые окна – это всем известная концепция из мира настольных приложений, она уже долгое время используется для сбора самой разной информации и настройки параметров приложения. В дизайне приложений для Магазина Windows, однако, диалоговые окна используются лишь для задавания вопросов и получения простых ответов, или для того, чтобы сообщить пользователю некие важные сведения. Настройка параметров приложений реализуется посредством чудо-кнопки Параметры, как мы увидим в лекции 2.
В том, что касается всех этих команд, важно то, что они не включаются в макет страницы: они, вместо этого "всплывают" и располагаются над текущей страницей. Это означает, что мы, к счастью, можем не беспокоиться об их воздействии на макет… с одним маленьким исключением, которое я пока буду держать в секрете.
Для начала, тем не менее, вернемся назад и подумаем о командах приложения в целом, и о том, где лучше всего эти команды размещать.
Размещение команд имеет важное значение в дизайне приложений. В отличие от руководства, или, скорее, их отсутствия – для командных элементов управления настольных приложений, что приводило к беспорядку, центр разработчиков Windows предлагает два довольно обширных материала на эту тему: "Проектирование командного интерфейса для приложений Магазина Windows" (http://msdn.microsoft.com/library/windows/apps/hh761499). Эти материалы обязательны для прочтения для дизайнеров, работающих над приложением, так как они описывают различные виды командного интерфейса и рассказывают о том, как получить одобрение у профессионалов в области дизайна для Windows 8. Это так же отличные материалы для разработчиков, так как они могут дать вам некоторое понимание того, чего вы можете ожидать от ваших дизайнеров. Рассмотрим эти руководства, в качестве вводного тура по различным возможностям:




И, наконец, другие сообщения об ошибках, которые не требуют реакции пользователя, могут быть отображены либо на полотне приложения, либо посредством всплывающих элементов. Смотрите материал "Создание макета пользовательского интерфейса: Ошибки" (http://msdn.microsoft.com/library/windows/apps/hh465304.aspx#errors) для того, чтобы узнать дальнейшие подробности. Позже мы рассмотрим некоторые примеры.
Если говорить о нижней панели приложения, то важно организовать команды в наборы, так как это упрощает их реализацию, как мы увидим в следущем разделе. Для того, чтобы получить полную информацию по этому вопросу, я рекомендую обратиться к материалам "Руководство по панелям приложения" (http://msdn.microsoft.com/library/windows/apps/hh465302.aspx) и "Проектирование командного интерфейса для приложений Магазина Windows" (http://msdn.microsoft.com/library/windows/apps/hh761499.aspx). Они содержат множество подробностей о размещении, разделении и группировке. Вот выдержки из этих руководств:

Панель приложения доступна всегда, в любых состояниях просмотра, в том числе – в прикрепленном. Рекомендована в прикрепленном режиме (и иногда в портретном) ограничивать число команд до 10, чтобы они уместились в один или два ряда.
Знайте, так же, что панель приложения не ограничена круглыми командными кнопками. Вы можете создать все, что захотите, обычно именно так реализуются верхние панели приложений. Работая с любым собственным макетом, убедитесь, что ваши элементы имеют размер, подходящий для сенсорного взаимодействия. Больше об этом можно найти в материалах "Руководство по панелям приложения" (http://msdn.microsoft.com/library/windows/apps/hh465302.aspx) и "Проектирование взаимодействия с сенсорным экраном" (http://msdn.microsoft.com/library/windows/apps/hh465415.aspx) в разделе "Целевые объекты сенсорного ввода Windows 8". Там можно увидеть и небольшую картинку с изображением вышеупомянутых пальцев баскетболиста.
Как отмечено выше, Microsoft рекомендует, чтобы ввод пользовательских даных для входа в программу, управление учетными данными, лицензионные соглашения, условия использования программы были доступны посредством чудо-кнопки Параметры. При этом программа добавляет необходимые команды на панель чудо-кнопки Параметры, которая появляется при активации этой чудо-кнопки. Эти команды затем вызывают вспомогательные страницы с необходимыми элементами управления для каждой функции. Конечно, иногда вход в систему или лицензионные соглашения требуют специальной обработки. Например, если ваше приложение требует ввода данных пользователя или принятия лицензионного соглашения при старте, подобные элементы управления можно отобразить на первой странице приложения или предоставить их посредством интерфейса средства выбора учетных даных (Credential Picker UI) (подробнее об этом – в лекции 3 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой"). Если пользователь вводит учетные данные и/или принимает лицензионное соглашение, приложение может продолжить работу. В противном случае приложению следует показать страницу, которая указывает на то, что учетные данные или принятие соглашения необходимы для того, чтобы пользователь мог с его помощью сделать что-нибудь более интересное, чем вывод сообщений об ошибках.
Если ввод учетных данных рекомендован, но не является необходимым, возможно, нужен лишь для включения дополнительных возможностей, вы можете разместить соответствующие элементы управления прямо на полотне. Когда пользователь вводит учетные данные, вы можете заменить эти элементы управления на сведения об учетной записи (имя пользователя и изображение, например, как на Начальном экране Windows). Если, с другой стороны, ввод учетных данных необязателен, соответствующие элементы управления лучше всего размещать в Параметрах приложения.
Во всех случаях, команды для просмотра лицензионного соглашения, для управления учетными записями или профилями, для входа в программу или выхода из нее, следует размещать среди средств чудо-кнопки Параметры. Другие команды на панели приложения или на полотне могут активировать Параметры программным образом, как мы увидим в лекции 2.
После размещения самых важных команд на полотне приложения, большинство команд будет размещено в панели приложения. Повторюсь, панель приложения можно вызвать разными способами – жестом скольжения от верхнего или нижнего края экрана, комбинацией клавиш Win+Z, щелчком правой кнопки мыши. Когда вы выполните одно из этих действий, Windows ищет элемент управления панели приложения на текущей странице и запускает его – вам не нужно самостоятельно обрабатывать события ввода.
Совет. Для того чтобы предотвратить появление панели приложения, вы можете использовать один из двух подходов. Первый заключается в том, чтобы полностью отключить появление панели приложения в ответ на любой из жестов, для этого нужно установить свойство winControl.disabled элемента панели в значение true. Второй применим тогда, когда вы, например, не хотите, чтобы панель приложения появлялась при щелчке правой кнопкой мыши по конкретному элементу (например, по полотну). Для этого можно прослушивать событие
contextmenu (то есть – щелчок правой кнопки мыши) для этого элемента и вызвать eventArgs.preventDefault() в обработчике события.
В приложениях, написанных на HTML и JavaScript, элемент управления панели приложения реализован как элемент управления WinJS:
WinJS.UI.AppBar
(http://msdn.microsoft.com/library/windows/apps/br229670.aspx). Для первой демонстрации нам достаточно тех возможностей, которые представлены в шаблоне проекта Приложение таблицы в Visual Studio/Blend, где стандартная панель приложения включена в default.html (изначально закомментирована):
<div id="appbar" data-win-control="WinJS.UI.AppBar">
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}">
</button>
</div>
Восхитительный результат этой разметки, с использованием таблицы стилей ui-dark.css, показан ниже:
Так как панель приложения объявлена в default.html, которая является контейнером для всех других элементов управления страниц, та же панель приложения будет использована для всех остальных страниц приложения. Используя такой подход, вы можете объявить все команды на одной панели приложения и присваивать командам различные классы, что позволит вам легко показывать и скрывать наборы команд для соответствующих страниц. Это так же позволяет централизованно управлять теми командами, которые актуальны для нескольких страниц, и вы можете подключить к ним обработчики событий в основном коде активации приложения (например, в default.js).
Кроме того, вы можете объявить панель приложения в разметке отдельных элементов управления страниц. До тех пор, пока панель приложения находится в DOM, жесты Windows активируют ее на каждой странице. В проекте Приложение таблицы, например, вы можете переместить вышеприведенную разметку из default.html в groupedItems.html, groupDetail.html и itemDetail.html с любыми модификациями для каждой страницы. Это может быть особенно полезным, если у страниц приложения немного общих команд.
В подобных случаях, метод ready каждой страницы должен позаботиться о том, чтобы привязать команды на панелях приложения этих страниц к обработчикам. Обратите внимание на то, что вы можете добавлять обрабочтики внутри метода страницы ready даже для общей панели приложения. Это – вопрос вызова addEventListener для нужного дочернего элемента в панели приложения.
Посмотрим теперь как все это работает с помощью примера "HTML-элемен управления AppBar" (http://code.msdn.microsoft.com/windowsapps/App-bar-sample-a57eeae9). (Дополнительные материалы к этой лекции так же содержат модифицированную версию этого примера). Начнем с основ панели приложения и со стандартной конфигурации, ориентированной на команды. Посмотрим, как показывать меню для некоторых из этих команд, и затем посомтрим, как можно создать собственный макет, как это делается для верхней панели приложения.
Подсказка. Чисто технически, вы можете объявить столько панелей приложения, сколько вам нужно, на любой странице, и все они будут представлены в DOM. Однако, последней из них, которая будет обрабатываться, будет та, что расположена на самом верху по оси Z по умолчанию, и, таким образом, она одна будет получать события. Windows не делает попыток комбинирования панелей приложения, в итоге, потому что элементы управления страниц вставляются в середину главной страницы наподобие default.html, панель приложения в default.html, которая объявляется после элемента управления страницы, появится над панелью приложения, объявленной на странице. В то же время, если панель приложения элемента управления страницы больше, чем в default.html, ее часть может быть видимой. В итоге: объявляйте панели приложения или на главной странице, или на элементах управления страниц, но не и там и там одновременно.
Как я уже говорил, может быть объявлена одна панель приложениян на все приложение, например, на странице-контейнере default.html, или собственную панель приложения можно объявить для каждого элемента управления страницы. Пример, посвященный элементу управления HTML AppBar, реализует последний вариант, так как он в нем показаны очень разные панели приложения, реализующие различные сценарии.
Сценарий 1 в примере (html/create-appbar.html) объявляет панель приложения с четырьмя командами и разделителем:
<div id="createAppBar" data-win-control="WinJS.UI.AppBar" data-win-options="">
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdAdd',
label:'Add', icon:'add', section:'global', tooltip:'Add item'}">
</button>
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdRemove',
label:'Remove', icon:'remove', section:'global', tooltip:'Remove item'}">
</button>
<hr data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{type:'separator', section:'global'}" />
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdDelete',
label:'Delete', icon:'delete', section:'global', tooltip:'Delete item'}">
</button>
<button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmdCamera',
label:'Camera', icon:'camera', section:'selection', tooltip:'Take a picture'}">
</button>
</div>
В приложении это выглядит так, как показано ниже, с использованием таблицы стилей ui-light.css. Здесь мы так же можем видеть всплывающую подсказку, прямоугольник фокуса ввода и эффект, применяемый при зависании указателя мыши над командой Add (Добавить) (для того, чтобы все это увидеть, я разместил указатель мыши над этой командой):
В разметке, элемент управления панель приложения объявлен так же, как другие элементы управления WinJS (это становится привычкой!), с использованием элемента-контейнера (это div) с data-win-control="WinJS.UI.AppBar". Каждая страница в этом примере загружается с помощью WinJS.UI.Pages.render, которая удобна тем, что вызывает WinJS.UI.processAll для создания экземпляра панели приложения. (Можно, как и для других элементов управления, программно создавать панель приложения, используя оператор new).
Этот пример не предусматривает каких-либо специальных параметров для панели приложения в data-win-options, но имеется ряд возможностей:
disabled, если установлено в значение true, создается изначально отключенная панель приложения. Значение по умолчанию – false.layout может принимать значение "commands" (по умолчанию) или "custom", как мы увидим в разделе "Пользовательские панели приложений и панели навигации" ниже.placement может быть либо "top", либо "bottom" (по умолчанию). Далее мы используем значение "top" для навигационной панелиsticky изменяет поведение панели приложения, когда она исчезает с экрана, если в ней нет необходимости. Со значением по умолчанию, установленным в false, панель приложения исчезает, когда вы щелкнете мышью или коснетесь экрана за ее пределами. Если параметр установлен в true, панель приложения будет оставаться видимой до тех пор, пока вы не измените параметр на false, и пользователь не коснется экрана за ее пределами, или вы программно не скроете ее с помощью метода hide.Если вам нужна постоянно видимая панель приложения с пользовательским макетом, отображаемая в верхней части экрана, вы можете воспользоваться размеркой, подобной этой:
<div id="navBar" data-win-control="WinJS.UI.AppBar"
data-win-options="{layout:'custom', placement:'top', sticky: true}">
Обратите внимание на то, что две панели приложения на странице, с различными значениями параметра placement не мешают друг другу. Кроме того, свойство sticky для каждого из расположений панелей действует независимо. Таким образом, если вам нужно реализовать верхнюю навигационную панель в масштабах всего приложения, вы можете объявить ее в default.htm (или в другой подобной странице) и объявить нижние панели приложения на каждом элементе управления страницы. Опять же, это всего лишь элементы в DOM!
Как вы можете видеть, элемент управления панели приложения может содержать любое количество дочерних элементов для своих команд, каждый из которых должен быть элементом управления ) внутри элемента button или hr, или экземпляр панели приложения не будет создан.
Свойства и параметры команд панели приложения перечислены ниже:
id Идентификатор элемента, который вы можете использовать с командой document.getElementById или с методом панели приложения getCommandById для подключения обработчиков события click.type Может принимать несколько значений: "button" (по умолчанию), "separator"(с его помощью создаются вертикальные разделители), "flyout" (используется для создания всплывающих меню, которые описывает свойство flyout; смотрите дальше раздел "Командные меню"), и "toggle" (создающий кнопку, которая может быть во включенном либо выключенном состоянии). В последнем случае, свойство selected команды может быть использовано для установки начального значения и получения состояния кнопки во время выполнения программы.label Задает текст, который отображается ниже командной кнопки. Его используют всегда вместо того, чтобы задавать текстовое содержимое самой кнопки, так как подобный текст не выравнивается соответствующим образом в элементе управления. (Попробуйте это сделать и увидите сами!). Кроме того, обратите внимание на то, что это свойство, наряду со свойством tooltip , которое описано ниже, часто локализуют, используя атрибут data-win-res . Мы рассмотрим это в лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", но уже сейчас вы можете посмотреть файл html/localize-appbar.html в примере Сценарий 8 для того, чтобы увидеть, как это работает.tooltip Содержит текст всплывающей подсказки для команды (обычно локализованный), в качестве значения по умолчанию используется текст, заданный в label. Обратите внимание на то, что это – обычный текст. Использование полнофункционального HTML- элемента управления WinJS.UI.Tooltip не поддерживается.\uE109 . Перечисление, фактически, просто предоставляет понятные имена для кодов символов – от \uE100 до \uE1E9 . Но вы не ограничены этими символами. Вы можете использовать любые escape-коды в формате ‘\uXXXX’ из шрифта Segoe UI Symbol (Обратите внимание на одиночные кавычки). Так же вы можете воспользоваться другими шрифтами или использовать собственные изображения, как показано в разделе "Пользовательские значки" section Управляет расположением команд. Для языков, где тексты расположены слева направо (таких, как английский), значение по умолчанию "selection" располагает команды (контекстно-зависимые) в левой части панели приложения. Свойство "global" располагает команды (глобальные для приложения) с правой стороны. В языках, где пишут справа налево (таких, как еврейский, арабский), стороны меняются местами. Эти простые варианты выбора приводят к единообразному расположению этих двух категорий команд (значение по умолчанию – "selection" ). Это притягивает взгляд пользователей к одной стороне панели, если они ищут постоянные команды, и к другой, если ищут команды, зависящие от контекста. Обратите внимание на то, что команды в каждом из разделов располагаются слева направо (или справа налево) в том порядке, в котором они появляются в разметке.onClick Может быть использовано для декларативного задания обработчика события click . Помните, что любая функция, упомянутая в разметке, должна быть помечена как безопасная для обработки (смотрите Главу 4 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript"). Обработчик события click может быть так же задан программно, с помощью addEventListener , в таком случае маркировка не нужна.disabled Отключает команду, если установлено в true . Значение по умолчанию – false .extraClass Задает один или большее количество классов CSS, которые прикреплены к команде. Они могут использоваться для индивидуальной стилизации командных элементов управления и для создания наборов команд, которые можно легко отображать и скрывать, как показано в разделе "Отображение, скрытие, включение и обновление команд" ниже.section Управляет расположением команд. Для языков, где тексты расположены слева направо (таких, как английский), значение по умолчанию "selection" располагает команды (контекстно-зависимые) в левой части панели приложения. Свойство "global" располагает команды (глобальные для приложения) с правой стороны. В языках, где пишут справа налево (таких, как еврейский, арабский), стороны меняются местами. Эти простые варианты выбора приводят к единообразному расположению этих двух категорий команд (значение по умолчанию – "selection" ). Это притягивает взгляд пользователей к одной стороне панели, если они ищут постоянные команды, и к другой, если ищут команды, зависящие от контекста. Обратите внимание на то, что команды в каждом из разделов располагаются слева направо (или справа налево) в том порядке, в котором они появляются в разметке.onClick Может быть использовано для декларативного задания обработчика события click . Помните, что любая функция, упомянутая в разметке, должна быть помечена как безопасная для обработки (смотрите Главу 4 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript"). Обработчик события click может быть так же задан программно, с помощью addEventListener , в таком случае маркировка не нужна.disabled Отключает команду, если установлено в true . Значение по умолчанию – false .extraClass Задает один или большее количество классов CSS, которые прикреплены к команде. Они могут использоваться для индивидуальной стилизации командных элементов управления и для создания наборов команд, которые можно легко отображать и скрывать, как показано в разделе "Отображение, скрытие, включение и обновление команд" ниже.Если вы хотите создавать команды во время выполнения приложения, вы можете сделать это, присвоив свойству панели приложения commands JSON-массив
AppBarCommand в любое время, когда панель приложения не видна (то есть, когда ее свойство hidden имеет значение true). Массив подобных дескрипторов из Сценария 1 нашего примера может выглядеть так (вы можете найти этот код в модифицированном примере к этой лекции; смотрите файл js/create_appbar.js):
var appbar = document.getElementById("createAppBar").winControl;
//Set the app bar commands property to populate it
var commands = [
{ id: 'cmdAdd', label: 'Add', icon: 'add', section: 'global', tooltip: 'Add item' },
{ id: 'cmdRemove', label: 'Remove', icon: 'remove', section: 'global', tooltip: 'Remove item' },
{ type: 'separator', section: 'global' },
{ id: 'cmdDelete', label: 'Delete', icon: 'delete', section: 'global', tooltip: 'Delete item' },
{ id: 'cmdCamera', label: 'Camera', icon: 'camera', section: 'selection',tooltip: 'Take a picture' }
];
appbar.commands = commands;
Когда создается панель приложения, происходит перебор массива commands и создание элементов управления WinJS.UI.AppBarCommand для каждого элемента. Если параметр type не задан или если он установлен в "button", "flyout" или "toggle", тогда создается командный элемент button. При параметре type, установленном в значение "separator" создается элемент hr. Обратите внимание на то, что вам следует локализовать поля label, tooltip, и, возможно, icon в каждом описании команды, вместо того, чтобы использовать встроенный текст, как показано здесь, в примерах кода.
Так же вы можете использовать подобный массив непосредственно в декларативной разметке, но подобная конструкция не поддается локализации и, таким образом, не рекомендуется (хотя я, в модифицированном примере, включил комментарии, которые показывают, как это делается). В то же время, так как значение commands в разметке – это обычная строка, вы можете присваивать данное значение посредством механизмов привязки данных к атрибутам, наподобие этого в элементе панели приложения:
data-win-bind="{ winControl.commands: myData.commands }"
Однако подобный подход не работает для атрибута data-win-res (как мы увидим в лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", и как, кроме того, показано в Сценарии 8 текущего примера), так как строка ресурса не может быть конвертирована в JSON как часть поиска ресурса. Попытка выполнить подобный трюк, в любом случае, принесет больше сложностей, чем преимуществ, поэтому лучше использовать либо декларативную форму описания в HTML, либо программное подключение локализованных массивов команд во время выполнения приложения.
Так же следует помнить о том, что commands – это редкий пример свойства только для записи (write-only property). Вы можете записать в него данные, но не можете получить массив из панели приложения. Панель приложения использует этот массив только для задания собственной конфигурирации и массив отбрасывается сразу после того, как все элементы созданы в DOM. Во время выполнения приложения, однако, вы можете использовать метод панели приложения getCommandById для получения конкретного командного элемента.
Говоря о командных элементах, об элементах управления AppbarCommand панели приложения (о тех, которые не являются разделителями), все это – лишь элементы button , и, таким образом, они реагируют на обычные события. Так как каждому командному элементу присвоен заданный вами id, вы можете использовать getElementById , как обычно, перед добавлением addEventListener . В Сценарии 1 примера, посвященного элементу управления HTML AppBar, например, подобный код можно увидеть в методе страницы ready :
document.getElementById("cmdAdd").addEventListener("click", doClickAdd, false);
document.getElementById("cmdRemove").addEventListener("click", doClickRemove, false);
document.getElementById("cmdDelete").addEventListener("click", doClickDelete, false);
document.getElementById("cmdCamera").addEventListener("click", doClickCamera, false);
Хотя такой подход работает, он относительно неэффективен, так как каждый вызов document.getElementById обходит всю DOM. Я рекомендую, вместо этого, использовать метод панели приложения getCommandById. Вот изменения, которые я внес в модифицированный пример, который можно найти в дополнительных материалах к этой лекции:
//Использование метода getCommandById панели приложения избавляет от необходимости обходить всю DOM для каждой кнопки
var appbar = document.getElementById("createAppBar").winControl;
appbar.getCommandById("cmdAdd").addEventListener("click", doClickAdd, false);
appbar.getCommandById("cmdRemove").addEventListener("click", doClickRemove, false);
appbar.getCommandById("cmdDelete").addEventListener("click", doClickDelete, false);
appbar.getCommandById("cmdCamera").addEventListener("click", doClickCamera, false);
Конечно, если вы зададите обработчик для свойства onclick каждой команды в разметке (эти обработчики должны иметь свойство supportedForProcessing, установленное в значение true), вы полностью устраните необходимость в подобных действиях!
Так же должно быть очевидно то, что можно подключать обработчики событий, подобные этим, в любом месте приложения, и вы можете, конечно, прослушивать любые другие события, особенно, когда задаете собственный макет панели приложения с иным пользовательским интфрейсом. Кроме того, знайте, что событие click удобно объединяет обработку касаний, нажатий мыши, событий ввода данных с клавиатуры, таким образом, в данной области вам не нужно выполнять дополнительные действия. В случае с клавиатурой, кстати, панель приложения позволяет вам перемещаться между командами с помощью клавиши Tab. Нажатие на Enter или на клавишу пробела активирует обработчик click.
В дополнение к методу панели приложения getCommandById , который мы только что рассмотрели, панель приложения имеет несколько других методов и событий. Во-первых, вот методы:
show отображает панель приложения, если ее свойство disabled установлено в значение false . В противном случае вызов игнорируется.hide скрывает панель приложения.showCommands, hideCommands и showOnlyCommands используются для управления наборами команд, как описано в следующем разделе "Показ, скрытие и обновление команд".Что касается событий, то их всего четыре, что обычно для элементов управления WinJS, которые накладываются на пользовательский интерфейс (то есть, для тех, которые не включаются в состав макета приложения):
beforeshow происходит перед тем, как всплывающий элемент станет видимым. Для панели приложения, это время, когда вы можете задать свойство commands в зависимости от состояния приложения в данный момент, активировать или деактивировать отдельные команды.aftershow происходит сразу после того, как всплывающий элемент станет видимым. Для панели приложения, если ее свойство sticky установлено в значение true , вы можете использовать это приложение для подстройки макета приложения, если у вас есть элементы для прокрутки содержимого, которые могут быть, в противном случае, частично перекрыты (смотрите ниже).beforehide происходит перед тем, как всплывающий элемент будет скрыт. Для панели приложения, вы можете использовать это событие для скрытия вспомогательныхз элементов пользовательского интерфейса, созданных вместе с панелью приложения и для перенастройки макета вокруг панели приложения с включенным режимом sticky .afterhide происходит сразу после того, как всплывающий элемент будет скрыт. Для панели приложения, это может быть временем перенастройки макета приложения, если в этом есть необходимость.Вы можете найти пример использования метода show вместе с событиями aftershow и beforeshow в Сценарии 4 примера, посвященного элементу управления HTML AppBar.
Проблемы с макетом приложения, названные выше (и то, что я держал в секрете во введении к этой лекции) возникают потому, что панель приложения накладывается на нижную часть страницы и перекрывает ее. Если эта страница содержит элемент управления для прокрутки содержимого, панель приложения со свойством sticky , установленным в true , для тех пользователей, которые применяют мышь, частично перекрывает вертикальную полосу прокрутки, а горизонтальную полосу прокрутки делает полностью недоступной. Если вы используете залипающую (sticky) панель приложения на подобных страницах, тогда, тогда – так как политика Магазина Windows не поддерживают ущемление прав пользователей мышей, вам следует использовать aftershow для уменьшения высоты элементов прокрутки с помощью значения панели приложения offsetHeight или clientHeight , таким образом, оставляя полосы прокрутки доступными. Когда панель приложения скрывается и происходит событие afterhide , вы можете снова перенастроить макет. Всегда используйте то значение, которое имеется во время выполнения приложения, такое, как clientHeight в данных вычислениях, так как оно учитывает масштабирование разрешения и потому, что высота панели приложения может меняться в зависимости от содержащихся на ней команд и режима просмотра.
Для того чтобы показать это, в Сценарии 6 имеется горизонтально прокручиваемый элемент управления ListView, который, в обычном состоянии, занимает почти всю страницу. Полоса прокрутки отображается вдоль самой нижней части элемента при использовании мыши. Если вы выделяете элемент, панель приложения переводится в режим залипания и отображается (смотрите функцию doSelectItem в файле js/appbar-listview.js):
appBar.sticky = true; appBar.show();
Вызов метода show приводит к вызову событий beforeshow и aftershow . Для подстройки макета подходит событие aftershow , которое позволяет работать с реальным значением высоты панели приложения. В примере это событие обрабатывается в функции doAppBarShow (так же в файле js/appbar-listview.js):
function doAppBarShow() {
var listView = document.getElementById("scenarioListView");
var appBar = document.getElementById("scenarioAppBar");
var appBarHeight = appBar.offsetHeight;
// Сделать полосу прокрутки видиой, если панель приложения в режиме залипания
if (appBar.winControl.sticky) {
var listViewTargetHeight = "calc(100% - " + appBarHeight + "px)";
var transition = { property: 'height', duration: 367,
timing: "cubic-bezier(0.1, 0.9, 0.2, 0.1)",
to: listViewTargetHeight
};
WinJS.UI.executeTransition(listView, transition);
}
}
Примечание. Пример из Центра разработчиков Windows использует beforeshow вместо aftershow , в результате, иногда полученная высота панели приложения равняется нулю и макет не подстраивается соответствующим образом. Для того чтобы гарантировать то, что панель приложения имеет подходящую высоту для подобных вычислений, используйте событие aftershow , как показано в модифицированном примере, включенном в состав дополнительных материалов к этой лекции.
Здесь вы можете видеть, что значение appBar.offsetHeight просто вычитается из значения height элемента управления ListView с применением анимированного перехода. (Смотрите Главу 5). Операция выполняется в обратном порядке в doAppBarHide , где высота ListView просто сбрасывается до 100% с использованием похожей анимации. В этом случае обработчик события совсем не зависит от высоты панели приложения, поэтому мы можем использовать либо событие beforehide, либо – afterhide . Если, с другой стороны, вам нужно знать размер панели приложения для настройки вашего макета, используйте событие beforehide .
В качестве эксперимента, запустите Сценарий 7 из SDK-примера. Обратите внимание на то, как нижняя часть полосы прокрутки текстовой области перекрывается залипающей панелью приложения. Попытайтесь взять код из Сценария 6 для того, чтобы используя события aftershow и beforehide настроить высоту текстовой области и сохранить видимость полосы прокрутки. И не считайте это оценочным испытанием ваших возможностей: в дополнительных материалах к этой лекции есть решение этой задачи.
В предыдущем разделе я упоминал об использовании события beforeshow для настройки свойства commands панели приложения таким образом, чтобы панель содержала команды, соответствующие текущей странице и состоянию страницы. Это может включать установку свойства disabled для некоторых команд, которые, например, зависят от состояния выделения. Это можно реализовать с помощью массива commands, в разметке, или, опять же, используя метод панели приложения getCommandById:
appbar.getCommandById("cmdAdd").disabled = true;
Позвольте мне повторить, что команды, которые появляются на панели приложения, уникальны для каждой страницы. Нет необходимости пытаться поддерживать неизменную структуру панели приложения для всех страниц. Таким образом, если команды не нужны на какой-либо странице, не беспокойтесь о том, чтобы их отображать. Что важнее, так это то, постоянный состав панели приложения для отдельно взятой страницы, так как это по-настоящему неудачная идея – появляющиеся и исчезающие команды, зависящие от состояния страницы. Это приведет к тому, что пользователи будут размышлять о том, как привести страницу в нужное состояние для того, чтобы появились какие-либо команды!
Говоря об изменениях, вполне допустимо изменять или обновлять команды во время выполнения программы, что может устранить необходимость создания множества команд, которые вы можете поочередно показывать или скрывать. Так как каждая команда в панели приложения – это лишь DOM-элемент, вы можете, на самом деле, выполнять любые изменения, какие хотите, в любое время. Пример этого показан в Сценарии 3 примера, где панель приложения изначально создается с кнопкой Play (html/custom-icons.html):
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmdPlay', label:'Play', icon:'play', tooltip:'Play this song'}">
</button>
Обработчик события click этой кнопки использует функцию doCluckPlay в js/custom-icons.js для переключения между состояниями:
var isPaused = true;
function doClickPlay() {
var cmd = document.getElementById('cmdPlay');
if (!isPaused) {
isPaused = true; // поставлено на паузу cmd.winControl.icon = 'play';
cmd.winControl.label = 'Play'; cmd.winControl.tooltip = 'Play this song';
} else {
isPaused = false; // проигрывается cmd.winControl.icon = 'pause';
cmd.winControl.label = 'Pause';
cmd.winControl.tooltip = 'Pause this song';
}
}
Вы можете использовать что-то подобное для закрепления или открепления дополнительных плиток (secondary tiles), как мы увидим в лекции 2 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой". И, снова, кнопки – это лишь элементы в DOM и обновление любых их свойств, включая стили, обновит элемент на экране, как только вы вернете управление в поток пользовательского интерфейса.
Использование beforeshow для целей настройки команд, безусловно, эффективно, но вы можете достичь тех же результатов другими способами. Стратегия, которой вы пользуетесь, зависит от архитектуры вашего приложения и от ваших собственных предпочтений. С точки зрения пользователя, до тех пор, пока нужные команды доступны ему в нужное время, неважно, как это реализовано в программе!
Размышления в рамках выбранного подхода особенно важны при работе с режимом прикрепленного просмотра, так как рекомендовано, чтобы в подобном режиме использовалось десять или меньшее количество команд, таким образом, панель приложения вместит их в одном или в двух рядах. Это означает, что вам нужно подумать о том, как настроить панель приложения для различных состояний просмотра, возможно, комбинируя несколько команд во всплывающем меню отдельных кнопок.
Один из подходов заключается в том, чтобы каждая страница приложения объявляла и поддерживала собственную панель приложения, что подразумевает создание панелей приложения "на лету" в методе страниц ready. Это делает взаимодействие между содержимым страницы и панелью приложения весьма четким и ограниченным рамками страницы. Недостаток такого подхода заключается в том, что общие команды – те, которые появляются более чем на одной странице – приходится объявлять множество раз, что ухудшает возможности по управлению ими и способствует привлечению небольшой неоднородности, которая приходит как муравьи на сахар. Тем не менее, если содержимое разных страниц сильно отличается и у вас есть лишь несколько общих команд, такой подход может быть правильным выбором. Это, так же, необходимо, если ваша страница использует несколько страниц верхнего уровня, вместо одной страницы с элементом управления страницы, как мы обсуждали в
лекции 3курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", так как каждая HTML-страница верхнего уровня, в любом случае, должна объявить свою собственную панель приложения.
Для приложений, использующих элементы управления страниц, применяется другой подход, который заключается в объявлении одной панели приложения на странице верхнего уровня и в установке ее свойства command внутри метода ready каждого из подобных элементов управления. Недостатком здесь является то, что так как command – это свойство только для записи, вы не можете объявить общие команды в HTML и позже добавить команды, характерные для отдельной страницы, только если вы не идете сложным путем создания каждого отдельного дочернего элемента AppBar в каждом методе ready . Подобный код утомительно писать и поддерживать.
К счастью, есть и третий подход, который позволяет вам объявить одну панель приложения на странице верхнего уровня, которая содержит все необходимые команды, для всех страниц, и затем выборочно показывать определенные наборы таких команд в каждом из методов ready отдельных страниц. Это – цель существования методов панели приложения showCommands, hideCommands и showOnlyCommands .
Все эти методы прпнимают массив командных элементов, которые могут быть либо объектами AppBarCommand , либо id команд. Метод showCommands делает эти командные элементы управления видимыми и может быть вызван несколько раз с различными установками для получения итогового набора. С другой стороны, метод hideCommands скрывает заданные команды в панели приложения, опять же, с возможностью многократного вызова. Основы использования этих методов показаны в Сценарии 4 нашего примера.
Метод showOnlyCommands объединяет два вышеописанных метода, делая заданные команды видимыми и скрывая остальные. Если вы задаете панель приложения со всеми командами, вы можете использовать метод showOnlyCommands в методе ready каждой страницы для того, чтобы быстро и просто настроить вид панели приложения. Хитрость заключается в получении массива, подходящего для передачи в метод. Вы можете, конечно, вручную написать код для команд, занести данные в специальный массив, как в Сценарии 4 примера сделано для showCommands и hideCommands . Однако, если вы думаете, что это хороший пример Плохой идеи, то знайте, что я тоже так думаю! Использование подобного массива подразумевает, что любые изменения, которые вы вносите в панель приложения, должны произойти и в HTML-файле, и в JS-файле, а это означает, что любой, кто будет поддерживать ваш код в будущем, без сомнения, станет осыпать вас проклятиями!
Лучший путь к счастью и долгой жизни, таким образом – программно получать необходимые массивы из DOM, используя свойство extraClass для каждой команды для того, чтобы эффективно задавать наборы команд. Это позволяет вам вызывать querySelectorAll для получения тех команд, которые принадлежат опреденному набору.
Рассмотрим нижеприведенное определение панели приложения, где, для краткости, я опустил свойства вроде label, icon и section , так же, как и любые другие классы стилей:
<div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="{
commands:[
{id:'home', extraClass: 'menuView gameView scoreView'},
{id:'play', extraClass: 'menuView gameView scoreView'},
{id:'rules', extraClass: 'menuView gameView scoreView'},
{id:'scores', extraClass: 'menuView gameView scoreView'},
{id:'newgame', extraClass: 'gameView gameSnapView'},
{id:'resetgame', extraClass: 'gameView gameSnapView'},
{id:'loadgame', extraClass: 'gameView gameSnapView'},
{id:'savegame', extraClass: 'gameView gameSnapView'},
{id:'hint', extraClass: 'gameView gameSnapView'},
{id:'timer', extraClass: 'gameView gameSnapView'},
{id:'pause', extraClass: 'gameView gameSnapView'},
{id:'home2', extraClass: 'gameSnapView'},
{id:'replaygame', extraClass: 'scoreView'},
{id:'resetscores', extraClass: 'scoreView'}
]}" >
</div >
В свойствах extraClass мы задаем четыре различных набора: menuView, gameView, gameSnapView, и scoreView. Когда все находится там, где нужно, простой вызов querySelectorAll предоставляет тот массив, который нам нужен для showOnlyCommands. Общая функция, наподобие следующей, может быть, затем, использована из метода ready каждой страницы (или из любого другого места) для активации командных элементов для нужного экрана программы:
function updateAppBar(view) {
var appbar = document.getElementById("appbar").winControl;
var commands = appbar.element.querySelectorAll(view);
appbar.showOnlyCommands(commands);
}
При таком подходе, заслуга создания которого принадлежит моему колеге Джесси МакГата, панель приложения полностью определяется в едином месте, что серьезно упрощает управление ей и ее поддержку.
Свойство команд extraClass может, конечно, быть использовано для целей стилизации, так же, как и для управления наборами команд. Это очень просто: любые классы, которые заданы в extraClass, добавляются к элементам AppBarCommand, созданных для панели приложения.
Существуют семь классов WinJS для стилизации, которые используются панелью приложения, как описано в нижеприведенной таблице, где первые два применяются к панели приложения в целом, а другие пять – к отдельным командным элементам:
| CSS-класс (панель приложения) | Описание |
|---|---|
win-appbar |
Задает стиль контейнера панели приложения. Обычно этот стиль используется в качестве корневого для более специфических селекторов. |
win-commandlayout |
Задает стиль макета команд панели приложения. Обычно приложения не модифицируют этот стиль |
| CSS-класс (команды) | Описание |
win-command |
Задает стиль всех элементов AppBarCommand. |
win-commandicon |
Задает стиль контейнера элемента для AppBarCommand. |
win-commandimage |
Задает стиль изображения для AppBarCommand. |
win-commandring |
Задает стиль круглой рамки, которая выводится вокруг изображения на AppBarCommand. |
win-label |
Стилизует подпись AppBarCommand. |
Подсказка. Для того чтобы помочь себе в настройке стилей панели приложения в Blend сделайте ее залипающей (sticky ), или добавьте вызов метода show в методе ready страницы, или в обработчике события activated . Это позволит сделать панель приложения видимой в Blend, работать с ней, в противном случае возникают сложностью с открытием панели в процессе разработки.
Обычно нет необходимости в прямом переопределении стилей win-appbar или win-commandlayout. Вместо этого следует создавать селекторы для пользовательских классов, связанных с этими стилями и затем стилизовать то, что вам нужно. Здесь можно использовать и псевдоселекторы, наподобие button:hover, button:active и так далее.
В Сценарии 2 примера, посвященного элементу управления HTML AppBar, показано много подобных селекторов в действии, в данном случае – для установки синего фона панели приложения и команд и зеленого цвета переднего плана (несколько отвратительное сочетание, однако, показывающее все, что требуется от примера).
В качестве основы, Сценарии 2 (html/custom-color.html) добавляет CSS-класс customColor к панели приложения:
<div id="customColorAppBar" data-win-control="WinJS.UI.AppBar" class="customColor" ...>
В css/custim-color.css он затем стилизует селекторы на основе .win-appbar.customColor. Следующие правила, например, задают фоновый цвет, цвет текста подписи и цвет круглой рамки вокруг команды для состояний :hover и :active:
.win-appbar.customColor {
background-color: rgb(20, 20, 90);
}
.win-appbar.customColor .win-label {
color: rgb(90, 200, 90);
}
.win-appbar.customColor button:hover .win-commandring,
.win-appbar.customColor button:active .win-commandring {
background-color: rgba(90, 200, 90, 0.13);
border-color: rgb(90, 200, 90);
}
Все эти настройки стилей, кстати, применимы только к стандартному макету, основанному на командах. Если вы используете пользовательский макет, панель приложения просто содержит в себе все элементы, которые вы хотите на нее добавить, с любыми классами стилей, таким образом, вы можете управлять стилизацией как при работе с любыми другими HTML-элементами
Ранее мы видели, что свойство icon элемента типа AppBarCommand обычно является одним из символов шрифта Segoe UI Symbol. Хотя для большинства нужд это подходит, время от времени может возникнуть необходимость в использовании символов из разных шрифтов (некоторые из нас просто не могут не пользоваться Wingdings!) или в отображении пользовательских изображений. Панель приложения поддерживает и то и другое.
Для использования различных шрифтов для всей панели приложения, просто добавьте класс к панели приложения и создайте правило, основанное на win-appbar :
win-appbar.customFont {
font-family: "Wingdings";
}
Для изменения шрифта определенной командной кнопки, добавьте класс к ее свойству extraClass (такому, как customButtonFont ) и создать правило со следующим селектором (как в Сценарии 1 модифицированного примера):
button.customButtonFont .win-commandimage {
font-family: "Wingdings";
}
Для использования собственных изображений, выполните следующие шаги для масштаба разрешения 100%:
Для других масштабов разрешений, умножьте размеры на 1.4 (140%) и 1.8 (180%) и используйте в именах файлов суффиксы .scale-140 и .scale-180.
Для использования пользовательского значка, установите свойство icon командного элемента в значение базового URI изображения (без суффиксов .scale-1x0). Например, icon: 'url(images/icon.png)' .
Сценарий 3 примера, посвященного элементу управления HTML AppBar показывает использование пользовательских значков для кнопки Accept:
Значок хранится в файле, который называется accept.png, который выглядит примерно так, как показано ниже. Я настроил яркость и контраст, добавил рамку, чтобы вы могли четко увидеть каждую ячейку:
Объявление кнопок панели приложения, таким образом, выглядит так, как показано ниже (некоторые свойства опущены для краткости):
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmdAccept', label:'Accept', icon:'url(images/accept.png)' }">
Обратите внимание на то, что хотя в примере нет различных значков для разных масштабов разрешения, он содержит варианты для тем с высокой контрастностью, это важное условие обеспечения соглашений по специальным возможностям, о которых мы поговорим в лекции 6 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой". По этой причине элемент button включает style="-ms-high-contrast-adjust:none" для того, чтобы переопределить автоматическую настройку для тем с высокой контрастностью.
Следующий аспект панели приложения, который нуждается в пояснениях, касается тех команд, свойство type который установлено в значение flyout. В данном случае свойство flyout командного элемента должно идентифицировать объект WinJS.UI.Flyout или на элемент управления WinJS.UI.Menu (он является всплывающим элементом). Как мы упоминали ранее, всплывающие элементы или всплывающие меню наподобие этих используются тогда, когда в панели приложения нужно расположить слишком много связанных команд, или когда вам нужны другие типы элементов управления, которые не вполне подходят для их непосредственного расположения в панели приложения. Если вы пытаетесь воспользоваться кнопками с подписями "Еще", "Дополнительно", "Другие возможности", так как вы не можете решить, как организовать команды, это верный признак того, что приложение получается слишком сложным. Изыщите способы упрощения целей приложения, в итоге панель приложения не превратится в хранилище случайных элементов управления.
Мы подробнее поговорим о всплывающих элементах ниже в этой лекции, а сейчас посмотрим, как использовать их в панели приложения, как показано в Сценарии 6 примера "Всплывающие элементы управления HTML" (http://code.msdn.microsoft.com/windowsapps/Flyout-sample-258757b3) (обратите внимание, это не пример про панель приложения!):
В файле html/appbar-flyout.html этого примера мы видим, что кнопка панели приложения задана следующим образом:
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'respondButton', label:'Respond', icon:'edit', type:'flyout', flyout:'respondFlyout'}"
>
Элемент respondFlyout, который здесь идентифицируется, определен ранее, в html/appbar-flyout.html. Обратите внимание на то, что подобный элемент должен быть объявлен раньше, чем панель приложения для того, чтобы его экземпляр был создан до создания панели приложения:
<div id="respondFlyout" data-win-control="WinJS.UI.Menu"
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'alwaysSaveMenuItem',
label:'Always save drafts', type:'toggle', selected:'true'}">
</button>
<hr data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'separator', type:'separator'}" />
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'replyMenuItem', label:'Reply'}">
</button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'replyAllMenuItem', label:'Reply All'}">
</button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'forwardMenuItem', label:'Forward'}">
</button>
</div>
Вас не должно уже удивить то, что меню – это лишь еще один элемент управления WinJS, WinJS.UI.Menu, дочерние элементы которого определяютс содержимое меню. Все эти элементы, опять же, лишь элементы в DOM. Их события click подключаются к обработчикам в js/appbar-flyout.js с уже известным вам addEventListener. (В примере использована команда document.getElementById для получения элементов при вызове addEventListener. Эффективнее будет использовать метод панели приложения getCommandById, как в модифицированном примере работы с панелью приложения).
Каждый элемент меню, как вы можете видеть, это объект WinJS.UI.MenuCommand, мы так же рассмотрим подробности позднее, а сейчас вы можете видеть, что эти элементы имеют свойства id, label, type – это очень похоже на объекты WinJS.UI.AppBarCommand.
Вот и почти все, что нужно сделать, добавлю лишь, что когда элемент меню выбирают, нужно скрыть меню и, возможно, так же панель приложения (если она не находится в режиме залипания). Это показано в примере, в файле hs/appbar-flyout.js, в функции, которая называется hideFlyoutAndAppBar:
function hideFlyoutAndAppBar() {
document.getElementById("respondFlyout").winControl.hide();
document.getElementById("appBar").winControl.hide();
Все это время мы изучали стандартный макет команд панели приложения, что, конечно, является самым простым способом использования этого элемента управления. Однако, стандартных возможностей хватает не всегда. Возможно, вы захотите расположить на панели приложения больше интересных элементов управления, в особенности это касается пользовательских элементов управления (таких, как средство для выбора цвета). Для подобных целей вы можете установить свойство панели приложения layout в значение ‘custom’ и задать любой HTML-макет для панели приложения, стилизовать его с помощью CSS и подключить обработчики любых необходимых событий в JavaScript.
Пользовательские макеты обычно используют для создании верхней панели навигации – то есть, панели приложения со свойством placement установленным в значение ‘top’ – так как командные кнопки – это обычно не тот пользовательский интерфейс, который вам нужен. Мы видели пример подобной панели приложения ранее, в приложении Weather, и навигационная панель в Internet Explorer устроена по тому же принципу:
Наш старый друг – пример элемента управления HTML AppBar (http://code.msdn.microsoft.com/windowsapps/App-bar-sample-a57eeae9) предоставляет пример пользовательского макета в Сценарии 5. В html/custom-layout.html мы видим разметку для пользовательской верхней панели приложения, содержащей произвольные элементы:
<div id="customLayoutAppBar" data-win-control="WinJS.UI.AppBar" aria-label="Navigation Bar"
data-win-options="{layout:'custom', placement:'top'}">
<header aria-label="Navigation bar" role="banner">
<button id="cmdBack" class="win-backbutton" aria-label="Back"
</button>
<div class="titleArea">
<h1 class="win-type-xx-large" tabindex="0">
Page Title </h1>
</div>
</header>
</div>
Правда, результат этого примера странноват – он создает навигационную панель с обычным заголовком страницы и кнопкой "Назад", где каждый элемент управления может иметь прямоугольник выделения. Я не советую следовать подобным образцам дизайна!
Как я упоминал в подразделе "Некоторые советы и секреты" (в разделе "Стилизация элементов управления), в лекции 4 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", вы можете подавить вывод прямоугольника фокуса с помощью правила <selector>:focus { outline: none; } в CSS. Для того, чтобы убрать его с кнопки "Назад", например, вы можете добавить следующее правило стиля в css/custom-layout.css
.win-appbar header .win-backbutton {
margin-left: 39px;
margin-top: 59px;
outline: none;
}
Снова обратите внимание на то, как это правило и другие правила в css/custom-layout.ss используют класс win-appbar как базовый селектор, но только потому, что он используется для стилизации других обычных классов, таких, как header или win-backbutton. Если вы используете особые классы в панели приложения или в навигационной панели, вы можете совсем не пользоваться селектором win-appbar.
Для того, чтобы реализовать навигационную панель, напоминающую таковую в Internet Explorer или в приложении Weather, вы можете использовать элемент управления ListView вместе с шаблонами элементов или пользовательскими функциями вывода элементов, где вы подключаете события itemInvoked к WinJS.Navigation.navigate и так далее. Опять же, здесь нет ничего особенно сложного: панель приложения с пользовательским макетом – это лишь всплывающий контейнер для других HTML-элементов.
Возвращаясь к дискуссии о том, где размещать командные элементы управления, напомним, что всплывающий элемент управления WinJS.UI.Flyout – используется для целей подтверждения действий, сбора информации и получения ответов на вопросы при реакции на действия пользователя. Меню – элемент управления WinJS.UI.Menu – это, в свою очередь, особый вид всплывающего элемента, который содержит элементы управления WinJS.UI.MenuCommand вместо произвольного HTML-кода. На самом деле, WinJS.UI.Menu является, благодаря использованию WinJS.Class.define, прямым наследником WinJS.UI.Flyout , поэтому у них много общего. Что касается всплывающих элементов, они так же имеют общие черты с панелью приложения. (Классы панели приложения и всплывающего элемента являются наследниками базового класса WinJS.UI._Overlay , который является внутренним классом WinJS).
Совет. В дополнение к объекту ).
Прежде чем мы рассмотрим подробности, взглянем на некоторые изображения из примера "Всплывающий элемент управления HTML" (http://code.msdn.microsoft.com/windowsapps/Flyout-sample-258757b3), где мы уже видели меню, которое всплывает по команде элемента управления панели приложения. Элементы управления WinJS.UI.Flyout, использованные в сценариях 1-4 показаны на рис. 1.1. Обратите внимание на использование разного содержимого во всплывающих элементах и на то, что всплывающий элемент всегда расположен около элемента управления, который вызвал его, например, у кнопок Buy, Login, Format output text и около текста гиперссылки Lorem ipsum. Эти примеры показывают, что всплывающий элемент может содержать простое сообщение с кнопкой (Сцераний 1, для предупреждений и подтверждений), может содержать поля для ввода информации или изменения параметров (Сценарии 2 и 3), и может иметь заголовок (Сценарий 4). Сценарий 5, в свою очередь, содержит пример всплывающего меню заголовка WinJS.UI.Menu, которое мы увидим немного позже.
Существуют две ключевые характеристики всплывающего элемента управления, включая меню. Первая заключается в том, что всплывающий элемент может быть скрыт программно, как панель приложения, когда активирован соответствующий элемент управления внутри него. Подобное поведение характерно для кнопки Complete order в Сценарии 1, для кнопки Login в Сценарии 2.
Вторая характеристика, так же актуальная для панели приложения, это закрытие неактивных всплывающих окон (light dismiss): щелчок мышью или прикосновение к области вне элемента управления скрывает его, как при нажатии клавиши Escape, что означает, что такое закрытие аналогично нажатию на кнопку Cancel (Отмена) или на кнопку закрытия в традиционном диалоговом окне. Преимущество такого подхода заключается в том, что нам не нужны видимые кнопки для этой цели, что помогает упрощению пользовательского интерфейса. В то же время, обратите внимание на то, что в Сценарии 3 рис. 1.1 нет кнопки OK или другого элемента управления, подтверждающего изменения, которые вы могли внести во всплывающем элементе. При рассматриваемой модели взаимодействия, изменения немедленно вступают в силу, таким образом ,закрытие всплывающего элемента не возвращает их к исходному состоянию и не отменяет. Если вас такое поведение не устраивает, вы можете разместить что-то вроде кнопки "Применить изменения" во всплывающем элементе и не вносить изменения до тех пор, пока кнопка не будет нажата. В таком случае, закрытие всплывающего элемента отменит изменения.
(рис 1.1) Примеры всплывающих элементов управления из примера, который посвящен всплывающим элементам HTML
Я снова предлагаю вам почитать "Руководство и контрольный список для всплывающих элементов" (http://msdn.microsoft.com/library/windows/apps/hh465341.aspx), который в подробностях рассматривает разные варианты дизайна и размещения, которые возможны для этого элемента управления. В нем так же говорится о том, где не следует использовать этот элемент управления: например, для вывода сведений об ошибках, не связанных с действиями пользователя (вместо этого используйте окна сообщений), для основных команд (используйте панель приложения), для контекстных меню, связанных с выделением текста, и для элементов пользовательского интерфейса, которые являются частью рабочего процесса и которые следует размещать на полотне приложения. Описываемые принципы так же предполагают использование всплывающих элементов небольших размеров, целенаправленное их использование (без лишних элементов управления), и их расположение около объекта, который их вызывает. Посмотрим теперь, как все это работает в коде.
Примечание. В дополнение к приложениям, которые могут отображать собственные элементы WinJS.UI.Flyout, некоторые системные API (например, при создании дополнительных плиток) создают системные всплывающие элементы. В подобных случаях приложение получает событие blur, что ведет к закрытию панелей приложения. Для предотвращения подобного поведения, переведите панель приложения в режим залипания (sticky) при вызове этих API.
Большинство свойств, методов и событий элемента управления WinJS.UI.Flyout – это те, что мы уже рассматривали для панели приложения. Методы show и hide управляют его видимостью, свойство hidden показывает состояние видимости, в соответствующие моменты вызываются события beforeshow, aftershow, beforehide , и afterhide . Событие afterhide обычно используют для определения момента закрытия элемента.
Как и панель приложения, всплывающий элемент имеет свойство placement, но оно имеет другие значения, которые имеют смысл лишь в контексте свойств всплывающего элемента alignment и anchor . На самом деле, все три свойства – это необязательные параметры для метода show, так как они определяют позицию, в которой всплывающий элемент отобразится на экране. Значения placement и alignment могут быть установлены на сам элемент управления, так как они не обязательны при вызове show . (Заметим, что если вы не установите anchor в методе show , это свойство уже должно быть установлено на элемент управления, иначе show выдаст исключение.)
Свойство anchor идентифицирует элемент управления, который активирует всплывающий элемент, или любую другую операцию, которая может открыть всплывающий элемент (как в случае с подтверждением некоторого действия). Свойство placement, указывает, как всплывающий элемент должен располагаться по отношению к положению, заданному свойством anchor: 'top', 'bottom', 'left', 'right' , или 'auto' (по умолчанию). Обычно placement задают только тогда, когда всплывающий элемент может перекрыть важное содержимое. В противном случае вы рискуете тем, что всплывающий элемент будет уменьшен для того, чтобы занять доступное ему место. Содержимое всплывающего элемента при этом останется того же размера, что означает, - вот незадача! – что появятся полосы прокрутки! Таким образом, если только у вас нет достойной причины и справки от врача, используйте тип размещения 'auto' , так всплывающий элемент будет размещен там, где он может быть показан в полном размере. В том же духе, помните, что в прикрепленном режиме просмотра у вас есть лишь 320 пикселей по горизонтали, что означает, что всплывающий элемент, который вы показываете в данном режиме просмотра следует делать такого или меньшего размера.
Свойство alignment, в свою очередь, когда используется со свойством placement , установленным в 'top' или 'bottom' , определяет, как всплывающий элемент выравнивается с anchor: 'left', 'right' , или 'center' (по умолчанию). Само содержимое всплывающего элемента выравнивается посредством CSS как и любые HTML-элементы.
Если вам нужно стилизовать сам всплывающий элемент управления, вы можете задать стили в классе win-flyout , наподобшие шрифтов, выравнивания по умолчанию, полей и так далее. Что касается других классов стилей WinJS, родственных этому, используйте win-flyout как базу для более конкретных селекторов, если только вы не хотите стилизовать каждый всплывающий элемент в приложении. Обычно, на самом деле, вы так же исключаете win-menu из правила, таким образом, на всплывающие элементы меню такая стилизация не действует. Например, большинство сценариев в примере, который посвящен всплывающим элементам управления HTML, которые мы рассмотрим в дальнейшем, имеют правила наподобие такого:
.win-flyout:not(.win-menu) button,
.win-flyout:not(.win-menu) input[type="button"] {
margin-top: 16px;
margin-left: 20px;
float: right;
И, наконец, если по какой-либо причине вам нужно знать, когда загружается всплывающий элемент, прослушивайте DOMNodeInserted в document.body:
document.body.addEventListener("DOMNodeInserted", insertionHandler, false);
Всплывающий элемент управления создается так же, как любой другой элемент управления WinJS, с помощью атрибутов data-win-control и data-win-options, и обрабатывается WinJS.UI.process/processAll. Всплывающие элементы с относительно постоянным содержимым элементов обычно объявляют в разметке, где вы можете использовать привязку данных для некоторых свойств элементов во всплывающем элементе. Всплывающие элементы с динамическим содержимым, с другой стороны, могут быть созданы прямо из кода с использованием команды вида new WinJS.UI.Flyout(<element>, <options>) , и вы можете, безусловно, менять его дочерние элементы в любое время. Все это – лишь часть DOM! (Не повторяю ли я собственные слова?)
Как я говорил раньше (видимо, я повторяюсь) элемент управления WinJS.UI.Flyout может содержать произвольный HTML, стилизованный, как всегда, с помощью CSS. Всплывающий элемент для Сценария 1 в примере выглядит следующим образом в html/confirm-action.html (слегка сокращенный вид):
<div id="confirmFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{Confirm purchase flyout}">
<div>Your account will be charged $252. </div>
<button id="confirmButton">Complete Order</button>
</div>
Всплывающий элемент для входа в систему из Сценария 2 похож, и он даже использует HTML-форму для прикрепления к кнопке Login события нажатия на кнопку Enter на клавиатуре:
<div id="loginFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{Login flyout}" >
<form onsubmit="return false;" >
<p >
<label for="username" gt;Username <br / ></label >
<span id="usernameError" class="error" > </span >
<input type="text" id="username" / >
</p >
<p >
<label for="password">Password<br / ></label >
<span id="passwordError" class="error" ></span >
<input type="password" id="password" / >
</p >
<button id="submitLoginButton" >Login</button >
</form >
</div >
Всплывающий элемент отображается путем вызова его метода show . В Сценарии 1, например, событие кнопки click прикреплено к функции showConfirmFlyout (js/confirm-action.js), где кнопка Buy задается как элемент прикрепления. Обработка нажатия на кнопку Complete Order так же производится посредством обработчика события click, прикрепленного к этому элементу, и здесь мы вызовем метод hide для программного закрытия всплывающего элемента. Наконец, событие afterhide используется для выявления факта закрытия элемента:
var bought;
var page = WinJS.UI.Pages.define("/html/confirm-action.html", {
ready: function (element, options) {
document.getElementById("buyButton").addEventListener("click", showConfirmFlyout, false);
document.getElementById("confirmButton").addEventListener("click",
confirmOrder, false);
document.getElementById("confirmFlyout").addEventListener("afterhide",
onDismiss, false);
}
function showConfirmFlyout() {
bought = false;
var buyButton = document.getElementById("buyButton");
document.getElementById("confirmFlyout").winControl.show(buyButton);
// Когда кнопка Buy нажата, скрыть всплывающий элемент, так как пользователь завершил работу с ним.
function confirmOrder() {
bought = true;
document.getElementById("confirmFlyout").winControl.hide();
}
// При закрытии элемента, определяет, был ли он закрыт потому, что пользователь нажал
// Кнопку Byu. Если нет, элемент был закрыт автоматически.
function onDismiss() {
if (!bought) {
// (В примере сообщение о закрытии отображается на полотне)
}
Работа с элементами управления для входа в систему в Сценарии 2, в основном – это то же самое, с некоторыми дополнениями в виде кода, который позволяет проверить, были ли заполнены поля для ввода имени пользователя и пароля. Если это не так, обработчик нажатия кнопки Login показывает встроенное в элемент сообщение об ошибке и устанавливает фокус ввода на соответствующее поле:
Так как всплывающий элемент в Сценарии 2 немного больше, значение по умолчанию ‘auto’ для свойства placement (как в имитаторе) приводит к появлению элемента ниже кнопки, которая его вызывает. Выше этой кнопки не так много места. Поэтому попытаемся установить свойство placement в значение ‘top’ в вызове show:
function showLoginFlyout() {
// ...
document.getElementById("loginFlyout").winControl.show(loginButton, "top");
}
После этого вы можете увидеть, как во всплывающем элементе появляются полосы прокрутки, так как его размер слишком мал:
Каким словом я раньше это называл? "Незадача"?
Двигаемся дальше, к Сценарию 3, в котором всплывающий элемент определен в разметке, где он содержит элементы управления label, select, input. В JavaSctipt, однако, он прослушивает события изменения этих элеменов и применяет полученные значения к элементам вывода данных на полотне приложения:
var page = WinJS.UI.Pages.define("/html/change-settings.html", {
ready: function (element, options) {
// ...
document.getElementById("textColor").addEventListener("change", changeColor, false);
document.getElementById("textSize").addEventListener("change", changeSize, false);
}
});
// Изменение цвета текста
function changeColor() {
document.getElementById("outputText").style.color =
document.getElementById("textColor").value;
// Изменение размера текста
function changeSize() {
document.getElementById("outputText").style.fontSize =
document.getElementById("textSize").value + "pt";
Если бы у этого всплывающего элемента была бы кнопка Применить (Apply) вместо его немедленной реакции на изменения, ее обработчик click получал бы текущее выделение и значение из ползунка и использовал бы их для того, чтобы выполнить действия, аналогичные тем, что производятся в функциях changeColor и changeSize.
И, наконец, в Сценарии 4 мы видим всплывающий элемент с заголовком, который является просто элементом в разметке, выводящим крупный текст. Сам по себе всплывающий элемент не имеет выделенного описания для заголовков:
<div id="moreInfoFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{More info flyout}">
<div class="win-type-x-large">Lorem Ipsum</div>
<div>
Lorem Ipsum is text used as a placeholder by designers...
</div>
</div>
Смысл этого последнего примера заключается в том, чтобы показать, что в отличие от традиционных диалоговых окон настольных приложения, всплывающие элементы не часто нуждаются в заголовках, так как у них уже есть контекст внутри приложения. Диалоговые окна в настольных приложениях нуждаются в заголовках, так как они могут относиться к разным приложениям.
Элемент управления WinJS.UI.Menu отличает от более общего элемента WinJS.UI.Flyout то, что меню ожидает, что все его дочерние элементы будут объектами типа WinJS.UI.MenuCommand , так же, как стандартный макет панели приложения ожидает объекты AppBarCommand (и его экземпляр не будет создан, если вы объявите у него какие-нибудь другие дочерние элементы). На самом деле, у меню есть общие характеристики и с панелью приложения, и со всплывающим элементом:
show и hide .getCommandById, showCommands, hideCommands , и showOnlyCommands , вместе со свойством commands , что означает, что вы можете использовать те же подходы для управления командами меню, которые обсуждались в подразделе "Отображение, скрытие, включение и обновление команд" в разделе, посвященном панели приложения, в том числе – задание команд меню используя JSON-массив вместо описания отдельных элементов.beforeshow, aftershow, beforehide , и afterhide .anchor, alignment , и placement .Меню поддерживает два стиля, влияющих на его отображение – win-menu и win-command – который можно использовать для создания более конкретных селекторов, как мы видели, для всего меню или для отдельных текстовых команд.
Объекты MenuCommand так же очень похожи на объекты AppBarCommand. У них много одинаковых свойств: id, label, type ('button', 'toggle', 'flyout' , и 'separator' ), disabled, extraClass, flyout, hidden, onclick , и selected . У команд меню нет значков, разделов и всплывающих подсказок, но из type вы можете видеть, что элементы меню могут быть кнопками (включая полностью текстовые элементы), элементами с возможностью установки флагов, разделителями и, кроме того, другими всплывающими элементами. В последнем случае, вторичное меню заменит первое вместо того, чтобы отображаться рядом с ним, а чтобы быть полностью честным, я должен все же видеть вторичное меню, используемое в реальном приложении. Тем не менее, такая возможность поддерживается.
Мы уже видели, как использовать всплывающие меню с помощью команд панели приложения, что показано в Сценарии 6 примера, посвященного всплывающему элементу управления HTML (посмотрите раздел "Командные меню" выше). Другая типичная сфера использования – реализация того, что выглядит как выпадающее меню заголовочного элемента, что показано в Сценарии 5. Здесь (html/header-menu.html), показан стандартный дизайн помещения символа перевернутой угловой скобки (#xe099) в конце заголовка:
<header aria-label="Header content" role="banner"> <button class="win-backbutton" aria-label="Back"></button> <div class="titlearea win-type-ellipsis"> <button class="titlecontainer"> <h1> <span class="pagetitle">Music</span> <span class="chevron win-type-x-large" > #xe099 </span> </h1> </button> </div> </header>
Обратите внимание на то, что весь заголовок помещен в элемент управления button (кнопка), ее обработчик click может отображать меню с помощью метода show:
document.querySelector(".titlearea").addEventListener("click", showHeaderMenu, false);
function showHeaderMenu() {
var title = document.querySelector("header .titlearea");
var menu = document.getElementById("headerMenu").winControl;
menu.anchor = title; menu.placement = "bottom";
menu.alignment = "left";
menu.show();
}
Всплывающий элемент (определенный как headerMenu в html/header-menu.html) появляется, когда вы щелкаете в любом месте заголовка (а не только на угловой скобке, которая является обычным символом в тексте заголовка):
Отдельные команды меню – это простые элементы button, поэтому вы можете присоединить к ним обработчики click если вам это нужно. Как и в случае с панелью приложения, лучше всего использовать метод элемента управления меню getCommandById для обнаружения этих элементов, так как это гораздо эффективнее, чем document.getElementById (что, к сожалению, используется в примере из SDK).
Для того чтобы увидеть в действии вторичное меню, попытайтесь добавить следующий элемент secondaryMenu в html/header-menu.thml перед элементом headerMenu и добавить элемент button в headerMenu, свойство flyout которого ссылается на secondaryMenu:
<div id="secondaryMenu" data-win-control="WinJS.UI.Menu">
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'command1', label:'Command 1'}"></button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'command2', label:'Command 2'}"></button>
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'command3', label:'Command 3'}"></button>
</div>
<div id="headerMenu" data-win-control="WinJS.UI.Menu">
<!-- ... -->
<button data-win-control="WinJS.UI.MenuCommand"
data-win-options="{id:'showFlyout', label:'Show secondary menu',
type:'flyout', flyout:'secondaryMenu'}">
</button>
</div>
Кроме того, перейдите в css/header-menu.css и установите стиль width элемента #headerMeny в значение 200px. С этими изменениями первое меню будет выглядеть так, как показано ниже, изменение цвета заголовка – результат действия эффекта зависания над ним указателя мыши:
Когда вы выберете пункт Show secondary meny (Показать вторичное меню), первое меню будет закрыто и откроется меню второго уровня:
Другой пример использования всплывающих элементов в заголовке можно найти в примере "Адаптивный макет с использованием CSS" (http://code.msdn.microsoft.com/windowsapps/Adaptive-layout-with-sample-062e7fe2), который мы видели в лекции 6 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript". Он реализован так же, как показано выше, с добавлением механизмов, которые изменяют содержимое страницы в ответ на выделение.
Помимо всплывающих меню, которые мы уже видели, существуют и контекстные меню, описанные в материале "Руководство и контрольный список для контекстных меню" (http://msdn.microsoft.com/library/windows/apps/hh465308.aspx). Они используются для команд, которые напрямую связаны с выделением разного рода, таким, как команды буфера обмена для текста, они вызываются щелчком правой кнопки мыши по элементу, прикосновением или клавишей вызова контекстного меню на клавиатуре. Текстовые элементы управления и гиперссылки обеспечивают данную возможность по умолчанию. Контекстные меню так же хорошо подходят для предоставления команд для объектов, которые не могут быть выделены (таких, как части беседы в программе мгновенного обмена сообщениями), команды панели приложения не могут быть контекстно чувствительными к подобным элементам. Они так же рекомендованы для совершения действий, которые нельзя выполнить путем непосредственного взаимодействия с объектом. Однако, не используйте их для фона страницы – именно для этого существует панель приложения, она будет появляться автоматически при щелчке правой кнопкой мыши или соответствующем жесте.
Совет. Если вы обрабатываете событие нажатий правой кнопки мыши на элемент, убедитесь в том, что для этого элемента отключено стандартное поведение, которое подразумевает отображение панели приложения. Таким образом, используйте событие щелчка правой кнопки мыши разумно, так как пользователи привыкли пользоваться правой кнопкой для вызова панели приложения. Обратите так же внимание на то, что вы можете программно открыть панель приложения, используя ее метод show.
Пример "Контекстное меню" (http://code.msdn.microsoft.com/windowsapps/Context-menu-sample-40840351) предоставляет нам некоторый контекст – я знаю, что это неудачная игра слов! В любом случае, вам нужно лишь прослушивать событие HTML contextmenu для соответствующего элемента. Вам не нужно заботиться особым образом об обработке касаний или о событиях клавиатуры. Сценарий 1 примера, например, имеет невыделяемый элемент attachment, для которого прослушивается вышеупомянутое событие (html/scenario1.html):
document.getElementById("attachment").addEventListener("contextmenu", attachmentHandler, false);
В обработчике события, вы создаете объект ) (это объект из WinRT, не из WinJS!), заполняет его объектами (они содержат подпись элемента и обработчик события) или объектами ), и затем вызывает метод меню showAsync (js/scenario1.js):
function attachmentHandler(e) {
var menu = new Windows.UI.Popups.PopupMenu();
menu.commands.append(new Windows.UI.Popups.UICommand("Open with", onOpenWith));
menu.commands.append(new Windows.UI.Popups.UICommand("Save attachment",
onSaveAttachment));
menu.showAsync({ x: e.clientX, y: e.clientY }).done(function (invokedCommand) {
if (invokedCommand === null) {
// Команда - это null если ни одна команда не была активирована.
}
});
Обратите внимание на то, что результат
Объект PopupMenu так же поддерживает метод, который называется showForSelectionAsync, первым аргументом которого является объект ), который описывает область выделения. Опять же, меню размещается выше прямоугольника выделения с выравниванием по центру. Это показано в Сценарии 2 примера в js/scenario2.js:
//В обработчике contextmenu
menu.showForSelectionAsync(getSelectionRect()).then(function (invokedCommand) {
// ...
function getSelectionRect() {
var selectionRect = document.selection.createRange().getBoundingClientRect();
var rect = {
x: getClientCoordinates(selectionRect.left),
y: getClientCoordinates(selectionRect.top),
width: getClientCoordinates(selectionRect.width),
height: getClientCoordinates(selectionRect.height)
};
return rect;
};
Сценарий так же показывает, что вы можете использовать обработчик события contextmenu с текстом для переопределения команд по умолчанию, которые предоставляют соответствующие элементы управления.
Последнее замечание, касающееся контекстных меню: так как они создаются с помощью API WinRT, а не с помощью элементов управления WinJS, меню не существуют в DOM, они не известны DOM, что объясняет использование других конструкций WinRT – как point (Точка) и Rect (Прямоугольник). Подобное утверждение так же справедливо для окон сообщений, которые станут последней темой этой лекции.
Последняя тема этой лекции, касающаяся командного интерфейса, посвящена диалоговым окнам сообщений. Как и контекстные меню, эти всплывающие элементы относятся к WinRT а не к WinJS, для работы с ними используется API Windows.UI.MessageDialog (http://msdn.microsoft.com/library/windows/apps/windows.ui.popups.messagedialog.aspx). Это, опять же, означает, что окна сообщений просто появляются поверх текущей страницы и не присутствуют в DOM. Диалоговые окна автоматически затеняют текущую страницу приложения и блокируют события ввода от приложения до тех пор, пока пользователь не отреагирует на окно сообщения.
В материале "Руководство и контрольный список для окон сообщений" (http://msdn.microsoft.com/library/windows/apps/hh738363.aspx) показаны варианты их использования:
Интерфейс диалоговых окон очень прост. Вы создаете объект диалогового окна сообщения командой new Windows.UI.Popups.MessageDialog. Конструктор принимает в качесве обязательного параметра строку с содержимым сообщения и с необязательным вторым строковым параметром, содержащим заголовок окна. Окно сообщения имеет свойства content и title, которые вы можете использовать независимо друг от друга. В любом случае, строковые параметры поддерживают лишь обычный текст.
После этого вы конфигурируете диалоговое окно с помощью его свойств commands, options, defaultCommandIndex (команда, связанная с клавишей Enter) и cancelCommandIndex (команда, связанная с клавишей Escape).
Значения параметра options берутся из перечисления Windows.UI.Popups.MessageDialogOptions, которое содержит лишь два члена: none (значение по умолчанию, для случаев, когда особое поведение не требуется), и acceptUserInputAfterDelay (что приводит к тому, что окно сообщения некоторое время не воспринимает пользовательский ввод для того, чтобы предотвратить кликджекинг (clickjaking); это существенно, преимущественно, для интернет-браузеров, которые загружают произвольное веб-содержимое и практически не требуется большинству приложений).
Свойство command содержит до трех объектов типа тех же самых, которые используются в контекстных меню. Каждая из команд содержит свойства id, label и invoked, к которому вы подключаете обработчик для команды. Обратите внимание на то, работа свойств defaultCommandIndex и cancelCommandIndex основана на индексах массива commands, а не на id этих команд. Кроме того, если вы не добавите собственные команды, диалоговое окно сообщения отобразится с единственной командой по умолчанию – Закрыть (Close).
И, наконец, когда диалоговое окно настроено, вы отображаете его, вызывая его метод showAsync. Как и в случае с контекстным меню, в результате выбранный пользователем объект UICommand, который передается обработчику завершения, который вы предоставили для метода done promise-объекта. Обычно вам не нужно получать этот результат, так как выбранная команда имеет связанный с ней обработчик invoked, где вы и выполняете действия, соответствующие команде.
Примечание. Если в то время, когда активно окно сообщения, активируются чудо-кнопки Поиск, Общий доступ, Устройства или Параметры, или если приложение активируется для обслуживания контракта, диалоговое окно сообщения будет закрыто и без выбора какой-либо команды в нем. Будет осуществлен вызов обработчика завершения для showAsync, с результатом, установленным на команду по умолчанию. Знайте об этом, если вы используете обработчик завершения для обработки подобных команд.
Пример "Диалоговое окно сообщения" (http://code.msdn.microsoft.com/windowsapps/Message-dialog-sample-00c928f5) – это один из самых простых примеров во всем SDK! Он показывает разные варианты использования API окон сообщений. Сценарий 1 показывает окно сообщения с заголовком и двумя командными кнопками, вторая командная кнопка (с индексом 1) установлена как кнопка по умолчанию. Вот как это выглядит:
Сценарий 2 показывает вывод команды по умолчанию – Закрыть (Close) с сообщением и без заголовка:
Сценарий 3 похож на Сценарий 1, но использует обработчик завершения метода showAsync().done для обработки команды, выбранной пользователем. Вы можете использовать этот пример для того, чтобы увидеть эффект активации чудо-кнопок при открытом диалоговом окне.
И, наконец, Сценарий 4 делает командой по умолчанию первую команду и отмечает вторую команду в качестве команды отмены, то есть, той команды, которая закрывает окно либо при ее выборе, либо при нажатии на клавишу клавиатуры Escape.
И это действительно все, что относится к окнам сообщений!
Для того, чтобы завершить эту лекцию и свести воедино большую часть того, о чем мы говорили, внесем некоторые изменения в "Here My Am!". В последний раз мы работали над этой программой, улучшая обработку различных ошибок. В ее теперешнем состоянии программа ведет себя не очень хорошо. А именно, есть сложности в следующих областях:
Приложение "Here My Am!" (7) для этой лекции учитывает эти вопросы. Во-первых, я добавил изображение, которое должно выводиться при ошибке, в html/map.html (изображение находится по адресу html/maperror.png), таким образом, если не удается загрузить скрипт карт Bing, вместо карты будет выведено изображение (в этом случае стиль отображения none будет удален):
<img id="errorImage" style="display: none; width: 100%; height: 100%;" src="maperror.png" />
Кроме того, я добавил к изображению обработчик события click, который перезагружает содержимое iframe с document.location.reload(true). Когда это сделано, я могу убрать исключения, которые возникли ранее, когда карта не могла быть создана, предотвращая аварийное завершение приложения. Вот как выглядит ситуация, когда карта не может быть создана:
Для того, чтобы протестировать такой вариант развития событий, вам нужно отключиться от Интернет, деинсталлировать приложение (для очистки кэшированных скриптов карты; в противном случае карта, все равно, будет загружаться!) и снова запустить приложение. Ошибка должна произойти в начале метода init в файле html/map.html, что приведет к появлению изображения путем удаления стиля по умолчанию display: none и подключения обработчика click . Затем снова подключитесь к Интернету и нажмите на изображение. Карта должна перезагрузиться, но если проблема с соединением сохранилась, снова появится изображение, сигнализирующее об ошибке.
Вторая проблема – добавление возможности обновлять наше местоположение – просто решаема с помощью панели приложения. Я добавил такой элемент управления на страницу default.html с одной командой:
<div id="appbar" data-win-control="WinJS.UI.AppBar" data-win-options="">
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id:'cmdRefreshLocation', label:'Refresh location',
icon:'globe', section:'global', tooltip:'Refresh your location'}">
</button>
</div>
Эта команда подключена внутри pages/home/home.js в методе ready элемента управления страницы:
var appbar = document.getElementById("appbar").winControl;
appbar.getCommandById("cmdRefreshLocation").addEventListener("click", this.tryRefresh.bind(this));
Здесь обработчик tryRefresh, так же в элементе управления страницы, скрывает панель приложения и вызывает другой новый метод, refreshPosition, куда я поместил код, который получает сведения о местоположении и обновляет карту:
tryRefresh: function () {
//Скрыть панель приложения и попробовать снова
var appbar = document.getElementById("appbar").winControl.hide();
this.refreshPosition();
},
Мне так же понадобилось настроить функцию pinLocation в html/map.html. Без команды обновления местоположения, эта функция вызывалась лишь однажды, при запуске приложения. Так как теперь она может быть вызвана много раз, нам нужно удалить любые существующие метки на карте, прежде чем добавлять метку для нового местоположения. Это делается путем вызова map.entities.pop перед уже существующим вызовом map.entities.push, который отмечает новое местоположение.
Панель приложения теперь выглядит так, как показано ниже, и мы можем, при необходимости, обновить местоположение. (Если вы запускаете программу не на мобильном устройстве, попытайтесь перетащить первую метку на новое место и затем запустить обновление для того, чтобы увидеть, как метка вернулась на прежнее место).
Что касается третьей проблемы – дать пользователю знать о том, что приложение пытается получить данные о местоположении – мы можем показать маленькое всплывающее сообщение сразу же после попытки вызова getGeopositionAsync из WinRT. Всплывающий элемент, определенный в pages/home/home.html (в нашем элементе управления страницы) центрирован вдоль нижней части области карты:
<div id="retryFlyout" data-win-control="WinJS.UI.Flyout" aria-label="{Trying geolocation}"
data-win-options="{anchor: 'map', placement: 'bottom', alignment: 'center'}">
<div class="win-type-large">Attempting to obtain geolocation...</div>
</div>
Функция refreshFunction которую мы только что добавили в pages/home/home.js является отличным местом для отображения всплывающего окна сразу перед вызовом getGeopositionAsync:
refreshPosition: function () {
document.getElementById("retryFlyout").winControl.show();
var gl = new Windows.Devices.Geolocation.Geolocator();
gl.getGeopositionAsync().done(function (position) {
//...
//Всегда скрывать всплывающий элемент document.getElementById("retryFlyout").winControl.hide();
//...
}, function (error) {
//...
//Всегда скрывать всплывающий элемент
document.getElementById("retryFlyout").winControl.hide();
});
},
Обратите внимание на то, что мы скрываем всплывающий элемент внутри обработчиков ошибки и завершения, поэтому сообщение остается видимым во время проведения асинхронной операции. Если мы разместим одну команду, скрывающую всплывающий элемент, за пределами этих обработчиков, сообщение будет отображаться лишь очень короткое время перед закрытием, а это – не то, что нам нужно. Когда мы это написали, у пользователя будет достаточно времени для того, чтобы увидеть сообщение вдоль нижней части карты (если только он не решит закрыть его):
Последняя задача заключается в том, чтобы сообщить пользователю о том, что при попытке получить сведения о местоположении произошла ошибка. Мы можем сделать это с помощью еще одного всплывающего элемента с кнопкой Retry (Повторить), или во встроенном сообщении, как показано ниже. Нам не следует пользоваться в этом случае диалоговым окном сообщения, так как сообщение появляется в ответ на действие пользователя, которым он запускает процесс обновления данных. Окно сообщения допустимо использовать при запуске программы, но с помощью встроенного сообщения, скомбинированного со всплывающим элементом мы уже выполнили все, что нужно.
Для встроенного сообщения, я добавил плавающий элемент div, который позиционирован примерно на трети расстояния от верхнего края карты. Он определен в defined/home/home.html, как показано, в качестве элемента одного уровня с iframe:
<div id="locationSection" class="subsection" aria-label="Location section"> <h2 class="group-title" role="heading">Location</h2> <iframe id="map" class="graphic" src="ms-appx-web:///html/map.html" aria-label="Map"></iframe> <div id="floatingError" class="win-type-x-large">Unable to obtain geolocation;<br /> use the app bar to try again.</div> </div>
Стиль для правила #floatingError в pages/home/home.css задает внешний вид и расположение элемента:
#floatingError {
display: none; float: left;
-ms-grid-column: 1;
-ms-grid-row: 2;
-ms-grid-row-align: start;
width: 100%;
text-align: center;
background-color: rgba(128, 0, 0, 0.5);
margin-top: 20%;
}
Так как этот элемент размещен в той же ячейке сетки, что и карта со стилем float, это выглядит как хорошо выглядящее наложение:
Это сообщение появится, если пользователь отклонил запрос на доступ приложения к информации о местоположении, который ему задавали при запуске программы, или позволил доступ, но позже, используя чудо-кнопку Параметры, запретил пользоваться этой возможностью. Вы можете использовать эти варианты для тестирования появления сообщения. Возможно так же, если вы запускаете приложение в первый раз и при этом нет соединения с Интернетом, чтобы это сообщение выводилось поверх изображения, сообщающего об ошибке загрузки карты. Именно поэтому я расположил сообщение об ошибке сервиса получения местоположения ближе к верхней части, в итоге, оно не перекрывает сообщение об ошибке загрузке карты. Но, если вы успешно запустили приложение и затем связь оборвалась, карта должна создаваться, так как скрипты карт Bing кэшируются.
С параметром display: none в CSS, сообщение об ошибке первоначально скрыто, как это и должно быть. Если мы попадаем в обработчик ошибки для getGeolocationAsync , мы устанавливаем style.display в значение block , что отображает элемент:
document.getElementById("floatingError").style.display = "block";
Мы снова скрываем сообщение внутри функции tryRefresh, которая вызывается посредством команды на панели приложения, таким образом, сообщение остается скрытым, если ошибка повторяется:
tryRefresh: function () {
document.getElementById("floatingError").style.display = "none";
//...
},
Так же мы можем добавить окно сообщения, если мы обнаружим, что сетевое соединение потеряно, и, таким образом, не можем обновить местоположение. Как мы увидим в лекции 3 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", это можно сделать с помощью события ). В подобном случае вполне можно воспользоваться окном сообщения, так как происходящее не связано с непосредственными действиями пользователя.
Кроме того, стоит отметить, что если бы мы использовали элемент управления из Bing Maps SDK в данном приложении, скрипт, который мы загружаем из удаленного источника, хранился бы в пакете нашего приложения, таким образом, устраняя причину первой ошибки. Мы внесем это изменение в следующую версию программы
Windows.UI.Popups.PopupMenu ) так же можно использовать для команд, связанных с содержимым. WinJS.UI.Flyout используется для подтверждений и других вопросов, которые отображаются в результате действий пользователя. Они так же могут просто выводить сообщения, собирать дополнительную информацию или предоставлять элементы управления для изменения настроек некоторых частей страниц. Неактивные всплывающие элементы могут быть скрыты, то есть, щелчок или прикосновение за пределами элемента или нажатие на клавишу Escape приводит к их скрытию, что равносильно нажатию на кнопку Отмена (Cancel) в ответ на вопрос, который может быть задан в подобном элементе.Windows.UI.Popups.MessageDialog ) используются для задавания вопросов, на которые пользователь должен ответить, или вывода информации, с которой он обязательно должен ознакомиться, прежде чем приложение сможет продолжить работу. Окно сообщения блокирует приложение. Окна сообщений лучше всего использовать для вывода сведений об ошибках или условиях, которые воздействуют на приложение в целом. Сообщения об ошибках, которые относятся к содержимому странице, следует показывать на странице.showCommand , hideCommand и showOnlyCommand , вместе со свойством команд extraClass , упрощают описание панели приложения в одном месте приложения и выборочный показ конкретных наборов команд с использованием querySelectorAll с классом, который представляет данный набор.WinJS.UI.Menu .Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.