Начала Metro-программирования списки и поиск файлов Платформа Metro, предназначенная для выполнения нового класса приложений, что доступны в Windows 8, предоставляет разработчикам богатейшие средства для создания пользовательского интерфейса и работы с данными. Они позволяют реализовать выполнение многих операций написанием буквально одного выражения, в то время как традиционным Windows-разработчикам приходилось писать для этого весьма объемный код. Тому есть простое объяснение: операционная система Windows 8 предназначена, в том числе, и для планшетных компьютеров, а их аппаратные ресурсы по сравнению с компьютерами традиционными сильно ограничены, в том числе, ограничен и объём доступной памяти. Поэтому приложения для планшетов должны быть, по возможности, как можно более компактными, а достичь этого можно, только перенеся часть задач, которые они выполняют, на "плечи" самой операционной системы. Ведь операционная система для того и существует, чтобы выполнять по запросу приложений всевозможные типовые задачи. Но хватит "лирики". Перейдём к прикладным вопросам. В этой статье мы изучим следующие инструменты платформы Metro: Механизм прав приложений, описывающий привилегии на доступ к файловой системе и аппаратным средствам, которыми должны обладать приложения для успешного функционирования. Средства для программного доступа к системным библиотекам Windows. Стандартный экран выбора папки, предоставляющий пользователю возможность указать папку, содержимое которой следует обработать приложению. Этот экран аналогичен привычному нам стандартному диалоговому окну выбора папки. Средства для получения списка файлов и папок, хранящихся в какой-либо папке файловой системы компьютера. В том числе платформа Metro может сформировать по нашему заказу список файлов, сгруппированных по какому-либо критерию: типу, году, месяцу, рейтингу, исполнителю композиции, альбому и др. Средства для получения свойств файла: его имени, пути, размера, даты последнего изменения и др. Средства для получения миниатюры файла. Списки Metro - мощнейшее средство для вывода перечня каких-либо позиций (списка файлов, электронных писем, товаров в интернет-магазине и др.). Причём формат, в котором должны выводиться эти позиции, мы можем задавать произвольно, либо декларативно - в HTML-коде, описывающем интерфейс, либо программно - в коде логики. Панель вывода Metro - особый элемент интерфейса, автоматически растягивающий своё содержимое, чтобы оно заняло всё пространство данного элемента. А чтобы испытать всё это функциональное богатство в действии, мы создадим простое приложение для просмотра графики. Оно будет: Изначально, сразу после запуска, выводить список файлов, хранящихся в библиотеке "Изображения". Давать пользователю возможность вывести содержимое произвольной папки. Выводить изображение, хранящееся в выбранном пользователем файле, на экран. Программа предстоит насыщенная, так что не расслабляемся! Внимание! Перед чтением следует ознакомиться с обоими статьями из цикла "Начала Metro-программирования", опубликованными на нашем сайте ранее. Описанные в них приёмы программирования будут активно здесь использоваться. 2. Начинаем создавать тестовое приложение Запустим Visual Studio и создадим новый проект по имени ImageViewer. Сразу же откроем файл default.html, где хранится описывающий интерфейс приложения HTML-код, и вставим в парный тег вот что: Код:
 
 
Этот код создаст, прежде всего, четыре блока (блочных контейнера, тега
), которые мы потом расположим на экране с применением сеточной разметки в направлении сверху вниз: первый - divFolderPath - выведет путь к папке, указанной пользователем; второй - divViewer - выведет выбранное пользователем изображение; третий - divFileName - выведет имя выбранного пользователем файла; четвёртый - divList - перечислит все файлы, хранязиеся в указанной пользователем папке. Кроме того, будет создана панель инструментов, которая расположится в верху экрана и включит в свой состав единственную кнопку Выбрать папку. При нажатии на неё на экране появится стандартный экран вывода папки (разговор о нём пойдёт далее). Откроем файл default.css, где описывается оформление приложения, и создадим в нём несколько стилей, перечисленных далее. Код: body {   display: -ms-grid;   -ms-grid-columns: 1fr;   -ms-grid-rows: 30px 1fr 40px 170px; } Здесь мы создаём сеточную разметку из четырёх строк, которые вместят все созданные нами блоки. Код: #divFolderPath {   font-size: 12pt;   text-align: center; } Для блока divFolderPath, где будет выводиться путь к указанной папке, зададим размер шрифта, равный 12 пунктам, и выравнивание по центру. Код: #divViewer {   -ms-grid-row: 2;   -ms-grid-column-align: center;   -ms-grid-row-align: center; } Для блока divViewer указываем, в том числе, выравнивание по центру. После этого выбранное пользователем изображение будет выведено в середине данного блока. Код: #divFileName {   -ms-grid-row: 3;   font-size: 16pt;   text-align: center; } Для блока divFileName также задаём размер шрифта в 16 пунктов и выравнивание по центру. Код: #divList {   -ms-grid-row: 4;   height: 170px; } Для блока divList указываем высоту в 170 пунктов - этого хватит, чтобы вместить список с миниатюрами изображений (как их получить, мы узнаем потом). Сохраним все исправленные файлы... 3. Права приложения и их указание ...И рассмотрим один очень важный вопрос, являющийся ключевым в Metro-программировании и реализованной в данной платформе системе безопасности. Дело в том, что Metro изначально не позволяет приложениям получать доступ к персональным данным пользователя, в частности, содержимому системных библиотек. То есть, если наше приложение попытается программно обратиться к содержимому любой библиотеки, попытка не увенчается успехом, и либо выполнение приложения будет прервано по ошибке, либо оно просто ничего не выведет. На заметку Кстати, то же самое произойдёт, если приложение попытается прочитать содержимое подключаемого носителя (например, флэш-диска), получить данные со встроенных датчиков (GPS, электронного компаса, акселерометра и др.) или изображение с фото- или видеокамеры. Всё это сделано для того, чтобы не дать вредоносным приложениям похитить персональные данные пользователя или скомпрометировать его иным способом. Хотя все Metro-приложения перед публикацией в магазине Windows Store проходят тщательную проверку, лишний раз обезопаситься не помешает. Чтобы всё-таки получить доступ к нужным библиотекам и устройствам, приложение должно обладать определёнными привилегиями, носящими название прав приложения. Так, если наше приложение собирается программно работать с содержимым библиотеки "Изображения", оно должно иметь право на доступ к этой библиотеке. Права приложения задаются на этапе его разработки прямо в среде Visual Studio. Найдём в списке панели Solution Explorer файл package.appxmanifest и дважды щёлкнем на нём. В главном окне появится ещё одно окно документа, в котором наглядно, в графическом виде, будут представлены все параметры открытого нами проекта и, в частности, права приложения. В этом окне документа будет присутствовать набор вкладок, каждая из которых служит для указания определённой группы параметров. Права приложения задаются на вкладке Capabilities, поэтому сразу переключимся на неё. Здесь мы увидим список Capabilities, содержащий набор пунктов-флажков. Эти пункты представляют все доступные в платформе Metro права приложения; установленный флажок означает, что приложение обладает соответствующим правом, сброшенный — отменяет данное право. На данном этапе нам будут интересны следующие пункты списка Capabilities и соответствующие им права: Document Library — доступ к библиотеке "Документы", к файлам тех типов, которые указаны в параметрах приложения; Music Library — доступ к библиотеке "Музыка"; Pictures Library — доступ к библиотеке "Изображения"; Private Networks (Client & Server) — доступ к домашней сетевой группе; Videos Library — доступ к библиотеке "Видео". Чтобы дать приложению какое-либо их этих прав, достаточно установить нужный флажок. Так, нам, чтобы дать приложению просмотровщика графики права на доступ к библиотеке "Изображения" (именно её содержимое приложение будет выводить изначально), следует установить флажок Pictures Library. Если приложение имеет права на доступ к библиотекам "Музыка", "Изображения" или "Видео", то доступ к домашней сетевой группе предоставляется ему автоматически, и специально давать приложению такое право не требуется. Если приложение должно иметь доступ к библиотеке "Документы", то, помимо установки флажка Document Library, нам обязательно следует указать типы файлов, которые оно сможет открывать и создавать. При этом доступ к файлам неуказанных типов будет невозможен (библиотек "Музыка", "Изображения" и "Видео" это не касается; если приложение имеет к ним доступ, оно может обрабатывать хранящиеся там файлы любых типов). Задать сведения о доступных типах файлов можно на вкладке Declarations. Переключимся на неё. Здесь мы сразу увидим список Supported Declarations, в котором перечисляются указанные нами типы файлов. Изначально он пуст. Чтобы задать для приложения поддерживаемый тип файлов, мы выберем в раскрывающемся списке Available Declarations пункт File Type Associations и нажмём расположенную правее этого списка кнопку Add. В спискеSupported Declarations появится пункт File Type Associations, представляющий вновь созданный тип. Выберем его. В поле ввода Name, расположенном в группе элементов управления Properties, введём имя созданного типа файлов. Это имя должно содержать только строчные (маленькие) латинские буквы, цифры и знаки подчёркивания. Например, для текстовых файлов мы можем задать в качестве имени типа строку textfiles. Ниже расположена группа элементов управления Supported File Types, в которой, в свою очередь, находится набор групп, описывающих конкретные расширения файлов, что относятся к данному типу. Расширение файла вводится в поле ввода File Type, обязательно с начальной точкой, например, .txt. Чтобы добавить в этот набор новую группу — новое расширение файлов, следует нажать кнопку Add New. Чтобы удалить ненужную группу и, соответственно, расширение файлов, достаточно нажать присутствующую в её заголовке кнопку Remove. Мы можем добавить в список Supported Declarations сколько угодно типов файлов и указать для каждого из них сколько угодно расширений. Так, мы можем дать приложению поддержку ещё одного типа файлов — системных (system) — с расширениями ini и log. Закончив задание прав приложения, следует закрыть окно документа, в котором выводятся его параметры, и сохранить его содержимое. Внимание! Если приложение использует стандартные экраны открытия и сохранения файла, его права на доступ к библиотекам указывать необязательно. Платформа Metro в этом случае будет исходить из того, что пользователь сам предоставил доступ к выбранному им в соответствующем экране файлу. Кстати, это же касается экрана выбора папки, о котором скоро пойдёт речь. 4. Получение доступа к библиотекам Теперь, указав для приложения соответствующие права, мы можем без проблем получить доступ к системным библиотекам. Для этого достаточно воспользоваться перечислением Windows.Storage.KnownFolders, содержащим следующие элементы: documentsLibrary — библиотека "Документы"; homeGroup — домашняя сетевая группа; musicLibrary — библиотека "Музыка"; picturesLibrary — библиотека "Изображения"; videosLibrary — библиотека "Видео". Каждый из этих элементов хранит основную папку соответствующей библиотеки. Она, как и любая другая папка файловой системы, представляется в платформе Metro в виде экземпляра объекта Windows.Storage.StorageFolder. Код: var libImages = Windows.Storage.KnownFolders.picturesLibrary; Получаем основную папку библиотеки "Изображения". 5. Использование стандартного экрана выбора папки Если же наше приложение должно предоставлять пользователю возможность выбора произвольной папки, мы реализуем это с помощью стандартного экрана выбора папки. Он аналогичен привычному нам диалоговому окну выбора папки и содержит средства для указания нужной папки, список, перечисляющий все хранящиеся в открытой в данный момент папке файлы, и кнопки Выбрать папку (Pick Folder) и Отмена (Cancel). Экран выбора папки представляется объектом Window.Storage.Pickers.FolderPicker. Мы должны создать экземпляр этого объекта и присвоить его какой-либо переменной. Код: var oFP = new Windows.Storage.Pickers.FolderPicker(); Объект Window.Storage.Pickers.FolderPicker поддерживает уже знакомые нам свойства suggestedStartLocation, fileTypeFilter, viewMode и commitButtonText. С их помощью мы зададим параметры экрана. Код: oFP.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary; oFP.fileTypeFilter.replaceAll([".gif", ".jpg", ".png"]); oFP.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail; Изначально открываем в экране выбора папки библиотеку "Изображения", перечисляем в списке файлы с расширениями gif, jpg и png (то есть графические файлы) и указываем режим вывода файлов в виде комбинаций их миниатюр и имён. Для вывода экрана и получения выбранной в нём папки мы вызовем у него метод pickSingleFolderAsync. Этот метод не принимает параметров и возвращает в качестве результата обязательство, для которого мы с помощью метода then зададим функцию. Данная функция будет выполнена после того, как пользователь выберет папку, и в качестве единственного параметра получит: либо экземпляр объекта Windows.Storage.StorageFolder, представляющий выбранную пользователем папку, если пользователь таки её выбрал; либо значение null, если пользователь отказался от выбора папки. Код: oFP.pickSingleFolderAsync().then(function(folder) {   if (folder) {     //Папка была выбрана   } else {     //Папка не была выбрана   } }); 6. Получение списка файлов и папок Платформа Metro позволяет нам получить как простой список файлов и папок, хранящихся в указанной нами папке, так и более сложный, отсортированный или сгруппированный по заданному нами же критерию. 6.1. Получение простого списка файлов и папок Проще всего получить простой список содержимого папки, без сортировки и группировки. Для этого служат два метода объекта Windows.Storage.StorageFolder, который, как мы уже знаем, представляет папку: getFilesAsync — получение списка всех файлов; getFoldersAsync — получение списка всех папок. Оба метода в данном случае вызываются без параметров и возвращают в качестве результата обязательство. Функция, которую мы для него укажем в вызове метода then, выполнится после завершения формирования списка файлов или папок и получит в качестве результата коллекцию, которая и представляет данный список. Это та же самая коллекция, что получается в результате вызова метода pickMultipleFilesAsync (см. описание работы с экраном открытия файла в предыдущей статье из цикла "Начала Metro-программирования"); она поддерживает свойство size и способ доступа к элементам, характерный для массивов. Код: var arrFiles = []; libImages.getFilesAsync().then(function(files) {   if (files.size > 0) {     for (var i = 0; i < files.size; i++) {       arrFiles.push(files[i]);     }   } }); Получаем список всех файлов, что хранятся в библиотеке Изображения, и помещаем эти файлы в массив arrFiles для дальнейшей обработки. Метод push массива принимает в качестве единственного параметра значение и добавляет его в массив, у которого был вызван. Данный метод предоставляет нам простейший и быстрейший способ добавить в массив новый элемент. 6.2. Получение списка файлов с сортировкой Получить список файлов, отсортированный по заданному критерию, немного сложнее. Мы вызовем знакомый нам метод getFilesAsync, передав ему в качестве единственного параметра один из элементов перечисления Windows.Storage.Search.CommonFileQuery, задающий критерий сортировки: defaultQuery — сортировка отсутствует (поведение по умолчанию); orderByName — сортировка по наименованию файла (это может быть заголовок, записанный в метаданных, или, если он отсутствует, имя файла); orderByTitle — сортировка по заголовку файла, записанному в его метаданных (это может быть заголовок документа, название музыкальной композиции или фильма, тема почтового сообщения и др.). Поддерживается только для файлов, хранящихся в библиотеке или домашней сетевой группе; orderByMusicProperties — сортировка по музыкальным метаданным (названию композиции и альбома, имени исполнителя и др.). Поддерживается только для файлов, хранящихся в библиотеке "Музыка" или домашней сетевой группе; orderBySearchRank — сортировка сначала по рейтингу во встроенной в Windows поисковой подсистеме, а потом — по дате последнего изменения; orderByDate — сортировка по дате файла (это может быть дата съёмки для файла с фотографией, дата последней правки документа, дата последнего изменения файла и др.). Если указать элемент defaultQuery, в результирующем списке будут присутствовать только файлы, непосредственно хранящиеся в заданной нами папке. Если же указать любой другой элемент, список включит также и все файлы, хранящиеся во вложенных папках. Код: var arrFiles = []; libImages.getFilesAsync(Windows.Storage.Search.CommonFileQuery.orderBySearchRank).then(function(files) {   if (files.size > 0) {     for (var i = 0; i < files.size; i++) {       arrFiles.push(files[i]);     }   } }); Получаем список всех файлов в библиотеке Изображения и вложенных в неё папках, отсортированный по их рейтингу и дате последнего изменения. 6.3. Получение списка файлов с группировкой Несложно получить и список файлов, хранящихся в указанной папке, с группировкой по заданному нами критерию. Достаточно вызвать метод getFoldersAsync, передав ему в качестве единственного параметра один из элементов перечисления Windows.Storage.Search.CommonFolderQuery, задающий критерий группировки: defaultQuery — группировка отсутствует (поведение по умолчанию); groupByYear — группировка файлов по году их даты (это может быть дата съёмки для файла с фотографией, дата последней правки документа, дата последнего изменения файла и др.); groupByMonth — группировка файлов по месяцу их даты; groupByArtist — группировка аудиофайлов по имени исполнителя; groupByAlbum — группировка аудиофайлов по названию альбома; groupByAlbumArtist — группировка аудиофайлов сначала по имени исполнителя, а потом — по названию альбома; groupByComposer — группировка аудиофайлов по автору произведения; groupByGenre — группировка аудиофайлов по жанру; groupByPublishedYear — группировка аудио- и видеофайлов по году выпуска альбома или фильма; groupByRating — группировка графических, аудио- и видеофайлов по рейтингу; groupByTag — группировка файлов по ключевым словам; groupByAuthor — группировка файлов документов по автору; groupByType — группировка файлов по наименованию их типа. Отметим, что группировка поддерживается только для файлов, хранящихся в библиотеке или домашней сетевой группе. Если мы зададим элемент defaultQuery, то получим "на выходе" коллекцию всех папок, хранящихся в указанной нами папке, как если бы мы вызвали метод getFoldersAsync без параметров. Если же мы зададим любой другой элемент перечисления Windows.Storage.Search.CommonFolderQuery, то есть собственно группировку файлов, метод getFoldersAsync выдаст нам список папок, сформированных платформой Metro и существующих только в памяти компьютера (виртуальных папок). Каждая из этих папок будет соответствовать определённому значению критерия группировки и включит файлы, относящиеся к этому значению. Для примера предположим, что в нашей библиотеке "Музыка" хранятся композиции групп "Flesh Field" и "Funker Vogt". Также предположим, что MP3-файлы с этими композициями содержат корректно заполненные теги; в противном случае ничего не выйдет. Если мы выполним группировку по имени исполнителя, то получим список из двух виртуальных папок: Flesh Field и Funker Vogt. Первая папка будет хранить файлы с композициями группы "Flesh Field", а вторая — файлы с композициями группы "Funker Vogt". Также заметим, что в процессе формирования списка файлов с группировкой платформа Metro будет просматривать не только основную папку указанной нами библиотеки, но и все папки, вложенные в неё. Так что результирующий список будет включать и файлы, хранящиеся во вложенных папках. Код: var arrFolders = []; libMusic.getFoldersAsync(Windows.Storage.Search.CommonFolderQuery. groupByArtist).then(function(folders) {   if (folders.size > 0) {     for (var i = 0; i < folders.size; i++) {       folders[i].getFilesAsync().then(function(files) {         if (files.size > 0) {           var arrFiles = [];           for (var j = 0; j < files.size; j++) {             arrFiles.push(files[j]);           }           arrFolders.push(arrFiles);         }       });     }   } }); Получаем список файлов из библиотеки "Музыка", сгруппированный по исполнителю, и формируем на его основе массив arrFolders. Каждый элемент этого массива будет соответствовать полученной в результате группировки виртуальной папке и хранить массив файлов, которые в этой папке находятся. 7. Получение сведений о файлах и папках Так, список файлов и папок мы получили. Теперь давайте узнаем, как получить сведения об отдельном файле или папке — имя, расширение, размер, дату создания и изменения и пр., - а также его или её миниатюру. 7.1. Получение основных сведений о файле Объект Windows.Storage.StorageFile, представляющий в платформе Metro файл, поддерживает ряд свойств, которые позволят нам узнать различные параметры этого файла. Самые полезные из этих свойств перечислены далее: name — имя файла с расширением в виде строки; displayName — имя файла без расширения в виде строки; fileType — расширение файла с начальной точкой в виде строки; path — полный путь к файлу в виде строки/ Отметим, что путь есть не у всех папок; так, у основных папок библиотек путей нет; dateCreated — дата и время создания файла в виде экземпляра объекта Date; displayType — наименование типа файла в виде строки; contentType — MIME-тип файла в виде строки. Код: var sFileName = arrFiles[0].name; Получаем имя первого файла из полученного ранее списка. А метод getBasicPropertiesAsync запускает процесс получения остальных параметров файла — его размера и даты последнего изменения. Он не принимает параметров, а в качестве результата возвращает обязательство, для которого мы с помощью метода then укажем функцию, что выполнится, когда параметры файла будут получены. Данная функция получит в качестве параметра экземпляр объекта Windows.Storage.FileProperties.BasicProperties, представляющий остальные параметры файла. Нас интересуют следующие свойства этого объекта: size — размер файла в байтах в виде целого числа; dateModified — дата и время последнего изменения файла в виде экземпляра объекта Date. Код: var iFileSize; arrFiles[0].getBasicPropertiesAsync().then(function(properties) {   iFileSize = properties.size; }); Получаем размер первого файла из полученного ранее списка. 7.2. Получение миниатюры файла и вывод её на экран Многие приложения, выводящие список файлов, против каждой позиции отображают небольшое изображение — миниатюру. Такой миниатюрой может быть содержимое графического файла или файла документа, первый кадр видеофайла или какое-либо стороннее изображение, обозначающее данный тип файлов. Ранее, чтобы получить миниатюру файла, разработчик должен был писать довольно громоздкий код. Но нам этого делать не придётся — платформа Metro предоставляет встроенные средства для получения миниатюры файла, исключительно гибкие и при этом простые в использовании. Объект Windows.Storage.StorageFile, представляющий файл, поддерживает метод getThumbnailAsync, который запускает процесс получения миниатюры данного файла. Формат его вызова таков: Код: <файл>.getThumbnailAsync(   <вид миниатюры>[,   <ширина миниатюры>[,   <параметры миниатюры>]] ) Единственный обязательный параметр этого метода задаёт вид получаемой миниатюры в виде одного из элементов перечисления Windows.Storage.FileProperties.ThumbnailMode: picturesView — миниатюра размером 190*130 пикселов для списка графических файлов. videosView — миниатюра размером 190*130 пикселов для списка видеофайлов (представляет собой первый кадр фильма). musicView — миниатюра размером 40*40 пикселов для списка звуковых файлов (либо обложка альбома, либо, если она отсутствует, некое стороннее изображение). documentsView — миниатюра размером 40*40 пикселов для списка файлов документов (либо первая страница содержимого, либо сохранённая в файле миниатюра, если она есть). На заметку При указании любого из этих элементов мы получим миниатюру, оптимизированную для использования в списке Metro, который выводит пункты сначала по вертикали, а потом — по горизонтали с горизонтальной прокруткой. (Подробнее о списках Metro мы поговорим позже). listView — миниатюра размером 40*40 пикселов, оптимизированная для списка Metro, который выводит свои пункты по вертикали с вертикальной прокруткой. singleItem — большая миниатюра, оптимизированная для использования в качестве отдельного элемента интерфейса. Минимальный размер самой длинной стороны такой миниатюры равен 256 пикселов. Вторым, необязательным, параметром указывается размер самой длинной стороны получаемой миниатюры в виде целого числа в пикселах. Если этот параметр опущен, будет создана миниатюра с размерами по умолчанию. Третьим, также необязательным, параметром задаются дополнительные параметры получаемой миниатюры. Они указываются в виде одного из элементов перечисления Windows.Storage.FileProperties.ThumbnailOptions: none — дополнительные параметры отсутствуют; returnOnlyIfCached — возвращать только миниатюры, либо хранящиеся в самом файле, либо те, что уже находятся в системном кэше миниатюр Windows. Если таковые отсутствуют, миниатюра не будет сформирована; resizeThumbnail — увеличить или уменьшить миниатюру, полученную из файла или системного кэша, чтобы она вписалась в указанные нами размеры; useCurrentScale — увеличить миниатюру, полученную из файла или системного кэша, соответственно разрешающей способности экрана (поведение по умолчанию). Метод getThumbnailAsync возвращает в качестве результата обязательство, для которого мы в вызове метода then укажем функцию. Эта функция будет выполнена по завершении процесса получения миниатюры и получит в качестве параметра следующее: либо представляющий эту миниатюру экземпляр объекта Windows.Storage.FileProperties.StorageItemThumbnail, если миниатюру удалось получить; либо значение null, если её получить не удалось. Теперь нам нужно как-то вывести полученную миниатюру на экран. Если мы используем для создания Metro-приложения "связку" технологий HTML, CSS и JavaScript, то можем применить для этого хорошо знакомый нам тег . Мы укажем в его атрибуте src ссылку на изображение, которое требуется вывести, то есть нашу миниатюру. Но как получить эту ссылку? Представляющий миниатюру объект Windows.Storage.FileProperties.StorageItemThumbnail не поддерживает ни свойства, ни метода, который позволил бы нам до неё добраться. А экземпляры этого объекта не поддерживаются тегом ... Решить эту проблему нам поможет метод createObjectURL объекта URL. (Объект URL предоставляет различные инструменты для работы с файлами и ссылками на них. Единственный его экземпляр создается самой платформой Metro и доступен через переменную URL.) В качестве единственного параметра он принимает миниатюру и возвращает представляющий его экземпляр особого объекта Blob. Последний можно рассматривать как массив данных, хранящий содержимое данного файла и представленный в виде, который поддерживается всеми элементами интерфейса Metro, в том числе и тегом . То, что нам и нужно! Код: . . . var imgThumbnail = document.getElementById("imgThumbnail"); arrFiles[0].getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.singleItem, 512, Windows.Storage.FileProperties.ThumbnailOptions.resizeThumbnail).then(function(thumbnail) {   imgThumbnail.src = URL.createObjectURL(thumbnail); }); Получаем миниатюру первого файла из полученного ранее списка и выводим её на экран. Для миниатюры мы задаём вид, оптимизированный для вывода в качестве отдельного элемента интерфейса, размер длинной стороны в 512 пикселов и масштабирование под указанный нами размер. 7.3. Получение сведений о папке и миниатюры содержимого папки Объект Windows.Storage.StorageFolder, который представляет папку, поддерживает ряд свойств, позволяющих нам узнать основные параметры этой папки. Это уже знакомые нам свойства displayName, name, path, dateCreated и properties. Помимо этого, платформа Metro позволяет нам получить миниатюру всего содержимого папки. Такая миниатюра представляет собой набор миниатюр отдельных файлов, хранящихся в папке, которые выглядят как сложенные в "стопку". Получение миниатюры содержимого папки выполняется с помощью того же метода getThumbnailAsync, который на этот раз вызывается у папки. Код: . . . var imgThumbnail = document.getElementById("imgThumbnail"); oFP.pickSingleFolderAsync().then(function(folder) {   if (folder) {     folder.getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.singleItem, 512).then(function(thumbnail) {       imgThumbnail.src = URL.createObjectURL(thumbnail);     });   } }); Получаем миниатюру папки, выбранной пользователем. 8. Списки Metro Так, список файлов, хранящийся в папке, мы получили. Теперь его нужно вывести на экран в виде перечня отдельных позиций, отформатированных нужным образом, и дать пользователю возможность выбрать какую-либо позицию, чтобы просмотреть содержимое соответствующего ей файла. Платформа Metro предоставляет нам для этого все нужные инструменты. А именно - списки Metro. 8.1. Создание списка Metro Список Metro является элементом управления Metro и представляется объектом WinJS.UI.ListView. Имя этого объекта нам следует указать в качестве значения для атрибута data-win-control тега
, что создаст для него элемент-основу. Код:
По умолчанию список Metro выстраивает свои пункты сначала по вертикали, а потом — по горизонтали и при необходимости обеспечивает их прокрутку по горизонтали. Получается нечто похожее на таблицу, которая заполняется сначала по столбцам, а потом — по строкам. Однако мы можем изменить это поведение, воспользовавшись свойством layout объекта WinJS.UI.ListView. В качестве значения оно принимает экземпляр одного из объектов, перечисленных далее: экземпляр объекта WinJS.UI.GridLayout указывает списку выстроить пункты сначала по вертикали, потом — по горизонтали с горизонтальной же прокруткой (вывод в виде таблицы; поведение по умолчанию); экземпляр объекта WinJS.UI.ListLayout указывает списку выстроить пункты по вертикали и реализовать вертикальную прокрутку (вывод в виде списка). При этом список Metro выглядит более традиционно. Код: var ctrList = document.getElementById("divList").winControl; ctrList.layout = new WinJS.UI.ListLayout(); Но в большинстве случаев удобнее указать расположение пунктов списка прямо в теге
, который создаёт элемент-основу. (Как мы знаем из предыдущей статьи цикла "Начала Metro-программирования", для указания параметров элемента управления Metro применяется атрибут тега data-win-options.) Но как задать в качестве значения для свойства списка экземпляр объекта? С помощью записи вот такого формата: Код: <имя свойства>: {type: <имя объекта>} Имя объекта, на основе которого будет создан экземпляр, указывается без кавычек. Код:
А свойство selectionMode объекта WinJS.UI.ListView позволит нам указать режим выбора пунктов в списке. Оно поддерживает три предопределённых значения, которые должны быть указаны в виде строк: none — пользователь не сможет выбрать ни один пункт в списке; single — пользователь сможет выбрать только один пункт; multi — пользователь может выбрать произвольное количество пунктов (поведение по умолчанию). Код:
Для задания режима выбора пунктов в коде логики удобнее использовать перечисление WinJS.UI.SelectionMode. Поддерживаемые им элементы none, single и multi соответствуют перечисленным ранее строковым значениям. Код: ctrList.selectionMode = WinJS.UI.SelectionMode.single; 8.2. Создание пунктов списка Пустой, не содержащий пунктов список никому не нужен. Поэтому следующим нашим шагом будет заполнение готового списка Metro пунктами. 8.2.1. Подготовка массива данных Данные, которые будут выводиться в списке Metro, должны представлять собой массив (он так и называется - массив данных). И формируется он по особым правилам: каждый элемент массива данных представляет один пункт списка; каждый элемент массива данных должен хранить экземпляр объекта Object; каждый экземпляр объекта Object, хранящийся в массиве данных, должен включать набор свойств, которые будут хранить отдельные значения, выводимые в пункте списка. Код: var arrNews = []; arrNews[0] = {   date: new Date(2012, 1, 17),   header: "Microsoft рассказала о ReFS, новой файловой системе Windows Server 8",   content: "В своем блоге B8 компания Microsoft поделилась информацией о новой файловой системе Windows Server 8, известной под названием ReFS и призванной заменить NTFS, которая используется в Windows с 1993 года." }; Здесь мы объявили согласно приведённым ранее правилам массив данных arrNews и создали первый его элемент, представляющий новость из канала RSS. Как видим, каждая новость включает в свой состав дату публикации (свойство date), заголовок (свойство header) и содержимое (свойство content). 8.2.2. Создание источника данных, получение адаптера и привязка его к списку Итак, массив данных готов. Можно создавать на его основе источник данных — особую структуру, которая позволит списку получать из массива данные и формировать на их основе пункты. Источник данных представляется объектом WinJS.Binding.List. Нам следует создать экземпляр этого объекта, передав ему в качестве параметра созданный ранее массив данных: Код: var dsrNews = new WinJS.Binding.List(arrNews); Далее мы обратимся к свойству dataSource созданного источника данных, чтобы получить так называемый адаптер. Это экземпляр особого объекта, который можно рассматривать как посредник между источником данных и списком. Останется только привязать полученный адаптер к списку Metro — и дело сделано. Для этого достаточно присвоить адаптер свойству itemDataSource списка. Код: ctrList.itemDataSource = dsrNews.dataSource; После этого список сам сформирует пункты на основе указанных нами данных и выведет их на экран. 8.2.3. Использование шаблонов для оформления пунктов списка Только пункты этого списка будут содержать совсем не то, что нам нужно. А именно — исходный код на языке JavaScript, с помощью которого мы создали соответствующие этим пунктам элементы массива данных. Дело в том, что список Metro не "знает", какие значения из элемента массива данных, описывающего пункт, ему следует вывести на экран и какой формат для этого применить. Поэтому он поступает по умолчанию — преобразует каждый элемент массива в текстовое представление, то есть в JavaScript-код, который его формирует. Так что нам придётся указать формат, в котором список Metro будет формировать свои пункты. Или, если придерживаться терминологии Metro, задать для списка шаблон — описание этого формата. Шаблон Metro можно описать на языке HTML прямо в файле, где хранится код интерфейса приложения (это файл default.html). HTML-код, создающий шаблон, обязательно должен находиться перед кодом, формирующим список, к которому он будет привязан; в противном случае список его не "найдёт". Шаблон — это особый элемент управления Metro. Он создается так же, как остальные элементы управления Metro, и представляется объектом WinJS.Binding.Template. Код:
Внутри элемента-основы шаблона формируются элементы интерфейса, в которых, собственно, и будут выводиться необходимые данные. Теги, формирующие эти элементы интерфейса, должны быть пустыми. Для каждого из этих элементов интерфейса нам потребуется указать два свойства. Во-первых, свойство экземпляра объекта Object — элемента массива данных, откуда будет взято значение, что будет выводиться в этом элементе. Во-вторых, свойство самого элемента интерфейса, которому будет присвоено данное значение. Например, значение свойства header элемента массива данных мы можем присвоить свойству textContent текстового абзаца (оно представляет текстовое содержимое элемента интерфейса), а значение свойства src элемента массива данных, хранящего интернет-адрес файла с изображением, — свойству src элемента — графического изображения. Эти свойства мы укажем в атрибуте тега data-win-bind, значение которого записывается в следующем формате: Код: <свойство элемента интерфейса>: <свойство экземпляра объекта Object — элемента массива данных> Код:
 

 

