Коллекции и элементы управления для вывода коллекций
Источники данных
Во всех случаях, которые мы рассмотрели, мы использовали источники данных, построенные на основе WinJS.Binding.List и находящиеся в памяти. Очевидно, существуют и другие типы источников данных, и их не обязательно сразу же загружать в память. Как же работать с такими источниками?
WinJS предоставляет некоторую помощь в этой области. Первое средство - это объект WinJS.UI.StorageDataSource, который работает с файлами в файловой системе, как в следующем разделе, в демонстрации работы FlipView с библиотекой изображений. Следующее средство - это WinJS.UI.VirtualizedDataSource, которое подразумевает использование базового класса для пользовательских источников данных, это расширенный сценарий, которого мы коснёмся лишь кратко.
FlipView и библиотека изображений
Всё, что мы видели в примере, касающемся FlipView, ведет нас к совершенно очевидной возможности: перемещаться по файлам изображений в папке. Используя то, что мы узнали, как нам реализовать это? У нас уже есть шаблон элемента, который содержит тег img, таким образом, возможно, нам лишь нужны некоторые URI для таких файлов. Возможно, нам следует создать их массив, используя API наподобие Windows.Storage.KnownFolders.picturesLibrary.getFilesAsync (конечно, объявив возможность Библиотека изображений (Pictures Library) в манифесте). Это даст нам множество объектов StrorageFile, для которых мы можем вызвать URL.createObjectURL. Найденные URI мы можем сохранить в массиве и затем поместить их в WinJS.Binding.List:
var myFlipView = document.getElementById("pictures_FlipView").winControl; Windows.Storage.KnownFolders.picturesLibrary.getFilesAsync() .done(function (files) { var pixURLs = []; files.forEach(function (item) { var url = URL.createObjectURL(item, {oneTimeOnly: true }); pixURLs.push({type: "item", title: item.name, picture: url }); }); var pixList = new WinJS.Binding.List(pixURLs); myFlipView.itemDataSource = pixList.dataSource; });
Хотя подобный подход работает, он может потребовать немного памяти при работе с большим числом изображений высокого разрешения, так как каждое изображение должно быть полностью загружено для того, чтобы оно отобразилось во FlipView. Возможно, это как раз то, что вам нужно, но в других случаях подобный подход потребует больше ресурсов, чем необходимо. Кроме того, минус такого подхода заключается в том, что изображения лишь сжимаются и растягиваются для того, чтобы отобразиться во FlipView, без какого-либо учёта соотношения сторон, а это не позволит получить наилучший результат.
Лучший подход заключается в использовании WinJS.UI.StorageDataSource (http://msdn.microsoft.com/library/windows/apps/br212650.aspx), который работает напрямую с файловой системой, вместо того, чтобы работать с массивом, расположенным в памяти. Я реализовал это в виде Сценария 8 в измененном примере использования FlipView в дополнительных материалах к этой лекции (Другие материалы можно найти в примере "Использование StorageDataSource и GetVirtualizedFilesVector" (http://code.msdn.microsoft.com/windowsapps/Data-source-adapter-sample-3d32e535)). Здесь мы можем использовать короткое имя для того, чтобы получить источник данных из библиотеки изображений:
myFlipView.itemDataSource = new WinJS.UI.StorageDataSource("Pictures");
"Pictures" это короткое имя, так как первый аргумент StorageSource, это файловый запрос, который исходит из API Windows.Storage.Search, подробнее об этом мы поговорим в "Быстрый старт" курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript". Эти запросы, которые попадают в мощную функцию Windows.Storage.StorageFolder.createFileQueryWithOptions (http://msdn.microsoft.com/library/windows/apps/br211591.aspx) - это способы для перебора файлов в папках вместе с метаданными - такими, как обложки альбомов, подробности о записях, и эскизы, которые кадрированы так, чтобы сохранить соотношение сторон. Короткие имена, наподобие"Pictures" (а так же "Music", "Documents", "Videos" которые нуждаются в соответствующих возможностях, объявленных в манифесте) лишь создают типичные запросы для подобных библиотек документов.
Нужно отметить, что StorageDataSource не поддерживает напрямую одностороннюю привязку данных, поэтому вы получите исключение, если вы попытаетесь сослаться на элемент напрямую в шаблоне. Чтобы это обойти, нужно явно использовать функцию-инициализатор WinJS.Binding.oneTime для каждого свойства:
<div id="pictures_ItemTemplate" data-win-control="WinJS.Binding.Template"> <div class="overlaidItemTemplate"> <img class="image" data-win-bind="src: thumbnail InitFunctions.thumbURL; alt: name WinJS.Binding.oneTime" /> <div class="overlay"> <h2 class="ItemTitle" data-win-bind="innerText: name WinJS.Binding.oneTime"></h2> </div> </div> </div>
В случае со свойством img.src, файловый запрос возвращает нам элементы типа Windows.Storage.BulkAccess.FileInformation (http://msdn.microsoft.com/library/windows/apps/windows.storage.bulkaccess.fileinformation.aspx) (переменная source в коде ниже), которая содержит эскизы изображений, а не URI. Для того чтобы конвертировать эти данные об изображениях в URI, нам нужно использовать свой собственный инициализатор привязки:
WinJS.Namespace.define("InitFunctions", { thumbURL: WinJS.Binding.initializer(function (source, sourceProp, dest, destProp) { if (source.thumbnail) { dest.src = URL.createObjectURL(source.thumbnail, { oneTimeOnly: true }); } }) });
В этом инициализаторе часть data-win-bind src : thumbnail, на самом деле, игнорируется, так как мы устанавливаем свойство изображения src напрямую в значение source.thumbnail. Это - лишь форма односторонней привязки данных.
Обратите внимание на то, что эскизы не всегда сразу доступны в объекте FileInformation, именно поэтому мы проверяем, действительно ли у нас имеется эскиз, прежде чем создаём URI для него. Это означает, что быстрое перелистывание изображений может показать пользователю пустые места для изображений. Для того, чтобы справиться с этим, мы можем прослушивать событие FileInformation.onthumbnailupdated и обновлять элементы в это время. Лучший способ добиться этого заключается в использовании вспомогательного метода StorageDataSource.loadThumbnail (http://msdn.microsoft.com/library/windows/apps/jj553712.aspx), который позволяет нам убедиться в вызове removeEventListener для этого события WinRT (смотрите раздел "События WinRT и removeEventListener" в "Анатомия приложения и навигация по страницам" ).
Вы можете использовать этот метод внутри инициализатора привязки, как показано в Сценарии 1 вышеупомянутого примера "Использование StorageDataSource и GetVirtualizedFilesVector" (http://code.msdn.microsoft.com/windowsapps/Data-source-adapter-sample-3d32e535), или внутри функции рендеринга, которая имеется в декларативном шаблоне. Мы сделаем это для нашего примера использования FlipView позже, в разделе "Как, на самом деле, работают шаблоны", что, кроме того, позволит нам избежать трюков с односторонней привязкой данных.
В качестве последнего замечания, Сценарий 6 в примере работы с FlipView, содержит некоторые образцы использования различных источников данных, особенно те, которые работают со службой поиска Bing. Поэтому, взглянем на пользовательские источники данных.
Пользовательские источники данных
Теперь, когда мы видели элемент управления для коллекций, наподобие FlipView, который работал с двумя разными источниками данных, вы, вероятно, начинаете догадываться о том, что все источники данных имеют некоторые общие характеристики и общий программный интерфейс. Это снова продемонстрировано в Сценарии 6 примера использования FlipView, так же, как и в примере "Работа HTML ListView с источниками данных" (http://code.msdn.microsoft.com/windowsapps/ListView-custom-data-4dcfb128), показанного на Рис. 5.6, который мы рассмотрим в этом разделе.
Сценарии 2 и 3 этого примера работают на основе источника данных WinJS.Binding.List, как мы уже видели, и предоставляют кнопки для управления этим источником данных. Эти изменения отражаются на выходных данных. Разница между двумя сценариями заключается в том, что Сценарий 2 манипулирует данными посредством методов WinJS.Binding.List наподобие move, в то время как Сценарий 3 манипулирует источником данных, на котором он основан, посредством более общего API ListDataSource (http://msdn.microsoft.com/library/windows/apps/br211786.aspx).
Из-за привязки данных, изменения в данных отражаются на элементе управления ListView в любом случае, но есть три важных различия. Первое, интерфейс ListDataSource - обычный для всех источников данных, в итоге любой код, написанный с его использованием, будет работать для любого источника данных. Второе - его методы обычно асинхронны, так как источник данных может быть подключён к онлайновому сервису или другому подобному ресурсу. Третье - ListDataSource обеспечивает вызов beginEdits для пакетного изменения данных, что подавляет любые сообщения об изменениях, направленные внешним присоединённым объектам, до вызова endEdits. Это позволяет вам выполнять изменение больших объемов данных способом, который может улучшить производительность ListView.
Сценарии 1 и 4 в примере показывают, как можно создать пользовательский источник данных. Сценарий 1 создаёт источник данных для поиска Bing. Сценарий 4 создаёт один источник в виде массива в памяти, который вы можете приспособить для работы с некоторыми полученными извне данными, которые поступают от некоего сервиса небольшими порциями. Важно здесь то, что при всех этих подходах реализуется то, что называется адаптером обработки данных (data adapter), который является объектом с методами интерфейса WinJS.UI.IListDataAdapter (http://msdn.microsoft.com/library/windows/apps/br212603.aspx). Это обеспечивает такие возможности, как кэширование, виртуализация, обнаружение изменений и так далее. К счастью, вы получаете большинство из этих методов, наследуя ваш класс от WinJS.UI.VirtualizedDataSource (http://msdn.microsoft.com/library/windows/apps/hh701413.aspx) и затем реализуя те методы, которые вам нужно настроить. В примере, например, bindingImageDataSource определен так, как показано ниже (смотрите js/BingImageSearchDataSource.js):
bingImageSearchDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function (devkey, query) { this._baseDataSourceConstructor(new bingImageSearchDataAdapter(devkey, query)); });
Где класс bingImageSearchDataAdapter реализует напрямую лишь методы getCount и itemsFromIndex.
Для получения более глубоких знаний по данному вопросу, выходящих за пределы этого примера, я отсылаю вас к сессии конференции Build 2011 года: "APP210-T: Построение коллекций, управляемых данными и приложений со списками с использованием ListView в HTML5" (http://channel9.msdn.com/Events/BUILD/BUILD2011/APP-210T). Кое-что с тех пор изменилось (так, ArrayDataSource теперь WinJS.Binding.List), но в целом здесь хорошо разъяснены механизмы. Кроме того, полезно помнить, что вы так же можете использовать другие языки, наподобие C# или C++ для того, чтобы описать пользовательский источник данных. Подобные языки могут предложить более высокую производительность внутри источника данных и позволят получить доступ к более производительным API, нежели те, к которым даёт доступ JavaScript.