Здесь мы создали шаблон с двумя абзацами. Свойству textContent первого из них будет присвоено значение свойства date элемента массива данных, в результате чего данный абзац получит в качестве текстового содержимого дату публикации новости, которая перед выводом будет автоматически преобразована в строковый вид. А во втором абзаце будет выведен заголовок новости (значение свойства header элемента массива данных). Мы можем задать оформление для элементов интерфейса, входящих в шаблон, привязав к ним стили. Код:
 

 

. . . .new-date { text-align: right; } .new-header { font-size: 12pt; } Здесь мы указали для абзаца с датой публикации выравнивание текста по правому краю, а для абзаца с заголовком — размер шрифта в 12 пунктов. Осталось лишь привязать готовый шаблон к списку. Для этого достаточно присвоить имя его элемента-основы свойству itemTemplate списка: Код:
Отметим две вещи. Во-первых, мы присваиваем этому свойству имя элемента-основы шаблона; получив его, список Metro сам "отыщет" созданный на его основе шаблон. Во-вторых, мы указываем это имя без кавычек. Вот теперь наш список будет выводить то, что нам нужно. Ещё платформа Metro позволяет создать шаблон в виде функции (так называемый шаблон-функцию). Эта функция будет формировать пункты списка программно. Единственным параметром, принимаемым данной функцией, станет обязательство. Для этого обязательства мы в вызове метода then укажем функцию, которая в качестве параметра получит экземпляр объекта Object, представляющий формируемый пункт. Его свойство data, в свою очередь, будет хранить экземпляр объекта Object — соответствующий данному пункту элемент массива данных. Результатом, который будет возвращать функция, заданная для этого обязательства, станет полностью созданный элемент интерфейса, который сформирует пункт списка. Этот элемент интерфейса должен представлять собой блок, в котором будут находиться элементы, представляющие отдельные части этого пункта: абзацы, заголовки, другие блоки и др. Помимо этого, сама шаблон-функция должна вернуть в качестве результата обязательство, что она получила как параметр. Лучше один раз увидеть, чем сто раз услышать. Поэтому давайте рассмотрим пример шаблона-функции. Код: function listTemplate(itemPromise) {   return itemPromise.then(function(item) {     var oItem = item.data;     var oDiv = document.createElement("div");     var oP = document.createElement("p");     oP.className = "new-date";     oP.textContent = oItem.date.getDate() + "." +       (oItem.date.getMonth() + 1) + "." + oItem.date.getFullYear();     oDiv.appendChild(oP);     oP = document.createElement("p");     oP.className = "new-header";     oP.textContent = oItem.header;     oDiv.appendChild(oP);     return oDiv;   }); } Для формирования значения даты в привычном нам формате <число>.<месяц>.<год> мы использовали три метода объекта Date. Метод getDate возвращает число, метод getMonth — номер месяца от 0 до 11 (январь — декабрь), а метод getFullYear — год в "полном" формате (четыре цифры). Все они не принимают параметров и возвращают результат в виде целого числа. Осталось только привязать готовый шаблон-функцию к списку. Сделать это можно только в коде логики. Код: ctrList.itemTemplate = listTemplate; Шаблон-функция имеет перед обычным, описанным в HTML-коде шаблоном определённые преимущества. Во-первых, мы можем использовать в таком шаблоне значения, полученные в результате каких-либо вычислений (так, в приведённом ранее примере мы вычисляли значение даты в привычном нам формате). Во-вторых, в зависимости от выполнения какого-либо условия мы можем менять содержимое пунктов списка. В HTML-шаблоне ничего этого сделать не получится. 8.2.4. Фильтрация пунктов списка Источник данных предоставляет нам интересную и полезную возможность — фильтрацию пунктов списка. То есть вывод в списке только тех пунктов, что удовлетворяют определённому условию — критерию фильтрации. Если мы хотим реализовать в своем списке фильтрацию, нам не обойтись без метода createFiltered источника данных. Код: <источник данных>.createFiltered(   <функция, задающая критерий фильтрации> ) Единственным параметром он принимает функцию, которая реализует критерий фильтрации. В качестве единственного параметра данная функция примет экземпляр объекта — элемент массива данных, а вернёт логическое значение: true, если данный элемент должен быть выведен в списке, и false, если не должен. Метод createFiltered возвращает в качестве результата новый источник данных — уже отфильтрованный. Код: var oDate = new Date(2012, 2, 17); function filterList(item) {   return (item.date.getTime() == oDate.getTime()); } var dsrFiltered = dsrNews.createFiltered(filterList); ctrList.itemDataSource = dsrFiltered.dataSource; Выводим в списке только новости, опубликованные 17 января 2012 года. Метод getTime объекта Date возвращает количество миллисекунд, прошедших между значением даты и полночью 1 января 1970 года. Фактически он позволяет получить числовое представление даты, которое можно использовать в операциях сравнения (что мы и сделали). Самое интересное, что фильтрация, которую мы реализуем, не затронет сам массив данных. Это значит, что все элементы, не удовлетворяющие заданному нами критерию фильтрации, хоть и не будут присутствовать в новом источнике данных, но всё же останутся в составе массива. 8.2.5. Сортировка пунктов списка Другая возможность, предоставляемая нам источником данных Metro — сортировка пунктов списка. Отсортированные по какому-либо критерию списки легче читаются. Выполнить сортировку нам поможет метод createSorted источника данных. Код: <источник данных>.createSorted(   <функция сравнения> ) Единственным параметром этому методу передаётся так называемая функция сравнения. Она принимает два параметра — два сравниваемых элемента массива данных — и возвращает в качестве результата: отрицательное число, если первый элемент "меньше" второго; 0, если оба этих элемента "равны"; положительное число, если первый элемент "больше" второго. Метод createSorted возвращает в качестве результата новый — отсортированный — источник данных. Код: function compareItems(item1, item2) {   return item1.date.getTime() - item2.date.getTime(); } var dsrSorted = dsrNews.createSorted(compareItems); ctrList.itemDataSource = dsrSorted.dataSource; Выводим в списке новости, отсортированные по дате публикации. Отметим, что в теле функции сравнения мы вычитаем числовое представление даты публикации второй новости из числового представления даты публикации первой новости. (Как говорилось ранее, эти представления можно получить вызовом метода getTime.) Это самый простой способ получить значение, указывающее, какое из сравниваемых значений "больше", а какое — "меньше". Сортировка, что мы реализуем в источнике данных, также не будет затрагивать исходный массив данных. 8.2.6. Группировка пунктов списка Вдоволь налюбовавшись списком Metro, самостоятельно фильтрующим и сортирующим данные, давайте дадим ему задачу потруднее. А именно, заставим его группировать пункты по какому-либо признаку, в нашем случае — по дате публикации новостей. В процессе группировки пункты списка разбиваются на отдельные группы по значению указанного нами признака — критерия группировки. Таким критерием может быть значение какого-либо свойства элемента в массиве данных, фрагмент этого значения или результат вычисления, выполненного на основе сразу нескольких значений. Например, если мы выберем в качестве критерия группировки дату публикации новости, то пункты списка будут разбиты на группы, каждая из которых будет соответствовать определённому значению этой даты. Группы будут выведены отсортированными. Критерий их сортировки также задаем мы. При выводе на экран каждой группе пунктов будет предшествовать заголовок. В заголовке обычно указывается значение критерия, общее для пунктов, что входят в группу (например, в нашем случае - значения даты публикации). Внимание! Группировка в списке Metro станет полноценно работать только в случае вывода пунктов в виде таблицы. Если пункты выводятся в виде списка, заголовки групп отображаться не будут. Чтобы реализовать группировку, нам сначала придётся объявить три функции. Первая функция будет определять значение критерия группировки для каждого элемента массива данных. Она примет в качестве единственного параметра сам этот элемент и вернёт соответствующее ему значение критерия группировки обязательно в виде строки. Код: function getGroupKeyValue(item) {   return item.date.getTime().toString(); } Используем в качестве критерия группировки числовое представление даты публикации новости, преобразованное в строку. Вторая функция будет подготавливать данные, которые понадобятся для формирования групп и будут впоследствии выводиться в их заголовках. В качестве единственного параметра она также примет элемент массива данных, а в качестве результата вернёт экземпляр объекта Object со свойствами, которые будут хранить все необходимые для формирования заголовков групп значения. Код: function getGroupData(item) {   return {     date: item.date.getDate() + "." + (item.date.getMonth() + 1) + "." +       item.date.getFullYear()   }; } Экземпляр объекта Object, который вернёт эта функция, будет включать свойство date — дату публикации новости в виде строки. Её-то мы и выведем в заголовке группы. Третья функция будет представлять уже знакомую нам функцию сравнения (см. ранее). Она получит в качестве параметров два значения критерия группировки. Код: function sortGroups(group1, group2) {   return parseInt(group1) - parseInt(group2); } Объявив все эти три функции, мы вызовем метод createGrouped источника данных: Код: <источник данных>.createGrouped(   <первая функция>,   <вторая функция>,   <третья функция> ) В качестве параметров он примет три объявленные нами функции. Результатом, который вернёт этот метод, станет новый источник данных, уже сгруппированных. Код: var dsrGrouped = dsrNews.createGrouped(getGroupKeyValue, getGroupData, sortGroups); Полученный источник данных мы привяжем к списку известным нам способом: Код: ctrList.itemDataSource = dsrGrouped.dataSource; Помимо этого, нам потребуется получить ещё один источник данных — содержащий все группы. Он хранится в свойстве groups источника сгруппированных данных. Извлечённый из него адаптер мы присвоим свойству groupDataSource списка Metro. Код: ctrList.groupDataSource = dsrGrouped.groups.dataSource; Останется только создать шаблон, на основе которого будут формироваться заголовки групп, и привязать его к списку. Такой шаблон создаётся по тем же правилам, что и шаблон для обычных пунктов списка. За одним исключением: данные для вывода он будет брать из экземпляра объекта Object, возвращаемого второй по счёту из объявленных нами функций. Код:
 

Чтобы привязать шаблон заголовка группы к списку, присвоим его свойству groupHeaderTemplate списка: Код:
Как и в случае фильтрации и сортировки, реализованная нами группировка не будет затрагивать исходный массив данных. 8.3. Получение выбранных пунктов Что ж, мы заполнили список пунктами и, возможно, реализовали их фильтрацию, сортировку или группировку. Теперь нам нужно как-то выяснить, какие пункты списка выбраны пользователем. Для этого служит свойство selection списка. Оно возвращает экземпляр особого объекта-коллекции, представляющего набор всех выбранных в списке пунктов. Код: var oSel = ctrList.selection; Этот объект поддерживает метод count, не принимающий параметров и возвращающий в качестве результата количество выбранных пунктов в виде целого числа. Код: if (oSel.count() > 0) {   //Получаем выбранные пункты } Получить выбранные пункты мы можем, вызвав метод getItems этого же объекта. Он не принимает параметров и возвращает в качестве результата обязательство, для которого мы зададим в вызове метода then функцию, что выполнится после завершения процесса получения выбранных пунктов. В качестве единственного параметра она получит массив, каждый элемент которого будет экземпляром объекта Object со свойствами index и data; первое свойство будет хранить номер выбранного пункта, а второе — соответствующий этому пункту элемент массива данных. Внимание! Следует иметь в виду, что таким образом мы получим не индексы элементов в массиве данных, соответствующих выбранным пунктам, а номера самих выбранных пунктов. А если мы реализовали в списке фильтрацию, сортировку или группировку, эти значения не будут идентичными! Код: var arrSelectedNews = []; oSel.getItems().then(function(selectedItems) {   for (var i = 0; i < selectedItems.length; i++) {     arrSelectedNews.push(selectedItems[i].data);   } }); Получаем все пункты, выбранные пользователем, и помещаем их в массив arrSelectedNews. Объект-коллекция, представляющий набор выбранных пунктов списка, поддерживает несколько полезных методов, о которых мы сейчас поговорим. Метод set позволяет сделать выбранными пункты списка с указанными номерами. В качестве единственного параметра он принимает массив номеров выбираемых пунктов и не возвращает результата. Код: oSel.set([1, 3, 10]); Делаем выбранными второй, четвёртый и одиннадцатый пункты списка. (Не забываем, что нумерация пунктов начинается с нуля.) Метод selectAll делает все пункты списка выбранными, а метод clear, наоборот, снимает выделение со всех пунктов. Оба метода не принимают параметров и не возвращают результата. Код: oSel.selectAll(); Событие selectionchanged списка возникает, когда состав выбранных в данный момент пунктов изменяется, то есть когда пользователь выбирает другой пункт или отменяет выбор у выбранного ранее. Мы можем использовать это событие, чтобы отслеживать выбор пунктов в списке. Впрочем, если список позволяет выбрать только один пункт, наша задача серьёзно упрощается. При выборе любого пункта в списке возникает событие iteminvoked, обработчик которого мы можем использовать для отслеживания выбора пункта и его получения. Выяснить в этом случае, какой пункт был выбран, можно из экземпляра объекта CustomEvent, который хранит сведения о возникшем событии и передаётся в его функцию-обработчик единственным параметром. Данный объект, в числе прочего, поддерживает свойство detail. Оно хранит набор дополнительных данных о событии в виде экземпляра объекта Object, свойства которого и содержат эти самые данные. В случае события iteminvoked данный экземпляр объекта Object содержит свойство itemPromise. Оно хранит обязательство, для которого мы в вызове метода then укажем функцию, что выполнится после получения выбранного пункта. Эта функция примет в качестве параметра экземпляр объекта Object, свойство index которого будет хранить номер выбранного пункта, а свойство data — соответствующий ему элемент массива данных. Код: ctrList.addEventListener("iteminvoked", function(evt) {   evt.detail.itemPromise.then(function(selected) {     var selectedIndex = selected.index;     var selectedItem = selected.data;     . . .   }); }); Ещё нам может пригодиться метод getAt источника данных. Он принимает в качестве единственного параметра номер пункта списка в виде числа и возвращает в качестве результата соответствующий этому пункту элемент массива данных. Код: var oItem = dsrList.getAt(0); Получаем элемент массива данных, соответствующий первому пункту списка. Источник данных также поддерживает свойство length, знакомое нам по массивам. Оно возвращает количество пунктов в списке. Код: var oItem = dsrList.getAt(dsrList.length - 1); Получаем элемент массива данных, соответствующий последнему пункту списка. 8.4. Прочие возможности списков Metro Осталось рассмотреть остальные возможности списка Metro, которые могут нам пригодиться. Метод ensureVisible списка выполняет прокрутку списка таким образом, чтобы нужный нам пункт стал видимым. В качестве единственного параметра этот метод принимает номер пункта в виде целого числа и не возвращает результата. Код: ctrList.ensureVisible(dsrList.length - 1); Прокручиваем список в самый конец. Теперь рассмотрим возможности по оформлению списков. Реализуются они созданием стилевых классов со строго определёнными именами. Стилевой класс .win-listview задаёт оформление для самого списка. Он автоматически привязывается к его элементу-основе. Код: .win-listview {   width: 100%;   height: 100%; } Растягиваем все списки Metro, что входят в интерфейс нашего приложения, на всё пространство их родителей. Стилевой класс .win-item задаёт оформление для пунктов списка. Код: #divList .win-item {   width: calc(25% - 10px);   padding-left: 10px; } Задаём для пунктов нашего списка отступ слева, равный 10 пикселам, и ширину, равную 25% от ширины списка минус заданное ранее значение отступа слева. Стилевой класс .win-groupheader задаёт оформление для заголовков групп. Код: #divList .win-groupheader { height: 50px; } Задаём для заголовка группы высоту в 50 пикселов. 9. Панель вывода Metro Панель вывода (в терминологии Metro — viewbox) устанавливает размеры своего единственного потомка таким образом, чтобы этот потомок занимал всё пространство внутри данного элемента. При этом панель вывода сама следит за тем, чтобы соотношение сторон потомка не исказилось. Настоящая находка для разработчиков, собирающихся создавать приложения для просмотра графики! Панель вывода представляется объектом WinJS.UI.ViewBox. Код:
 
} Создаём панель вывода и помещаем в него элемент графического изображения. 10. Собственно приложение просмотрщика графики Теперь мы можем наконец доделать приложение просмотрщика графики. Запустим Visual Studio и откроем в нём проект ImageViewer, если уже успели его закрыть. И не забудем дать приложению права на доступ к библиотеке "Изображения". Переключимся на файл default.html и исправим присутствующий в теге код, чтобы он выглядел следующим образом: Код:
 
 
Мы просто превратили блоки divViewer и divList в панель вывода и список соответственно, добавив в создающие их теги атрибуты data-win-control и data-win-options. Теперь переключимся на файл default.css и добавим в присутствующий в нём код такой фрагмент: Код: #divList .win-item {   width: 190px;   height: 130px; } Этот стиль установит для пунктов списка divList размеры 190*130 пикселов. Такие же размеры будут иметь миниатюры, которые мы станем выводить в этом списке. Осталось самое сложное - написать код логики. Переключимся на файл default.js, где он хранится. Сначала введём код, объявляющий необходимые переменные. Вот он: Код: var divFolderPath, divFileName, imgViewer, ctrList, oCurrentFolder, arrFiles, dsrFiles; Переменные divFolderPath и divFileName будут хранить соответствующие блоки, а переменные imgViewer и ctrList - панель вывода и список. Переменная oCurrentFolder сохранит папку, содержимое которой выводится в списке в данный момент, переменная arrFiles - массив с файлами, что хранятся в этой папке, а переменная dsrFiles - источник данных, созданный на основе этого массива. Напишем код инициализации. Код: document.addEventListener("DOMContentLoaded", function() {   WinJS.UI.processAll().then(function() {     var ctrAppBar = document.getElementById("divAppBar").winControl;     divFolderPath = document.getElementById("divFolderPath");     divFileName = document.getElementById("divFileName");     imgViewer = document.getElementById("imgViewer");     ctrList = document.getElementById("divList").winControl;     var btnSelect = ctrAppBar.getCommandById("btnSelect");     ctrList.addEventListener("iteminvoked", ctrListItemInvoked);     btnSelect.addEventListener("click", btnSelectClick);     ctrList.itemTemplate = listTemplate;     oCurrentFolder = Windows.Storage.KnownFolders.picturesLibrary;     fillList();   }); }); Здесь мы выполняем инициализацию элементов управления Metro, получаем доступ ко всех нужным нам элементам интерфейса и привязываем к ним обработчики событий. Также мы привязываем к списку функцию-шаблон listTemplate, помещаем в переменную oCurrentFolder библиотеку Изображения, содержимое которой должно выводиться в списке изначально, и вызываем функцию fillList, которая заполнит список. Объявим функцию, которая будет вызвана при щелчке на кнопке Выбрать папку панели инструментов. Код: function btnSelectClick() {   var oFP = new Windows.Storage.Pickers.FolderPicker();   oFP.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;   oFP.fileTypeFilter.replaceAll([".gif", ".jpg", ".png"]);   oFP.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;   oFP.pickSingleFolderAsync().then(function(folder) {     if (folder) {       oCurrentFolder = folder;       fillList();     }   }); } Она выведет экран выбора папки и, в случае, если пользователь укажет в нём какую-либо папку, поместит её в переменную oCurrentFolder и, опять же, вызовет функцию fillList... ...которую сейчас самое время объявить. Код: function fillList() {   divFolderPath.textContent = oCurrentFolder.path;   arrFiles = [];   oCurrentFolder.getFilesAsync().then(function(files) {     if (files.size > 0) {       for (var i = 0; i < files.size; i++) {         arrFiles.push(files[i]);       }     }     dsrFiles = new WinJS.Binding.List(arrFiles);     ctrList.itemDataSource = dsrFiles.dataSource;   }); } Мы выводим в блоке divFolderPath путь к текущей папке, помещаем все имеющиеся в ней файлы в массив arrFiles, формируем на основе этого массива источник данных и привязываем его к списку. На очереди - функция-шаблон listTemplate. Код: function listTemplate(itemPromise) {   return itemPromise.then(function(item) {     var oItem = item.data;     var oDiv = document.createElement("div");     var oImg = document.createElement("img");     oItem.getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.picturesView).then(function(thumbnail) {       oImg.src = URL.createObjectURL(thumbnail);     });     oDiv.appendChild(oImg);     return oDiv;   }); } Она создаёт блок, который станет пунктом списка, и помещает внутри него элемент графического изображения, в котором выводит миниатюру соответствующего файла. Не забудем также объявить функцию - обработчик события iteminvoked списка. Код: function ctrListItemInvoked(evt) {   evt.detail.itemPromise.then(function(selected) {     var oFile = selected.data;     imgViewer.src = URL.createObjectURL(oFile);     divFileName.textContent = oFile.name;   }); } Она получит выбранный в списке файл, выведет его содержимое в элементе графического изображения imgViewer, а его имя — в блоке divFileName. Сохраним все файлы и запустим приложение. Дождёмся, когда в списке появятся все файлы, хранящиеся в основной папке библиотеки Изображения, выберем какой-либо файл и посмотрим на его содержимое (рис. 3). После этого попробуем выбрать другую папку и понаблюдаем, что из этого выйдет. Если мы всё сделали правильно, приложение должно работать нормально. 11. Заключение В этой статье мы научились работать со списками и панелями вывода Metro, пользоваться экраном выбора папки и средствами Metro для получения списка файлов и сведений о файле, в том числе его миниатюры. Также мы разобрались с механизмами безопасности платформы Metro и узнали, как задавать права приложения. И в качестве практики написали простое приложение для просмотра графики.