Для того, чтобы сделать приложение поставщиком для средства выбора файлов, начинают – с чего же еще! – с добавления соответствующего объявления в манифест. В данном случае применимы три объявления: Средство выбора файлов для открытия (File Open Picker), Средство выбора файлов для сохранения (File Save Picker) и Средство обновления кэшированных файлов (Cached File Updater), как показано ниже, на примере окна редактора манифеста в Visual Studio. Каждое из этих объявлений можно сделать один раз в пределах одного приложения.
Объявления Средство выбора файлов для открытия (File Open Picker) и Средство выбора файлов для сохранения (File Save Picker) это то, что делает приложение-поставщика доступным в диалоговом окне, запущенном с помощью Windows.Storage.Pickers.FileOpenPicker и API FileSavePicker. Вызывающее приложение в обоих случаях ничего не знает о приложении, которое может быть запущено – всё взаимодействие между средством выбора файлов и поставщиком происходит с помощью контракта, и брокер контракта ответственен, во-первых, за отображение интерфейса, посредством которого можно выбрать объект, и, во-вторых, за возвращение объекта StorageFile для выбранного элемента.
И для контракта Средство выбора файлов для открытия (File Open Picker), и для контракта Средство выбора файлов для сохранения (File Save Picker), приложение-поставщик отражает в манифесте типы файлов, которые оно может обслуживать. Это выполняется с помощью кнопки Добавить (Add New) на нижеприведенном изображении. Затем средство выбора файлов делает приложение доступным для выбора только тогда, когда вызывающее приложение сообщает о подходящих типах файлов. Параметр Поддерживает все типы файлов (Supports Any File Type), который вы здесь видите позволяет приложению всегда появляться в списке, но это подходит только для приложений, наподобие SkyDrive, которые предоставляют доступ к некоему универсальному хранилищу файлов. Приложения, которые работают лишь со специфическими типами файлов, следует заявлять лишь об этих типах.
Приложение-поставщик содержит разные описания Начальной страницы (Start Page) для контрактов открытия и сохранения – это различные и независимые операции. И в том, и в другом случаях, как мы видели для других контрактов, это те страницы, которые средство выбора файлов загружает, когда пользователь выбирает приложение-поставщик. Как и в случае с целевым приложением для контракта Общий доступ, эти страницы обычно независимы от основного приложения и имеют собственный скриптовый контекст и обработчики активации, как мы увидим в следующем разделе. (Опять же, параметры Исполняемый файл (Executable) и Точка входа (Entry point) предназначены для других языков).
Вы можете задаться вопросом: почему контракты открытия и сохранения файлов разделены? Разве большинство приложений не реализуют обе функции? Не обязательно. Если вы создаёте приложение-поставщик для веб-сервиса, который, фактически, предоставляет данные только для чтения (наподобие изображений в результатах, полученных от поисковой системы), вы можете обслуживать лишь открытие файлов. Если сервис поддерживает создание новых файлов и обновление существующих файлов, как обычно делают сервисы по управлению фотографиями или документами, тогда вам так же нужно обрабатывать сохранение файлов. Возможен и сценарий, где поставщик обслуживает лишь операции сохранения файлов, такие, как запись данных в сервис общего доступа. Коротко говоря, Windows не может предположить того, какова природа источников данных, с которыми будут работать приложения-поставщики, поэтому данные два контракта разделены.
Так как следующий большой раздел данной лекции посвящен контракту Средство обновления кэшированных файлов (Cached File Updater), хорошо бы знать, как он соотносится с другими. Этот контракт позволяет приложению-поставщику синхронизировать локальные и удалённые копии файла, обычно подписываясь на уведомления об изменении или доступе к этим файлам и управляя ими. Это обычно используется для приложений, которые представляют хранилище файлов, в котором пользователь часто открывает и сохраняет файлы, наподобие SkyDrive или приложения баз данных. На самом деле, это сервисы двусторонней привязки данных для файлов, где и локальная и удалённая копии могут быть обновлены независимо. В итоге, эта возможность реализуется совместно с контрактами средств выбора файлов для приложения-поставщика.
) в Центре разработчиков Windows имеет некоторые полезные руководства о том, когда приложения могут быть поставщиками для контракта средства сохранения файлов и когда лучше подходит их настройка в виде целевых приложений общего доступа.
Демонстрация контракта поставщика средства выбор файла – для открытия и сохранения – находится в примере "Контракт Средство выбора файла" (http://code.msdn.microsoft.com/windowsapps/File-picker-app-extension-0cb95155 ), который я, для ясности, буду упоминать как пример поставщика. Декларация и того и другого включена в манифест, с параметром Поддерживает все типы файлов (Supports Any File Type), в итоге, приложение из примера будет перечислена вместе с другими приложениями при всех вызовах средства выбора файла, как показано здесь:
При активации будет загружена начальная страница, указанная в манифесте для соответствующего контракта (открытия или сохранения). Это страницы fileOpenPicker.html и fileSavePicker.html, которые можно найти в корневом разделе проекта. Обе эти страницы загружаются независимо от основного приложения и выглядят так, как показано на рис. 3.1. и рис. 3.2. Обратите внимание на то, что заголовок приложения и цветовая схема определены параметрами закладки Интерфейс приложения (Application UI) в манифесте приложения-поставщика. В частности, текст берется из поля Отображаемое имя (Display Name), а цвета берутся из параметров Текст переднего плана (Foreground Text) и Цвет фона (Background Color) из набора параметров Плитка (Tile), как показано на рис. 3.3. Отметим, что система автоматически добавляет стрелку, направленную вниз после заголовка на рис. 3.1 и рис. 3.2, с его помощью пользователь может выбрать другое расположение или приложение-поставщик.
(рис 3.1) Интерфейс открытия файла, предоставляемый примером
(рис 3.2) Интерфейс сохранения файла, предоставленный примером
(рис 3.3) Установки на закладке Интерфейс приложения (Application UI) в манифесте, которые влияют на внешний вид окон средств открытия и сохранения файлов приложения-поставщика. Серые полосы в изображении представляют собой другие поля, которые я скрыл для краткости
Когда вы впервые запустите этот пример, вы не увидите ни одной из этих страниц. Вместо этого вы увидите страницу, с которой вы можете запустить средство открытия или сохранения файлов и выбрать это приложение в качестве поставщика. Вы можете сделать это, если хотите, но я рекомендую использовать другое приложение для запуска средств работы с файлами, таким образом мы может быть уверены в том, какое приложение какую роль играет. Для этой цели вы можете использовать пример "Средство выбора файлов" (http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba ) (в качестве приложения-потребителя). Вы даже можете использовать что-то вроде приложения Windows 8 Music (Музыка), команда Open File (Открыть файл) на его панели приложения вызовет средство выбора файлов, в интерфейсе которого будет присутствовать и наш пример.
Что бы вы ни выбрали, важная часть примера поставщика – это его раздельные страницы для обслуживания контрактов, то есть, повторюсь, fileOpenPicker.html и fileSavePicker.html. В первом случае, код содержится в js/fileOpenPicker.js, где мы можем обработчик события activated с видом активации fileOpenPicker:
function activated(eventObject) {
if (eventObject.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.fileOpenPicker) {
fileOpenPickerUI = eventObject.detail.fileOpenPickerUI;
eventObject.setPromise(WinJS.UI.processAll().then(function () {
// Перемещение на страницу сценария...
}));
}
}
Здесь ), свойство )) предоставляет средства для выполнения обязанностей поставщика при обслуживании контракта.
Во втором случае код находится в js/fileSavePicker.js, с видом активации fileSavePicker:
function activated(eventObject) {
if (eventObject.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.fileSavePicker) {
fileSavePickerUI = eventObject.detail.fileSavePickerUI;
eventObject.setPromise(WinJS.UI.processAll().then(function () {
// Перемещение на страницу сценария
}));
}
}
Здесь ). Как и в случае с контрактом открытия, его свойство )) предоставляет средства для выполнения обязаностей поставщика при обслуживании контракта.
И в случае активации для открытия файла, и в случае активации для сохранения, содержимое начальной страницы контракта отображается внутри области, сверху и снизу которой расположены панели, предоставленные системой. Если содержимое страницы окажется больше, чем предоставленное пространство, появятся полосы прокрутки, но только в этой области – верхняя и нижняя полосы всегда будут оставаться на одном и том же месте. В обоих случаях WinRT так же предоставляет обычные средства, используемые при активации, такие, как свойства splashScreen и previousExecutionState, подобное мы видели в Главе 3 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", что означает, что вам следует перезагрузить необходимое состояние сеанса и использовать, при необходимости, расширенный экран-заставку.
Однако, гораздо интереснее – это взаимодействие, характерное для контрактов, которое представлено в различных сценариях для этих страниц (как вы можете видеть на рис. 3.1. и рис. 3.2.). Взглянем на каждый из них.
Примечание. Для того, чтобы узнать подробности по проектированию опыта взаимодействия пользователя со средством выбора файлов, обратитесь к материалу "Руководство по средствам выбора файлов) (http://msdn.microsoft.com/library/windows/apps/hh465182.aspx ).
Поставщик открытия файлов работает посредством объекта FileOpenPickerUI, поддерживаемого при виде активации fileOpenPicker. Проще говоря, какой бы пользовательский интерфейс не предоставлял бы провайдер для выбора некоторых файлов или данных, он будет связан с различными методами, свойствами и событиями этого объекта.
Во-первых, интерфейс будет использовать свойство )) для определения того, запущено ли средство выбора файла для выбора одного или нескольких файлов.
Когда пользователь выбирает элемент в интерфейсе, поставщик вызывает метод addFile с объектом StorageFile, который соответствует этому элементу. Очевидно, поставщик должен как-то создать этот объект StorageFile. В средстве для открытия файлов примера, в Сценарии 1, это выполняется с помощью StorageFolder.getFileAsync (где StorageFolder представлено расположением пакета).
Windows.ApplicationModel.Package.current.installedLocation
.getFileAsync("images\\squareTile-sdk.png").then(function (fileToAdd) {
addFileToBasket(localFileId, fileToAdd);
}
Здесь addFileToBasket просто вызывает FileOpenPickerUI.addFile и отображает сообщения для результата. Этот результат является значением из Windows.Storage.Pickers.Provider.AddFileResult: added (успешно добавлено), alreadyAdded (избыточная операция, так как файл уже добавлен), notAllowed (добавление запрещено, по причине несоответствующего типа файла), и unavailable (приложение не видимо). Это, на самом деле, лишь помогает вам сообщить пользователю о результате в вашем интерфейсе. Обратите так же внимание на то, что метод canAddFile может быть полезен для включения или выключения команд добавления в вашем интерфейсе, что позволяет предотвратить некоторые из этих ошибок, просто не позволяя происходить неверным действиям.
Приложение-поставщик так же должно реагировать на запросы, касающиеся удаления ранее добавленных элементов, в случаях, когда пользователь удаляет ранее выбранные файлы из "корзины" в интерфейсе средства выбора файлов, допускающего выбор нескольких файлов. Для того, чтобы это сделать прослушивайте событие fileRemoved объекта FileOpenPickerUI, которое, в качестве аргумента, предоставляет ID файла. Вы передаёте этот ID в containsFile, за которым следует removeFile, как это сделано в примере (js/fileOpenPickerScenario1.js):
// Подключает события в коде инициализации страницы
fileOpenPickerUI.addEventListener("fileremoved", onFileRemovedFromBasket, false);
function removeFileFromBasket(fileId) {
if (fileOpenPickerUI.containsFile(fileId)) {
fileOpenPickerUI.removeFile(fileId);
}
}
Если вам нужно знать, когда интерфейс средства выбора файла закрывает вашу страницу (как происходит, когда пользователь нажимает на кнопку Open (Открыть) или Cancel (отменить), как показано на рис. 3.1), прослушивайте событие closing. Это даёт вам возможность закрыть любые сеансы, которые были открыты при взаимодействии с онлайновыми сервисами и выполнить любые необходимые задачи по очистке. В eventArgs вы можете найти свойство isCanceled, которое показывает, был ли отменен сеанс работы со средством выбора файла (значение true), или интерфейс был закрыт кнопкой Open (Открыть) (false). Объект eventArgs.closingOperation так же содержит метод getDeferral и свойство deadline, что позволяет вам производить, так же, асинхронные операции, это похоже на то, что показано в Главе 3 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript" для события suspending.
Последнее замечание касается того, что средство выбора файлов должно учитывать FileOpenPickerUI.settingsIdentifier для перезапуска приложения-поставщика и приведения его в предыдущее состояние (то есть, к состоянию предыдущего сеанса работы с файлами). Если вы помните о другой стороне этого процесса, то приложение, которое использует средство выбора файла может использовать settingsIdentifier для различения внутри себя различных вариантов использования, возможно – для различения определенных типов файла или особенностей контекстов. Идентификатор так же может отличаться у различных приложений, которые запускают средство выбора файла. Таким образом, учитывая это свойство, приложение-поставщик может управлять контекстом, зависящим от каждого конкретного случая его активации (обычно, используя settingsIdentifier в именах файлов в пакете приложения и в именах контейнеров параметров), именно так работают встроенные средства выбора файлов для работы с файловой системой.
Так же приложение-поставщик может быть приостановлено, отображая свой пользовательский интерфейс, и, возможно, что оно может быть остановлено, если вызывающее приложение будет закрыто. Однако, если вы управляете состоянием средства выбора файлов на основе использования значений settingIdentifier, вам не нужно сохранять или управлять другими состояниями сеанса, которые касаются функциональности средства выбора файлов.
В основном, Сценарий 2 варианта средства открытия файлов в примере поставщика очень похож на то, что мы видели в предыдущем разделе. Единственное различие заключается в том, как создать StorageFile из источника, который не является файлом, как, например, из изображения, которое получено по удалённому URI. В этой ситуации нам нужно получить поток данных для удалённого URI и конвертировать этот поток в объект StorageFile. К счастью, несколько API WinRT серьезно упрощают эту задачу, как показано в js/fileOpenPickerScenario2.js, в методе onAddFileUri:
function onAddUriFile() {
// Ответ на нажатие кнопки Add (Добавить)
var imageSrcInput = document.getElementById("imageSrcInput");
if (imageSrcInput.value !== "") {
var uri = new Windows.Foundation.Uri(imageSrcInput.value);
var thumbnail =
Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(uri);
// Получение файла по URI для добавления в корзину средства выбора файлов
Windows.Storage.StorageFile.createStreamedFileFromUriAsync("URI.png", uri, thumbnail).then(function (fileToAdd) {
addFileToBasket(uriFileId, fileToAdd);
},
function (error) {
// ...
});
} else {
// ...
}
}
Здесь Windows.Storage.StorageFile.createStreamedFileFromUriAsync выполняет всё необходимое для того, чтобы дать нам StorageFile для URI, а addFileToBasket это внутренний метод, который просто вызывает метод addFile method объекта FileOpenPickerUI.
Заметьте, что если вам нужно произвести проверку подлинности или выполнить какие-то другие особые шаги для получения содержимого с веб-сервиса, вам обычно нужно использовать API ) для того, чтобы получить это содержимое (здесь вы можете задать учетные данные), за ним последует StorageFile.createStreamedFile для того, чтобы передать полученный файл через контракт. StorageFile.createStreamedFileFromUriAsync выполяет то же самое, но не предусматривает проверку подлинности.
Так же, как поставщик открытия файлов взаимодействует с объектом FileOpenPickerUI, поставщик для сохранения файлов работает со специальными методами, свойствам и событиями класса FileSavePickerUI. Повторюсь, контракты открытия и сохранения имеют дело с источниками данных, для которых вы можете создать приложение-поставщик, и которые могут поддерживать или не поддерживать операции сохранения независимо от операций открытия. Если вы поддерживаете и то и другое, вы, вероятно, повторно используете тот же пользовательский интерфейс и используете ту же начальную страницу и путь активации.
В классе FileSavePickerUI у нас, для начала, имеется свойство allowedFileTypes, соответствующее тому, что предоставлено приложением, которое активирует интерфейс средства сохранения файла. Как и в случае с открытием, вы будете использовать это свойство для фильтрации того, что будет отображено в вашем собственном интерфейсе, таким образом пользователь может точно видеть, какие элементы заданных типов уже существуют. Так же при этом обычно заполняют выпадающий список типов файлов теми же типами файлов.
Для восстановления сохраненного в предыдущей сессии состояния интерфейса поставщика для конкретных вызывающих приложений, опять же, можно использовать свойство settingsIdentifier.
Возвращаясь к рис. 3.2., обратите внимание на элементы управления вдоль нижней стороны экрана, они автоматически предоставляются интерфейсом средства выбора файлов, когда запускается приложение-поставщик. Когда пользователь меняет поле, содержащее имя файла, приложение-поставщик может прослушать и обработать событие ), которое может принимать значения succeeded, notAllowed (обычно при неподходящем типе файла), или unavailable. Обычно это используется, когда пользователь касается элемента в списке, ожидаемое поведение системы в таком случае заключается в установке имени файла в значение имени выбранного элемента.
Самое важное событие, конечно, происходит, когда пользователь, в итоге, нажимает на кнопку Save (Сохранить). Это действие вызывает событие ) объекта FileSavePickerUI. Вы должны предоставить обработчик для этого события, в котором вы должны создать пустой объект StorageFile, в котором приложение, которое активировало интерфейс средства сохранения файлов сможет сохранить свои данные. Имя этого StorageFile должно совпадать со свойством fileName.
Свойство ). В его свойство targetFile помещают созданный StorageFile (или null, если произошла ошибка). Вы должны установить это свойство, прежде чем вернетесь из обработчика события, но, конечно, для того, чтобы это сделать вам может понадобиться асинхронная операция. Для этой цели, как мы видели уже много раз, запрос так же содержит метод getDeferral. Он использован в Сценарии 1 примера поставщика, при сохранении файла (js/fileSavePickerScenario1.js):
function onTargetFileRequested(e) {
var deferral = e.request.getDeferral();
// Создаём файл для передачи его средству работы с файлами
Windows.Storage.ApplicationData.current.localFolder.createFileAsync(
fileSavePickerUI.fileName).done(function (file) {
// Присваиваем полученный файл свойству targetFile и завершаем отложенную операцию
e.request.targetFile = file;
deferral.complete();
}, function () {
// Устанавливаем targetFile в null и завершаем отложенную операцию для того,
// чтобы показать, что произошла ошибка
e.request.targetFile = null;
deferral.complete();
});
};
В вашем приложении, конечно, замените вызов createFileAsync в локальной папке любыми необходимыми шагами для создания файла или объекта данных. Если, с другой стороны, речь идёт об удалённых файлах, вам нужно задействовать контракт Обновление кэшированных файлов (Cached File Updater) (смотрите соответствующий раздел ниже).
Сценарий 2 интерфейса сохранения файлов примера поставщика показывает еще один аспект этого процесса: отображение сообщений об ошибках, если возникают ошибки при создании необходимого StorageFile. Вообще говоря, вы, для того, чтобы сообщить пользователю о том, что ему нужно сделать, можете использовать любой интерфейс, который кажется вам подходящим и соответствующим стилю приложения. В примере использован MessageDialog:
function onTargetFileRequestedFail(e) {
var deferral = e.request.getDeferral();
var messageDialog = new Windows.UI.Popups.MessageDialog("If the app needs the user to
correct a problem before the app can save the file, the app can use a message like this to
tell the user about the problem and how to correct it.");
messageDialog.showAsync().done(function () {
// Устанавливаем свойство targetFile в null и завершаем отложенную операцию для того,
// чтобы показать, что произошла ошибка, как только пользователь закроет диалоговое окно.
// Это позволит пользователю выполнить необходимые исправления и затем снова нажать на кнопку
// Save (Сохранить).
e.request.targetFile = null;
deferral.complete();
});
};
Использование контракта средства обновления кэшированных файлов предназначено для синхронизации локальных копий файла с копиями на удалённых ресурсах, которыми управляет приложение-поставщик. Данный контракт специально предназначен для приложений, которые предоставляют доступ к хранилищу, куда пользователь регулярно сохраняет файлы, откуда он их открывает и обновляет их содержимое. Приложение SkyDrive в Windows – это хороший пример подобного взаимодействия. В других случаях, когда пользователь обычно пользуется средством выбора файлов для получения файлов и использования их каким-либо образом, но не возвращается к ним, использование контрактов средств выбора файлов полностью оправданно.
Если взглянуть на Главу 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript", мы увидим там некоторые вызовы методов, выполненные приложением, которое использует средство выбора файлов: ). Проще говоря, это те вызовы, которые делает приложение-потребитель файлов, если и когда оно выполняет запись в файлы, которое оно получило от средства выбора файла. Оно делает это потому что не узнает (и ему не следует об этом беспокоиться), есть ли у поставщика файла другая копия файла в базе данных, на веб-сервисе, и так далее, с которой нужно синхронизировать данный файл. Если приложение-поставщик нуждается в обработке синхронизации, вызовы приложением-приемником этих методов будут вызывать необходимый интерфейс средства обновления кэшированных файлов приложения-поставщика, который может быть показан, а может и нет, в зависимости от конкретной ситуации. Даже если приложение-потребитель не вызывает эти методы, приложение-поставщик будет получать оповещения об изменениях, но не сможет показать какой-либо пользовательский интерфейс.
Этот контракт работает с двумя направлениями, в зависимости от того, нужно ли ему обновить локальную (кэшированную) копию файла, или удалённую (исходную) копию. В первом случае, поставщик запрашивает обновление локальной копии, обычно когда приложение-потребитель пытается получить доступ к файлу (берет его из списков ); оно не запрашивает обновление напрямую). Во втором случае приложение-потребитель модифицирует файл, поэтому поставщику нужно передать изменения в исходную копию.
С точки зрения приложения-поставщика, необходимость в обновлениях возникает, когда оно предоставляет файл другому приложению. Это может произойти с помощью контрактов средств выбора файлов , как мы видели в предыдущем разделе, но это может случиться и через сопоставление типов файлов, а так же посредством контракта общего доступа. В последнем случае приложение-источник данных для общего доступа, в известном смысле, является поставщиком файла и может так же использовать контракт средства обновления кэшированных файлов. Коротко говоря, если вам нужно, чтобы приложение-поставщик файлов смогло отслеживать и синхронизировать обновления между локальной и удалённой копиями файла, ему нужно использовать этот контракт.
Поддержка контракта начинается с объявления в манифесте, как показано ниже, где Начальная страница (Start page) указывает на страницу, реализующую интерфейс средства обновления кэшированных файлов. Эта страница обрабатывает необходимые для обновления файлов события и может быть показана, а может и не быть показана пользователю, как мы увидим позже.
Следующий шаг поставщика заключается в том, чтобы показать, когда данный ) для предоставленного файла, как показано в Сценарии 3 примера "Контракт Средство выбора файла" (http://code.msdn.microsoft.com/windowsapps/File-picker-app-extension-0cb95155 ), который я снова, упоминать как пример поставщика (js/fileOpenPickerScenario3.js):
function onAddFile() {
// Ответ на нажатие кнопки Add (Добавить)
Windows.Storage.ApplicationData.current.localFolder.createFileAsync("CachedFile.txt",
Windows.Storage.CreationCollisionOption.replaceExisting).then(function (file) {
Windows.Storage.FileIO.writeTextAsync(file, "Cached file created...").then(
function () { Windows.Storage.Provider.CachedFileUpdater.setUpdateInformation(
file, "CachedFile",
Windows.Storage.Provider.ReadActivationMode.beforeAccess,
Windows.Storage.Provider.WriteActivationMode.notNeeded,
Windows.Storage.Provider.CachedFileOptions.requireUpdateOnAccess);
addFileToBasket(localFileId, file);
}, onError);
}, onError);
};
Примечание. setUpdateInformation находится в пространстве имен Windows.Storage.Provider и отличается от объекта Windows.Storage.CachedFileManager, который используется с другой стороны контракта. Будьте внимательны и не перепутайте их.
Метод setUpdateInformation принимает следующие аргументы:
StorageFile для файла.requireUpdateAccess (обновление при доступе к локальному файлу), useCachedFileWhenOffline (будет осуществлено обновление при доступе, если это запросит вызывающее приложение, доступ разрешается, если нет сетевого соединения), и denyAccessWhenOnline (запускает обновление при попытке доступа и требует сетевое соединение).С помощью этого вызова, другими словами, поставщик управляет тем, как и когда ему следует активироваться для обработки обновления, когда осуществляется доступ к локальному файлу.
Итак, всего у нас есть два случая, когда приложение-поставщик может быть активировано и у него может быть запрошен показ пользовательского интерфейса: первый случай – когда вызывающее приложение обновляет файл, и второй – когда вызывающее приложение пытается получить доступ к файлу, но нуждается в обновлении перед чтением его содержимого.
Прежде чем переходить к техническим подробностям, давайте посмотрим, как всё это выглядит для пользователя. Для того, чтобы увидеть средство обновления кэшированных файлов в действии с использованием примера, активируйте его с использованием средства выбора файлов из другого приложения. Для начала, однако, запустите пример поставщика для того, чтобы удостовериться в том, что его контракты зарегистрированы в системе. Затем запустите вышеупомянутый пример "Средство выбора файлов" (http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba ). В нём, Сценарии 4, 5 и 6 вызывают взаимодействие с контрактом обновления кэшированных файлов. Сценарии 4 и 6 осуществляют запись в файл для вызова обновления удалённой копии. Сценарий 5 получает доступ к локальному файлу, что вызывает обновление локального файла как часть этого процесса.
В Сценарии 5 (обновление локального файла), начните с прикосновения к кнопке Выбрать локальный файл (Pick Cached File) в пользовательском интерфейсе, показанном здесь:
Это запустит пример поставщика. На данном экране, выберите Сценарий 3, в итоге, вы увидите интерфейс, показанный на рис. 3.4. Это режим примера поставщика, который отображает средство выбора файлов поставщика (js/fileOpenPickerScenario3.js), где он вызывает setUpdateInformation. Это еще не интерфейс для средства обновления кэшированных файлов. Щелкните кнопку Add File to Basket (Добавить файл в корзину), и коснитесь кнопки Open (Открыть). Это вернет вас в первое приложение (к примеру, средства выбора файлов на вышеприведенном рисунке), где теперь будет активна кнопка Output Latest Version (Вывести самую свежую версию). Прикосновение к этой кнопке теперь активирует например поставщика с помощью контракта средства обновления кэшированных файлов, как показано на рис. 3.5. Это происходит, когда нужно обновить локальную копию кэшированного файла.
(рис 3.4) Интерфейс примера поставщика для выбора файла. Для предоставленного файла вызван метод setUpdateInformation, для того, чтобы настроить взаимоотношения со средством обновления кэшированных файлов
(рис 3.5) Интерфейс для контракта обновления кэшированных файлов из примера поставщика, для локального файла
Обратите внимание на описания в примере. В то время, как пример показывает интерфейс по умолчанию, интерфейс средства обновления кэшированных файлов не отображается до тех пор, пока не нужно будет разрешить конфликт или запросить идентификационные данные. Часто в подобном взаимодействии нет необходимости и поставщик незаметно предоставляет обновление локального файла или показывает, что файл находится в актуальном состоянии. Интерфейс примера просто предоставляет обе эти возможности для явного выбора (и не забудьте выбрать одну из них, так как выбор Cancel (Отмена) приведет к выдаче исключения).
В Сценарии 6 (обновление удалённого файла) примера средства выбора файла, мы можетм видеть взаимодействие, которое присутствует, когда приложение-потребитель вносит изменения в свою локальную копию, таким образом, вызывая обновление удалённой копии. Начнём с прикосновения к кнопке Get Save File (Выполнить сохранение файла) в окне, показанном ниже:
В средстве выбора файлов, выберите пример поставщика как источник, что приведет к запуску интерфейса, показанного на рис. 3.6. посредством контракта средства сохранения файла, реализованного в html/fileSavePickerScenario3.html и js/fileSavePickerScenaro3.js. Если вы посмотрите в файл JavaScript, вы увидите вызов setUpdateInformatio, который осуществляется, когда вы вводите имя файла и нажимаете Сохранить (Save). Выполнение этого действия возвращает вас в пример средства работы с файлами выше и теперь должна быть доступна кнопка Запись в файл (Write To File). Прикосновение к этой кнопке повторно запускает пример поставщика посредством контракта обновления кэшированных файлов с пользовательским интерфейсом, показанным на рис. 3.7. Этот интерфейс предназначен для демонстрации того, как приложение-поставщик может обработать перезапись или переименование удалённого файла.
(рис 3.6) Интерфейс примера поставщика для сохранения файла. Метод setUpdateInformation снова вызывается для предоставленного файла, для того, чтобы настроить взаимоотношения со средством обновления кэшированных файлов
(рис 3.7) Интерфейс для контракта обновления кэшированных файлов из примера поставщика, для удалённого файла
Давайте посмотрим, как контракт обновления кэшированных файлов выглядит в коде. Как вы можете ожидать, приложение-поставщик запускается, загружается начальная страница (cachedFileUpdater.html в корневом разделе проекта), обработчик ), который содержит свойство )), вместе с обычным набором из kind, previousExecutionState, и splashScreen. Вот как это выглядит в файле js/cachedFileUpdater.js примера поставщика:
function activated(eventObject) {
if (eventObject.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.cachedFileUpdater) {
cachedFileUpdaterUI = eventObject.detail.cachedFileUpdaterUI;
cachedFileUpdaterUI.addEventListener("fileupdaterequested", onFileUpdateRequest);
cachedFileUpdaterUI.addEventListener("uirequested", onUIRequested);
switch (cachedFileUpdaterUI.updateTarget) {
case Windows.Storage.Provider.CachedFileTarget.local:
// Код опущен: настраивает пример для показа cachedFileUpdaterScenario1
// при необходимости.
break;
case Windows.Storage.Provider.CachedFileTarget.remote:
// Код опущен: настраивает пример для показа cachedFileUpdaterScenario2
// при необходимости.
break;
}
}
}
Когда приложение-поставщик запускается для обновления локального файла из удалённого источника, свойство cachedFileUpdaterUI.updateTarget будет иметь значение local, как вы можете видеть выше. Кода у приложения запрашивается обновление удаленного файла с использованием локальных изменений, цель будет установлена в значение remote. Всё, что пример выполняет в этих случаях – это указывает либо на html/cachedFileUpdaterScenario1.html (рис. 3.5.) либо на html/cachedFile-UpdaterScenario2.html (рис. 3.7) как на пользовательский интерфейс для обновления.
Интерфейс, на самом деле, не показывается сразу. В первую очередь объект ) для выполнения попытки незаметного обновления. Здесь )), объектом, который сохраняют в переменной, которая доступна из интерфейса обновления.
Если есть возможность незаметно обновить локальный файл, выполните следующее:
request.getDeferral.currentlyUnavailable (удаленная версия файла недоступна, и локальная версия недоступна), failed (синхронизация невозможна ни сейчас, ни в будущем, так как удалённая версия файла удалена), и completeAndRenamed (исходная версия файла была переименована, обычно – для разрешения конфликтов).
complete для того, чтобы завершить обновление.В итоге, теперь поставщик может знать заранее, что он не может выполнить незаметное обновление вовсе – пользователь может быть не авторизованным на удалённом сервисе (или учетные данные нужны каждый раз), может быть конфликт, требующий разрешения и так далее. В подобных случаях обработчик события должен проверить значение )) и соответствующим образом установить свойство request.status:
visible, переключиться к этому интерфейсу и вернуться из обработчика события. Отложенная операция завершится, когда пользователь введет нужные данные посредством пользовательского интерфейса.hidden, установить request.status в значение userInputNeeded и вернуться. Это вызовет событие CachedFileUpdaterUI.onuiRequested , за которым последует другое событие fileUpdateRequested, где uiStatus будет иметь значение visible, в таком случае вы переключитесь на ваш пользовательский интерфейс.unavailable, установить request.status в значение currentlyUnavailable.Вы можете найти кое-что из этого в обработчике onFileUpdateRequest примера. Он, на самом деле, обрабатывает лишь проверку uiStatus, так как не пытается выполнить незаметное обновление (как описано ниже, в комментариях):
function onFileUpdateRequest(e) {
fileUpdateRequest = e.request;
fileUpdateRequestDeferral = fileUpdateRequest.getDeferral();
// Попытка незаметного обновления с использованием fileUpdateRequest.file , или вызов
// fileUpdateRequest.updateLocalFile в локальном случае, setting fileUpdateRequest.status
// соответственно, затем вызов fileUpdateRequestDeferral.complete(). В противном случае, если вы
// знаете, что понадобится действие пользователя, исполнение следующего кода.
switch (cachedFileUpdaterUI.uiStatus) {
case Windows.Storage.Provider.UIStatus.hidden:
fileUpdateRequest.status =
Windows.Storage.Provider.FileUpdateStatus.userInputNeeded;
fileUpdateRequestDeferral.complete();
break;
case Windows.Storage.Provider.UIStatus.visible:
// Переключение на интерфейс обновления (настроено в событии activated)
var url = scenarios[0].url;
WinJS.Navigation.navigate(url, cachedFileUpdaterUI);
break;
case Windows.Storage.Provider.UIStatus.unavailable:
fileUpdateRequest.status = Windows.Storage.Provider.FileUpdateStatus.failed;
fileUpdateRequestDeferral.complete();
break;
}
}
Опять же, если произойдет незаметное обновление, пользовательский интерфейс приложения-поставщика никогда не будет показан пользователю. В случае с примером поставщика, так как незаметное обновление не происходит никогда, он всегда проверяет ), сообщая приложению-поставщику, что система делает пользовательский интерфейс видимым. Приложение, на самом деле, может отложить инициализацию своего пользовательского интерфейса до возникновения этого события, так как он не выполняет никаких функций при незаметном обновлении.
После этого событие fileUpdateRequested снова будет вызвано с uiStatus, теперь установленным в значение visible. Отметим, как вышеприведенный код будет вызывать в этом случае request.getDeferral но не будет вызывать его метод complete.
Мы откладывает этот шаг до того момента, когда работа с пользовательским интерфейсом приведет к нужному результату (и, на самом деле, мы оставляем и запрос и отложенный вызов для использования из кода пользовательского интерфейса).
Пользовательский интерфейс обновления данных ответственен за приём любых данных от пользователя, необходимых для выполнения задачи: пользователь может вводить учетные данные, указывать, какую из копий файлов следует сохранить (локальную или удаленную), разрешать переименование конфликтующего файла (при обновлении удалённого файла) и так далее. При обновлении локального файла, осуществляется запись в StorageFile в request.file или вызывается request.updateLocalFile; в случае с обновлением удаленного файла, данные из локальной копии копируются в request.file.
Для того, чтобы завершить обновление, код интерфейса устанавливает request.status в значение complete (или, если произошёл сбой, в другое подходящее значение) и вызывает метод complete отложенной операции. Это изменит статус кнопок, предоставленных системой, расположенных у нижней части экрана, как вы можете видеть на рис. 3.5. и рис. 3.7., а именно, активирует кнопку OK и деактивирует Cancel (Отмена). В примере поставщика обе кнопки просто выполняют эту пару строк кода для этой цели:
fileUpdateRequest.status = Windows.Storage.Provider.FileUpdateStatus.complete;
fileUpdateRequestDeferral.complete();
В целом, взаимодействие между системой и приложением при реализации контракта средства обновления кэшированных файлов выглядит довольно простым и понятным: обработка событий, копирование данных туда, куда нужно и обновление статуса запроса. Настоящая работа по реализации этого контракта заключается в том, чтобы сначала решить, когда вызывать setUpdateInformation и затем предоставить пользовательский интерфейс для поддержки обновления локальных или удаленных файлов при определенных обстоятельствах. Это, конечно, включит в себя интенсивное взаимодействие с вашей серверной системой хранения.
Последний контракт, который мы рассмотрим в этой лекции (ух ты!), это средство выбора контактов. Мы еще не видели этого средства в действии в Windows 8. Поэтому посмотрим на него для начала, а потом разберемся, как это средство используется с одной стороны контракта, и как приложение-поставщик удовлетворяет нужды другой стороны.
Контакт, или контактные сведения, как вы, вероятно, ожидаете, это информация о человеке, которая включает в себя подробности наподобие имени, номера телефона, адреса электронной почты и так далее. Очевидное место, где вам понадобится контакт – это составление электронного письма, как показано на рис. 3.8. Здесь, при прикосновении к элементу управления "+" в правой части полей To (Кому) и Cc (Копия), будет открыто средство выбора контактов, которое по умолчанию относится к приложению Peoples (Люди) Windows 8, как показано на рис. 3.9. (экран-заставка), и на рис. 3.10 (вид в режиме множественного выбора, где я размыл сведения о моих друзьях, и они не станут обвинять меня за привлечение к ним нежелательного внимания!). Как мы видели с интерфейсом средства выбора файлов, приложение-поставщик поддерживает пользовательский интерфейс для центральной части экрана, в то время как Windows поддерживает верхнюю и нижнюю части, заголовок и элемент меню в виде стрелки, направленной вниз, используя информацию из манифеста приложения-поставщика (обратиесь к рис. 3.3). На рис. 3.11. вы можете видеть внешний вид приложения из примера "Приложение для выбора контактов" (http://code.msdn.microsoft.com/windowsapps/Contact-Picker-App-sample-fc6677a1 ) в режиме поставщика, так же как и меню, которое позволяет вам выбирать разных поставщиков (те приложения, которые объявили себя в качестве поставщиков контактов)
Когда я выбираю один или большее количество контактов в любом приложении-поставщике и нажимаю на кнопку Select (Выбрать) в нижней части экрана, эти контакты поступают в первое приложение, в данном случае это Mail (Почта).
Так же, как контракт средства выбора файлов позволяет перемещаться по данным, представленным в виде файлов другим приложением, контракт контактов (скажите это быстро раз десять!) позволяет пользователю просто перемещаться по сведениям о людях, которые вы можете выбирать из любого другого источника.
(рис 3.8) Приложение Mail (Почта) использует средство выбора контактов для выбора получателей
(рис 3.9) Приложение Peoples (Люди), запускающееся в качестве поставщика контактов
(рис 3.10) Пользовательский интерфейс выбора внутри приложения Peoples (Люди), показанный при множественном выделении (сведения о моих друзьях здесь размыты, так как они обычно не ищут славы среди разработчиков). Выделенные объекты собраны в нижней части, в корзине элементов
(рис 3.11) Пользовательский интерфейс примера выбора контактов, когда приложение используется в качестве поставщика, вместе со всплывающим меню в заголовке, которое позволяет выбирать поставщика
Контакты, в целом, используют API в пространстве имен ), свойства которого, такие как name, phoneNumbers, locations, emails, instantMessages, и customFields, вместе с методами getThumbnailAsync и queryCustomFields, предоставляют вам контактную информацию. Выбор контакта осуществляется через интерфейс средства выбора контактов, который очень похож на интерфейс средства выбора файла. Интерфейс запускается с помощью ). После создания экземпляра данного объекта, вы можете установить подпись commitButtonText для первой (левой) кнопки в интерфейсе (как "Select" (Выбор) на предыдущих рисунках). Так же вы можете установить свойство selectionMode в одно из значений перечисления ContactSelectionMode: либо в contact (по умолчанию) либо в fields. В первом случае, возвращается полная контактная информация, в последнем, средство выбора работает с содежимым свойства desiredFields ( http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.contacts.contactpicker.desiredfields.aspx). Обратитесь к документации по данному свойству для того, чтобы узнать подробности.
Когда вы готовы к показу интерфейса, вызовите метод средства выбора контактов pickSingleContactAsync или pickMultipleContactsAsync. Они предоставляют вам обработчик завершения для одного объекта ContactInformation или для вектора из них, соответственно. Как и в случае со средством выбора файлов, обратите внимание на то, что эти API выдают исключения, если вызываются, когда приложение находится в прикрепленном режиме просмотра, очевидно, вы постараетесь избежать подобной ситуации.
Выбор и отображение отдельного контакта показан в Сценарии 1 примера "Приложение для выбора контактов" ( http://code.msdn.microsoft.com/windowsapps/Contact-Picker-App-sample-fc6677a1):
var picker = new Windows.ApplicationModel.Contacts.ContactPicker();
picker.commitButtonText = "Select";
// Открывает для пользователя средство выбор контакта
picker.pickSingleContactAsync().done(function (contact) {
if (contact !== null) {
// Получает информацию о контакте...
}
});
Выбор нескольких контактов (Сценарий 2, js/scenarioMultiple.js) работает похожим образом, лишь использует pickMultipleContactsAsync. В любом случае, вызывающее приложение затем применяет данные ContactInformation так, как ему нужно, например, заполняет поля To (Кому) и Cc (Копия) в приложении Mail (Почта). Однако, свойства этого объекта, помимо name, которое является обычной строкой, обладают некоторой структурой, как показано в следующей таблице.
| Свойство | Тип | Свойства и типы полей |
|---|---|---|
| emails phoneNumbers | Вектор ContactField |
category (ContactFieldCategory), name (строка), type (типа ContactFieldType), value (строка) |
| instantMessages | Вектор ContactInstantMessageField |
То же самое, что и ContactField выше, плюс - displayText, launchUri,service, и userName (все имеют строковой тип) |
| locations | Вектор ContactLocationField |
То же, что и ContactField выше, плюс city, country, postalCode, region, street, и unstructuredAddress (все имеют строковой тип) |
Таким образом, пример принимает объект ContactInformation следующим образом, сначала извлекая индивидуальные свойства вектора:
appendFields("Emails:", contact.emails, contactElement);
appendFields("Phone Numbers:", contact.phoneNumbers, contactElement);
appendFields("Addresses:", contact.locations, contactElement);
И затем перечисляя содержимое этих векторов и в этом случае создавая элементы с их содержимым. Другие приложения, конечно, будут перемещать значения в подходящие поля или в другие части интерфейса приложения, то, что показано здесь, демонстрирует обработку различных категорий:
function appendFields(title, fields, container) {
// Создаёт интерфейс для списка полей контактатого же типа, например, для
// адресов электронной почты или телефонов
fields.forEach(function (field) {
if (field.value) {
// Присоединяет title только если имеется не пустое поле контакта
if (title) {
container.appendChild(createTextElement("h4", title));
title = "";
}
// Отображает категорию около значения поля
switch (field.category) {
case Windows.ApplicationModel.Contacts.ContactFieldCategory.home:
container.appendChild(createTextElement("div", field.value + " (home)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.work:
container.appendChild(createTextElement("div", field.value + " (work)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.mobile:
container.appendChild(createTextElement("div", field.value + " (mobile)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.other:
container.appendChild(createTextElement("div", field.value + " (other)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.none:
default:
container.appendChild(createTextElement("div", field.value));
break;
}
}
});
}
Со стороны поставщика, что так же показано в примере "Приложение для выбора контактов" ( http://code.msdn.microsoft.com/windowsapps/Contact-Picker-App-sample-fc6677a1) мы видим тот же шаблон, что и для поставщиков средства выбора файла. Во-первых, приложению-поставщику нужно объявить контракт Выбор контактов (Contact Picker) в манифесте, где задаётся Начальная страница (Start page) для загрузки в контексте средства выбора контактов. В примере начальная страница – это contactPicker.html, которая, в свою очередь, загружает html/contactPickerScenario.html (со связанными с ней файлами JavaScript):
Как и в случае со средством выбора файлов, отдельная начальная страница подразумевает наличие отдельного обработчика активации, в данном случае он ищет вид активации contactPicker (js/contactPicker.js):
function activated(eventObject) {
if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.contactPicker)
{ contactPickerUI = eventObject.detail.contactPickerUI;
eventObject.setPromise(WinJS.UI.processAll().then(function () {
// ...
}));
}
}
Здесь eventObject.detail является объектом ContactPickerActivatedEventArgs (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.activation.contactpickeractivatedeventargs.aspx) (эти имена длинные, но, по крайней мере, предсказуемые!). Как и в случае со всеми соыбытиями активации, он содержит свойства kind, previousExecutionState, и splashScreen для обычных целей. Его свойство contactPickerUI, типа ContactPickerUI (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.contacts.provider.contactpickerui.aspx), в свою очередь, содержит информацию, специфичную для контракта средства выбора контакта:
selectionMode и desiredFields предоставляются вызывающим приложением.addContact, removeContact, и containsContact —для управления тем, что будет возвращено в вызываемое приложение. Эти методы соответствуют действиям типичного пользовательского интерфейса для выбора объектов.contactsRemoved, которое сообщает поставщику, когда пользователь удаляет элемент из корзины объектов, которая расположена вдоль нижней части экрана (вернитесь к рис. 3.10).Внутри поставщика каждый контакт представлен объектом Windows.ApplicationModel.Contacts.Contact. Поставщик создаст объект для каждого предоставляемого им контакта. В примере (js/contactPickerScenario.js), есть массив, который называется sampleContacts и имитирует то, что обычно поступает из базы данных. Этот массив содержит простые JSON-записи, вроде этих:
{
name: "David Jaffe",
homeEmail: "david@contoso.com",
workEmail: "david@cpandl.com",
workPhone: "",
homePhone: "248-555-0150",
mobilePhone: "",
address: {
full: "3456 Broadway Ln, Los Angeles, CA",
street: "",
city: "",
state: "", zipCode: ""
},
id: "761cb6fb-0270-451e-8725-bb575eeb24d5"
},
Каждая запись представлена в интерфейсе примера в виде поля с флагом (сгенерировано функцией createContactUI), что является быстрым и простым способом отображения списка элементов, поддерживающего выбор! Конечно, ваше приложение-поставщик, вероятнее всего, будет использовать для этих целей ListView. В примере лишь сделана попытка сделать всё как можно более простым, чтобы вы могли видеть, что происходит с самим контрактом.
Когда пользователь выбирает контакт, вызывается функция примера addContactToBasket. Это тот самый момент, когда мы создаём реальный объект Contact и вызываем ContactPickerUI.addContact. За обработкой каждого поля следует цепочка вызовов других функций, поэтому посмотрим, как это работает для одного поля homeEmail в записи источника, начиная с addContactToBasket (опять же, в js/contactPickerScenario.js). Оставшиеся значения полей обрабатываются, в значительной мере, тем же способом.
function addContactToBasket(sampleContact) {
var contact = new Windows.ApplicationModel.Contacts.Contact();
contact.name = sampleContact.name;
appendEmail(contact.fields, sampleContact.homeEmail, Windows.ApplicationModel.Contacts.ContactFieldCategory.home);
// добавить другие поля...
// Добавить контакт в корзину
switch (contactPickerUI.addContact(sampleContact.id, contact)) {
// Показать различные сообщения, основанные на результате, который имеет тип
// Windows.ApplicationModel.Contacts.Provider.AddContactResult
}
}
Как вы можете видеть, поле homeEmail передается в функцию, которая называется appendEmail, первый аргумент которой – это вектор (Contact.fields), в который добавляются поля, а третий параметр – это категория (home). Затем эти данные передаются другой функции общего назначения, appendField, где типы полей объединяются, все они используются для создания объекта ContactField, и он добавляется к контакту:
function appendEmail(fields, email, category) {
// Добавляет новый адрес электронной почты к вектору полей контакта
appendField(fields, email,
Windows.ApplicationModel.Contacts.ContactFieldType.email, category);
}
function appendField(fields, value, type, category) {
// Добавляет новое поле нужного типа, либо адрес электронной почты, либо номер телефона
if (value) {
fields.append(new Windows.ApplicationModel.Contacts.ContactField(value,
type, category));
}
}
Коротко говоря, это главным образом касается того, как собраны все поля в контакте, одно за один раз.
Теперь, когда с элемента в списке снято выделение, нужно удалить его из корзины:
function removeContactFromBasket(sampleContact) {
// Программным путём удаляет контакт из корзины элементов
if (contactPickerUI.containsContact(sampleContact.id)) {
contactPickerUI.removeContact(sampleContact.id);
}
}
Похожим образом, когда пользователь удаляет элемент из корзины, поставщик контакта нуждается в обновлении интерфейса, отражающего выделение контактов, что выполняется путём обработки события contactremoved:
contactPickerUI.addEventListener("contactremoved", onContactRemoved, false);
function onContactRemoved(e) {
// Добавьте любой код, который вызывается, когда пользователь удаляет контакт из корзины
var contactElement = document.getElementById(e.id);
var sampleContact = sampleContacts[contactElement.value];
contactElement.checked = false;
}
Вы могли заметить, что мы ничего не сказали о закрытии пользовательского интерфейса, и, на самом деле объект ContactPickerUI не имеет событий для этого. Проще говоря, когда пользователь нажимает на кнопку подтверждения (с любым текстом, предоставленным вызывающим приложением), он получает всё, что поставщик добавил в корзину. Если пользователь нажмёт на кнопку отмены, операция возвратит контакт null. В обоих случаях, приложение-поставщик будет приостановлено, если оно не было запущено до того, как было активировано для получения контакта, будет автоматически закрыто.
Отметим, что как и в случае с поставщиками выбора файла, поставщик контактов должен быть котов к тому, чтобы сохранить состояние сеанса при приостановке, так, чтобы он мог восстановить это состояние при повторном запуске с previousExecutionState установленном в terminated. Хотя это не показано в примере, настоящему приложению-поставщику следует сохранять текущее выделение и позицию просмотра списка, вместе с чем угодно другим, касающимся состояния сеанса, и восстанавливать всё это, когда нужно, в обработчике activated.
Для того, чтобы сделать приложение поставщиком для средства выбора файлов, начинают – с чего же еще! – с добавления соответствующего объявления в манифест. В данном случае применимы три объявления: Средство выбора файлов для открытия (File Open Picker), Средство выбора файлов для сохранения (File Save Picker) и Средство обновления кэшированных файлов (Cached File Updater), как показано ниже, на примере окна редактора манифеста в Visual Studio. Каждое из этих объявлений можно сделать один раз в пределах одного приложения.
Объявления Средство выбора файлов для открытия (File Open Picker) и Средство выбора файлов для сохранения (File Save Picker) это то, что делает приложение-поставщика доступным в диалоговом окне, запущенном с помощью Windows.Storage.Pickers.FileOpenPicker и API FileSavePicker. Вызывающее приложение в обоих случаях ничего не знает о приложении, которое может быть запущено – всё взаимодействие между средством выбора файлов и поставщиком происходит с помощью контракта, и брокер контракта ответственен, во-первых, за отображение интерфейса, посредством которого можно выбрать объект, и, во-вторых, за возвращение объекта StorageFile для выбранного элемента.
И для контракта Средство выбора файлов для открытия (File Open Picker), и для контракта Средство выбора файлов для сохранения (File Save Picker), приложение-поставщик отражает в манифесте типы файлов, которые оно может обслуживать. Это выполняется с помощью кнопки Добавить (Add New) на нижеприведенном изображении. Затем средство выбора файлов делает приложение доступным для выбора только тогда, когда вызывающее приложение сообщает о подходящих типах файлов. Параметр Поддерживает все типы файлов (Supports Any File Type), который вы здесь видите позволяет приложению всегда появляться в списке, но это подходит только для приложений, наподобие SkyDrive, которые предоставляют доступ к некоему универсальному хранилищу файлов. Приложения, которые работают лишь со специфическими типами файлов, следует заявлять лишь об этих типах.
Приложение-поставщик содержит разные описания Начальной страницы (Start Page) для контрактов открытия и сохранения – это различные и независимые операции. И в том, и в другом случаях, как мы видели для других контрактов, это те страницы, которые средство выбора файлов загружает, когда пользователь выбирает приложение-поставщик. Как и в случае с целевым приложением для контракта Общий доступ, эти страницы обычно независимы от основного приложения и имеют собственный скриптовый контекст и обработчики активации, как мы увидим в следующем разделе. (Опять же, параметры Исполняемый файл (Executable) и Точка входа (Entry point) предназначены для других языков).
Вы можете задаться вопросом: почему контракты открытия и сохранения файлов разделены? Разве большинство приложений не реализуют обе функции? Не обязательно. Если вы создаёте приложение-поставщик для веб-сервиса, который, фактически, предоставляет данные только для чтения (наподобие изображений в результатах, полученных от поисковой системы), вы можете обслуживать лишь открытие файлов. Если сервис поддерживает создание новых файлов и обновление существующих файлов, как обычно делают сервисы по управлению фотографиями или документами, тогда вам так же нужно обрабатывать сохранение файлов. Возможен и сценарий, где поставщик обслуживает лишь операции сохранения файлов, такие, как запись данных в сервис общего доступа. Коротко говоря, Windows не может предположить того, какова природа источников данных, с которыми будут работать приложения-поставщики, поэтому данные два контракта разделены.
Так как следующий большой раздел данной лекции посвящен контракту Средство обновления кэшированных файлов (Cached File Updater), хорошо бы знать, как он соотносится с другими. Этот контракт позволяет приложению-поставщику синхронизировать локальные и удалённые копии файла, обычно подписываясь на уведомления об изменении или доступе к этим файлам и управляя ими. Это обычно используется для приложений, которые представляют хранилище файлов, в котором пользователь часто открывает и сохраняет файлы, наподобие SkyDrive или приложения баз данных. На самом деле, это сервисы двусторонней привязки данных для файлов, где и локальная и удалённая копии могут быть обновлены независимо. В итоге, эта возможность реализуется совместно с контрактами средств выбора файлов для приложения-поставщика.
) в Центре разработчиков Windows имеет некоторые полезные руководства о том, когда приложения могут быть поставщиками для контракта средства сохранения файлов и когда лучше подходит их настройка в виде целевых приложений общего доступа.
Демонстрация контракта поставщика средства выбор файла – для открытия и сохранения – находится в примере "Контракт Средство выбора файла" (http://code.msdn.microsoft.com/windowsapps/File-picker-app-extension-0cb95155 ), который я, для ясности, буду упоминать как пример поставщика. Декларация и того и другого включена в манифест, с параметром Поддерживает все типы файлов (Supports Any File Type), в итоге, приложение из примера будет перечислена вместе с другими приложениями при всех вызовах средства выбора файла, как показано здесь:
При активации будет загружена начальная страница, указанная в манифесте для соответствующего контракта (открытия или сохранения). Это страницы fileOpenPicker.html и fileSavePicker.html, которые можно найти в корневом разделе проекта. Обе эти страницы загружаются независимо от основного приложения и выглядят так, как показано на рис. 3.1. и рис. 3.2. Обратите внимание на то, что заголовок приложения и цветовая схема определены параметрами закладки Интерфейс приложения (Application UI) в манифесте приложения-поставщика. В частности, текст берется из поля Отображаемое имя (Display Name), а цвета берутся из параметров Текст переднего плана (Foreground Text) и Цвет фона (Background Color) из набора параметров Плитка (Tile), как показано на рис. 3.3. Отметим, что система автоматически добавляет стрелку, направленную вниз после заголовка на рис. 3.1 и рис. 3.2, с его помощью пользователь может выбрать другое расположение или приложение-поставщик.
(рис 3.1) Интерфейс открытия файла, предоставляемый примером
(рис 3.2) Интерфейс сохранения файла, предоставленный примером
(рис 3.3) Установки на закладке Интерфейс приложения (Application UI) в манифесте, которые влияют на внешний вид окон средств открытия и сохранения файлов приложения-поставщика. Серые полосы в изображении представляют собой другие поля, которые я скрыл для краткости
Когда вы впервые запустите этот пример, вы не увидите ни одной из этих страниц. Вместо этого вы увидите страницу, с которой вы можете запустить средство открытия или сохранения файлов и выбрать это приложение в качестве поставщика. Вы можете сделать это, если хотите, но я рекомендую использовать другое приложение для запуска средств работы с файлами, таким образом мы может быть уверены в том, какое приложение какую роль играет. Для этой цели вы можете использовать пример "Средство выбора файлов" (http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba ) (в качестве приложения-потребителя). Вы даже можете использовать что-то вроде приложения Windows 8 Music (Музыка), команда Open File (Открыть файл) на его панели приложения вызовет средство выбора файлов, в интерфейсе которого будет присутствовать и наш пример.
Что бы вы ни выбрали, важная часть примера поставщика – это его раздельные страницы для обслуживания контрактов, то есть, повторюсь, fileOpenPicker.html и fileSavePicker.html. В первом случае, код содержится в js/fileOpenPicker.js, где мы можем обработчик события activated с видом активации fileOpenPicker:
function activated(eventObject) {
if (eventObject.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.fileOpenPicker) {
fileOpenPickerUI = eventObject.detail.fileOpenPickerUI;
eventObject.setPromise(WinJS.UI.processAll().then(function () {
// Перемещение на страницу сценария...
}));
}
}
Здесь ), свойство )) предоставляет средства для выполнения обязанностей поставщика при обслуживании контракта.
Во втором случае код находится в js/fileSavePicker.js, с видом активации fileSavePicker:
function activated(eventObject) {
if (eventObject.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.fileSavePicker) {
fileSavePickerUI = eventObject.detail.fileSavePickerUI;
eventObject.setPromise(WinJS.UI.processAll().then(function () {
// Перемещение на страницу сценария
}));
}
}
Здесь ). Как и в случае с контрактом открытия, его свойство )) предоставляет средства для выполнения обязаностей поставщика при обслуживании контракта.
И в случае активации для открытия файла, и в случае активации для сохранения, содержимое начальной страницы контракта отображается внутри области, сверху и снизу которой расположены панели, предоставленные системой. Если содержимое страницы окажется больше, чем предоставленное пространство, появятся полосы прокрутки, но только в этой области – верхняя и нижняя полосы всегда будут оставаться на одном и том же месте. В обоих случаях WinRT так же предоставляет обычные средства, используемые при активации, такие, как свойства splashScreen и previousExecutionState, подобное мы видели в Главе 3 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", что означает, что вам следует перезагрузить необходимое состояние сеанса и использовать, при необходимости, расширенный экран-заставку.
Однако, гораздо интереснее – это взаимодействие, характерное для контрактов, которое представлено в различных сценариях для этих страниц (как вы можете видеть на рис. 3.1. и рис. 3.2.). Взглянем на каждый из них.
Примечание. Для того, чтобы узнать подробности по проектированию опыта взаимодействия пользователя со средством выбора файлов, обратитесь к материалу "Руководство по средствам выбора файлов) (http://msdn.microsoft.com/library/windows/apps/hh465182.aspx ).
Поставщик открытия файлов работает посредством объекта FileOpenPickerUI, поддерживаемого при виде активации fileOpenPicker. Проще говоря, какой бы пользовательский интерфейс не предоставлял бы провайдер для выбора некоторых файлов или данных, он будет связан с различными методами, свойствами и событиями этого объекта.
Во-первых, интерфейс будет использовать свойство )) для определения того, запущено ли средство выбора файла для выбора одного или нескольких файлов.
Когда пользователь выбирает элемент в интерфейсе, поставщик вызывает метод addFile с объектом StorageFile, который соответствует этому элементу. Очевидно, поставщик должен как-то создать этот объект StorageFile. В средстве для открытия файлов примера, в Сценарии 1, это выполняется с помощью StorageFolder.getFileAsync (где StorageFolder представлено расположением пакета).
Windows.ApplicationModel.Package.current.installedLocation
.getFileAsync("images\\squareTile-sdk.png").then(function (fileToAdd) {
addFileToBasket(localFileId, fileToAdd);
}
Здесь addFileToBasket просто вызывает FileOpenPickerUI.addFile и отображает сообщения для результата. Этот результат является значением из Windows.Storage.Pickers.Provider.AddFileResult: added (успешно добавлено), alreadyAdded (избыточная операция, так как файл уже добавлен), notAllowed (добавление запрещено, по причине несоответствующего типа файла), и unavailable (приложение не видимо). Это, на самом деле, лишь помогает вам сообщить пользователю о результате в вашем интерфейсе. Обратите так же внимание на то, что метод canAddFile может быть полезен для включения или выключения команд добавления в вашем интерфейсе, что позволяет предотвратить некоторые из этих ошибок, просто не позволяя происходить неверным действиям.
Приложение-поставщик так же должно реагировать на запросы, касающиеся удаления ранее добавленных элементов, в случаях, когда пользователь удаляет ранее выбранные файлы из "корзины" в интерфейсе средства выбора файлов, допускающего выбор нескольких файлов. Для того, чтобы это сделать прослушивайте событие fileRemoved объекта FileOpenPickerUI, которое, в качестве аргумента, предоставляет ID файла. Вы передаёте этот ID в containsFile, за которым следует removeFile, как это сделано в примере (js/fileOpenPickerScenario1.js):
// Подключает события в коде инициализации страницы
fileOpenPickerUI.addEventListener("fileremoved", onFileRemovedFromBasket, false);
function removeFileFromBasket(fileId) {
if (fileOpenPickerUI.containsFile(fileId)) {
fileOpenPickerUI.removeFile(fileId);
}
}
Если вам нужно знать, когда интерфейс средства выбора файла закрывает вашу страницу (как происходит, когда пользователь нажимает на кнопку Open (Открыть) или Cancel (отменить), как показано на рис. 3.1), прослушивайте событие closing. Это даёт вам возможность закрыть любые сеансы, которые были открыты при взаимодействии с онлайновыми сервисами и выполнить любые необходимые задачи по очистке. В eventArgs вы можете найти свойство isCanceled, которое показывает, был ли отменен сеанс работы со средством выбора файла (значение true), или интерфейс был закрыт кнопкой Open (Открыть) (false). Объект eventArgs.closingOperation так же содержит метод getDeferral и свойство deadline, что позволяет вам производить, так же, асинхронные операции, это похоже на то, что показано в Главе 3 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript" для события suspending.
Последнее замечание касается того, что средство выбора файлов должно учитывать FileOpenPickerUI.settingsIdentifier для перезапуска приложения-поставщика и приведения его в предыдущее состояние (то есть, к состоянию предыдущего сеанса работы с файлами). Если вы помните о другой стороне этого процесса, то приложение, которое использует средство выбора файла может использовать settingsIdentifier для различения внутри себя различных вариантов использования, возможно – для различения определенных типов файла или особенностей контекстов. Идентификатор так же может отличаться у различных приложений, которые запускают средство выбора файла. Таким образом, учитывая это свойство, приложение-поставщик может управлять контекстом, зависящим от каждого конкретного случая его активации (обычно, используя settingsIdentifier в именах файлов в пакете приложения и в именах контейнеров параметров), именно так работают встроенные средства выбора файлов для работы с файловой системой.
Так же приложение-поставщик может быть приостановлено, отображая свой пользовательский интерфейс, и, возможно, что оно может быть остановлено, если вызывающее приложение будет закрыто. Однако, если вы управляете состоянием средства выбора файлов на основе использования значений settingIdentifier, вам не нужно сохранять или управлять другими состояниями сеанса, которые касаются функциональности средства выбора файлов.
В основном, Сценарий 2 варианта средства открытия файлов в примере поставщика очень похож на то, что мы видели в предыдущем разделе. Единственное различие заключается в том, как создать StorageFile из источника, который не является файлом, как, например, из изображения, которое получено по удалённому URI. В этой ситуации нам нужно получить поток данных для удалённого URI и конвертировать этот поток в объект StorageFile. К счастью, несколько API WinRT серьезно упрощают эту задачу, как показано в js/fileOpenPickerScenario2.js, в методе onAddFileUri:
function onAddUriFile() {
// Ответ на нажатие кнопки Add (Добавить)
var imageSrcInput = document.getElementById("imageSrcInput");
if (imageSrcInput.value !== "") {
var uri = new Windows.Foundation.Uri(imageSrcInput.value);
var thumbnail =
Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(uri);
// Получение файла по URI для добавления в корзину средства выбора файлов
Windows.Storage.StorageFile.createStreamedFileFromUriAsync("URI.png", uri, thumbnail).then(function (fileToAdd) {
addFileToBasket(uriFileId, fileToAdd);
},
function (error) {
// ...
});
} else {
// ...
}
}
Здесь Windows.Storage.StorageFile.createStreamedFileFromUriAsync выполняет всё необходимое для того, чтобы дать нам StorageFile для URI, а addFileToBasket это внутренний метод, который просто вызывает метод addFile method объекта FileOpenPickerUI.
Заметьте, что если вам нужно произвести проверку подлинности или выполнить какие-то другие особые шаги для получения содержимого с веб-сервиса, вам обычно нужно использовать API ) для того, чтобы получить это содержимое (здесь вы можете задать учетные данные), за ним последует StorageFile.createStreamedFile для того, чтобы передать полученный файл через контракт. StorageFile.createStreamedFileFromUriAsync выполяет то же самое, но не предусматривает проверку подлинности.
Так же, как поставщик открытия файлов взаимодействует с объектом FileOpenPickerUI, поставщик для сохранения файлов работает со специальными методами, свойствам и событиями класса FileSavePickerUI. Повторюсь, контракты открытия и сохранения имеют дело с источниками данных, для которых вы можете создать приложение-поставщик, и которые могут поддерживать или не поддерживать операции сохранения независимо от операций открытия. Если вы поддерживаете и то и другое, вы, вероятно, повторно используете тот же пользовательский интерфейс и используете ту же начальную страницу и путь активации.
В классе FileSavePickerUI у нас, для начала, имеется свойство allowedFileTypes, соответствующее тому, что предоставлено приложением, которое активирует интерфейс средства сохранения файла. Как и в случае с открытием, вы будете использовать это свойство для фильтрации того, что будет отображено в вашем собственном интерфейсе, таким образом пользователь может точно видеть, какие элементы заданных типов уже существуют. Так же при этом обычно заполняют выпадающий список типов файлов теми же типами файлов.
Для восстановления сохраненного в предыдущей сессии состояния интерфейса поставщика для конкретных вызывающих приложений, опять же, можно использовать свойство settingsIdentifier.
Возвращаясь к рис. 3.2., обратите внимание на элементы управления вдоль нижней стороны экрана, они автоматически предоставляются интерфейсом средства выбора файлов, когда запускается приложение-поставщик. Когда пользователь меняет поле, содержащее имя файла, приложение-поставщик может прослушать и обработать событие ), которое может принимать значения succeeded, notAllowed (обычно при неподходящем типе файла), или unavailable. Обычно это используется, когда пользователь касается элемента в списке, ожидаемое поведение системы в таком случае заключается в установке имени файла в значение имени выбранного элемента.
Самое важное событие, конечно, происходит, когда пользователь, в итоге, нажимает на кнопку Save (Сохранить). Это действие вызывает событие ) объекта FileSavePickerUI. Вы должны предоставить обработчик для этого события, в котором вы должны создать пустой объект StorageFile, в котором приложение, которое активировало интерфейс средства сохранения файлов сможет сохранить свои данные. Имя этого StorageFile должно совпадать со свойством fileName.
Свойство ). В его свойство targetFile помещают созданный StorageFile (или null, если произошла ошибка). Вы должны установить это свойство, прежде чем вернетесь из обработчика события, но, конечно, для того, чтобы это сделать вам может понадобиться асинхронная операция. Для этой цели, как мы видели уже много раз, запрос так же содержит метод getDeferral. Он использован в Сценарии 1 примера поставщика, при сохранении файла (js/fileSavePickerScenario1.js):
function onTargetFileRequested(e) {
var deferral = e.request.getDeferral();
// Создаём файл для передачи его средству работы с файлами
Windows.Storage.ApplicationData.current.localFolder.createFileAsync(
fileSavePickerUI.fileName).done(function (file) {
// Присваиваем полученный файл свойству targetFile и завершаем отложенную операцию
e.request.targetFile = file;
deferral.complete();
}, function () {
// Устанавливаем targetFile в null и завершаем отложенную операцию для того,
// чтобы показать, что произошла ошибка
e.request.targetFile = null;
deferral.complete();
});
};
В вашем приложении, конечно, замените вызов createFileAsync в локальной папке любыми необходимыми шагами для создания файла или объекта данных. Если, с другой стороны, речь идёт об удалённых файлах, вам нужно задействовать контракт Обновление кэшированных файлов (Cached File Updater) (смотрите соответствующий раздел ниже).
Сценарий 2 интерфейса сохранения файлов примера поставщика показывает еще один аспект этого процесса: отображение сообщений об ошибках, если возникают ошибки при создании необходимого StorageFile. Вообще говоря, вы, для того, чтобы сообщить пользователю о том, что ему нужно сделать, можете использовать любой интерфейс, который кажется вам подходящим и соответствующим стилю приложения. В примере использован MessageDialog:
function onTargetFileRequestedFail(e) {
var deferral = e.request.getDeferral();
var messageDialog = new Windows.UI.Popups.MessageDialog("If the app needs the user to
correct a problem before the app can save the file, the app can use a message like this to
tell the user about the problem and how to correct it.");
messageDialog.showAsync().done(function () {
// Устанавливаем свойство targetFile в null и завершаем отложенную операцию для того,
// чтобы показать, что произошла ошибка, как только пользователь закроет диалоговое окно.
// Это позволит пользователю выполнить необходимые исправления и затем снова нажать на кнопку
// Save (Сохранить).
e.request.targetFile = null;
deferral.complete();
});
};
Использование контракта средства обновления кэшированных файлов предназначено для синхронизации локальных копий файла с копиями на удалённых ресурсах, которыми управляет приложение-поставщик. Данный контракт специально предназначен для приложений, которые предоставляют доступ к хранилищу, куда пользователь регулярно сохраняет файлы, откуда он их открывает и обновляет их содержимое. Приложение SkyDrive в Windows – это хороший пример подобного взаимодействия. В других случаях, когда пользователь обычно пользуется средством выбора файлов для получения файлов и использования их каким-либо образом, но не возвращается к ним, использование контрактов средств выбора файлов полностью оправданно.
Если взглянуть на Главу 2 курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript", мы увидим там некоторые вызовы методов, выполненные приложением, которое использует средство выбора файлов: ). Проще говоря, это те вызовы, которые делает приложение-потребитель файлов, если и когда оно выполняет запись в файлы, которое оно получило от средства выбора файла. Оно делает это потому что не узнает (и ему не следует об этом беспокоиться), есть ли у поставщика файла другая копия файла в базе данных, на веб-сервисе, и так далее, с которой нужно синхронизировать данный файл. Если приложение-поставщик нуждается в обработке синхронизации, вызовы приложением-приемником этих методов будут вызывать необходимый интерфейс средства обновления кэшированных файлов приложения-поставщика, который может быть показан, а может и нет, в зависимости от конкретной ситуации. Даже если приложение-потребитель не вызывает эти методы, приложение-поставщик будет получать оповещения об изменениях, но не сможет показать какой-либо пользовательский интерфейс.
Этот контракт работает с двумя направлениями, в зависимости от того, нужно ли ему обновить локальную (кэшированную) копию файла, или удалённую (исходную) копию. В первом случае, поставщик запрашивает обновление локальной копии, обычно когда приложение-потребитель пытается получить доступ к файлу (берет его из списков ); оно не запрашивает обновление напрямую). Во втором случае приложение-потребитель модифицирует файл, поэтому поставщику нужно передать изменения в исходную копию.
С точки зрения приложения-поставщика, необходимость в обновлениях возникает, когда оно предоставляет файл другому приложению. Это может произойти с помощью контрактов средств выбора файлов , как мы видели в предыдущем разделе, но это может случиться и через сопоставление типов файлов, а так же посредством контракта общего доступа. В последнем случае приложение-источник данных для общего доступа, в известном смысле, является поставщиком файла и может так же использовать контракт средства обновления кэшированных файлов. Коротко говоря, если вам нужно, чтобы приложение-поставщик файлов смогло отслеживать и синхронизировать обновления между локальной и удалённой копиями файла, ему нужно использовать этот контракт.
Поддержка контракта начинается с объявления в манифесте, как показано ниже, где Начальная страница (Start page) указывает на страницу, реализующую интерфейс средства обновления кэшированных файлов. Эта страница обрабатывает необходимые для обновления файлов события и может быть показана, а может и не быть показана пользователю, как мы увидим позже.
Следующий шаг поставщика заключается в том, чтобы показать, когда данный ) для предоставленного файла, как показано в Сценарии 3 примера "Контракт Средство выбора файла" (http://code.msdn.microsoft.com/windowsapps/File-picker-app-extension-0cb95155 ), который я снова, упоминать как пример поставщика (js/fileOpenPickerScenario3.js):
function onAddFile() {
// Ответ на нажатие кнопки Add (Добавить)
Windows.Storage.ApplicationData.current.localFolder.createFileAsync("CachedFile.txt",
Windows.Storage.CreationCollisionOption.replaceExisting).then(function (file) {
Windows.Storage.FileIO.writeTextAsync(file, "Cached file created...").then(
function () { Windows.Storage.Provider.CachedFileUpdater.setUpdateInformation(
file, "CachedFile",
Windows.Storage.Provider.ReadActivationMode.beforeAccess,
Windows.Storage.Provider.WriteActivationMode.notNeeded,
Windows.Storage.Provider.CachedFileOptions.requireUpdateOnAccess);
addFileToBasket(localFileId, file);
}, onError);
}, onError);
};
Примечание. setUpdateInformation находится в пространстве имен Windows.Storage.Provider и отличается от объекта Windows.Storage.CachedFileManager, который используется с другой стороны контракта. Будьте внимательны и не перепутайте их.
Метод setUpdateInformation принимает следующие аргументы:
StorageFile для файла.requireUpdateAccess (обновление при доступе к локальному файлу), useCachedFileWhenOffline (будет осуществлено обновление при доступе, если это запросит вызывающее приложение, доступ разрешается, если нет сетевого соединения), и denyAccessWhenOnline (запускает обновление при попытке доступа и требует сетевое соединение).С помощью этого вызова, другими словами, поставщик управляет тем, как и когда ему следует активироваться для обработки обновления, когда осуществляется доступ к локальному файлу.
Итак, всего у нас есть два случая, когда приложение-поставщик может быть активировано и у него может быть запрошен показ пользовательского интерфейса: первый случай – когда вызывающее приложение обновляет файл, и второй – когда вызывающее приложение пытается получить доступ к файлу, но нуждается в обновлении перед чтением его содержимого.
Прежде чем переходить к техническим подробностям, давайте посмотрим, как всё это выглядит для пользователя. Для того, чтобы увидеть средство обновления кэшированных файлов в действии с использованием примера, активируйте его с использованием средства выбора файлов из другого приложения. Для начала, однако, запустите пример поставщика для того, чтобы удостовериться в том, что его контракты зарегистрированы в системе. Затем запустите вышеупомянутый пример "Средство выбора файлов" (http://code.msdn.microsoft.com/windowsapps/File-picker-sample-9f294cba ). В нём, Сценарии 4, 5 и 6 вызывают взаимодействие с контрактом обновления кэшированных файлов. Сценарии 4 и 6 осуществляют запись в файл для вызова обновления удалённой копии. Сценарий 5 получает доступ к локальному файлу, что вызывает обновление локального файла как часть этого процесса.
В Сценарии 5 (обновление локального файла), начните с прикосновения к кнопке Выбрать локальный файл (Pick Cached File) в пользовательском интерфейсе, показанном здесь:
Это запустит пример поставщика. На данном экране, выберите Сценарий 3, в итоге, вы увидите интерфейс, показанный на рис. 3.4. Это режим примера поставщика, который отображает средство выбора файлов поставщика (js/fileOpenPickerScenario3.js), где он вызывает setUpdateInformation. Это еще не интерфейс для средства обновления кэшированных файлов. Щелкните кнопку Add File to Basket (Добавить файл в корзину), и коснитесь кнопки Open (Открыть). Это вернет вас в первое приложение (к примеру, средства выбора файлов на вышеприведенном рисунке), где теперь будет активна кнопка Output Latest Version (Вывести самую свежую версию). Прикосновение к этой кнопке теперь активирует например поставщика с помощью контракта средства обновления кэшированных файлов, как показано на рис. 3.5. Это происходит, когда нужно обновить локальную копию кэшированного файла.
(рис 3.4) Интерфейс примера поставщика для выбора файла. Для предоставленного файла вызван метод setUpdateInformation, для того, чтобы настроить взаимоотношения со средством обновления кэшированных файлов
(рис 3.5) Интерфейс для контракта обновления кэшированных файлов из примера поставщика, для локального файла
Обратите внимание на описания в примере. В то время, как пример показывает интерфейс по умолчанию, интерфейс средства обновления кэшированных файлов не отображается до тех пор, пока не нужно будет разрешить конфликт или запросить идентификационные данные. Часто в подобном взаимодействии нет необходимости и поставщик незаметно предоставляет обновление локального файла или показывает, что файл находится в актуальном состоянии. Интерфейс примера просто предоставляет обе эти возможности для явного выбора (и не забудьте выбрать одну из них, так как выбор Cancel (Отмена) приведет к выдаче исключения).
В Сценарии 6 (обновление удалённого файла) примера средства выбора файла, мы можетм видеть взаимодействие, которое присутствует, когда приложение-потребитель вносит изменения в свою локальную копию, таким образом, вызывая обновление удалённой копии. Начнём с прикосновения к кнопке Get Save File (Выполнить сохранение файла) в окне, показанном ниже:
В средстве выбора файлов, выберите пример поставщика как источник, что приведет к запуску интерфейса, показанного на рис. 3.6. посредством контракта средства сохранения файла, реализованного в html/fileSavePickerScenario3.html и js/fileSavePickerScenaro3.js. Если вы посмотрите в файл JavaScript, вы увидите вызов setUpdateInformatio, который осуществляется, когда вы вводите имя файла и нажимаете Сохранить (Save). Выполнение этого действия возвращает вас в пример средства работы с файлами выше и теперь должна быть доступна кнопка Запись в файл (Write To File). Прикосновение к этой кнопке повторно запускает пример поставщика посредством контракта обновления кэшированных файлов с пользовательским интерфейсом, показанным на рис. 3.7. Этот интерфейс предназначен для демонстрации того, как приложение-поставщик может обработать перезапись или переименование удалённого файла.
(рис 3.6) Интерфейс примера поставщика для сохранения файла. Метод setUpdateInformation снова вызывается для предоставленного файла, для того, чтобы настроить взаимоотношения со средством обновления кэшированных файлов
(рис 3.7) Интерфейс для контракта обновления кэшированных файлов из примера поставщика, для удалённого файла
Давайте посмотрим, как контракт обновления кэшированных файлов выглядит в коде. Как вы можете ожидать, приложение-поставщик запускается, загружается начальная страница (cachedFileUpdater.html в корневом разделе проекта), обработчик ), который содержит свойство )), вместе с обычным набором из kind, previousExecutionState, и splashScreen. Вот как это выглядит в файле js/cachedFileUpdater.js примера поставщика:
function activated(eventObject) {
if (eventObject.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.cachedFileUpdater) {
cachedFileUpdaterUI = eventObject.detail.cachedFileUpdaterUI;
cachedFileUpdaterUI.addEventListener("fileupdaterequested", onFileUpdateRequest);
cachedFileUpdaterUI.addEventListener("uirequested", onUIRequested);
switch (cachedFileUpdaterUI.updateTarget) {
case Windows.Storage.Provider.CachedFileTarget.local:
// Код опущен: настраивает пример для показа cachedFileUpdaterScenario1
// при необходимости.
break;
case Windows.Storage.Provider.CachedFileTarget.remote:
// Код опущен: настраивает пример для показа cachedFileUpdaterScenario2
// при необходимости.
break;
}
}
}
Когда приложение-поставщик запускается для обновления локального файла из удалённого источника, свойство cachedFileUpdaterUI.updateTarget будет иметь значение local, как вы можете видеть выше. Кода у приложения запрашивается обновление удаленного файла с использованием локальных изменений, цель будет установлена в значение remote. Всё, что пример выполняет в этих случаях – это указывает либо на html/cachedFileUpdaterScenario1.html (рис. 3.5.) либо на html/cachedFile-UpdaterScenario2.html (рис. 3.7) как на пользовательский интерфейс для обновления.
Интерфейс, на самом деле, не показывается сразу. В первую очередь объект ) для выполнения попытки незаметного обновления. Здесь )), объектом, который сохраняют в переменной, которая доступна из интерфейса обновления.
Если есть возможность незаметно обновить локальный файл, выполните следующее:
request.getDeferral.currentlyUnavailable (удаленная версия файла недоступна, и локальная версия недоступна), failed (синхронизация невозможна ни сейчас, ни в будущем, так как удалённая версия файла удалена), и completeAndRenamed (исходная версия файла была переименована, обычно – для разрешения конфликтов).
complete для того, чтобы завершить обновление.В итоге, теперь поставщик может знать заранее, что он не может выполнить незаметное обновление вовсе – пользователь может быть не авторизованным на удалённом сервисе (или учетные данные нужны каждый раз), может быть конфликт, требующий разрешения и так далее. В подобных случаях обработчик события должен проверить значение )) и соответствующим образом установить свойство request.status:
visible, переключиться к этому интерфейсу и вернуться из обработчика события. Отложенная операция завершится, когда пользователь введет нужные данные посредством пользовательского интерфейса.hidden, установить request.status в значение userInputNeeded и вернуться. Это вызовет событие CachedFileUpdaterUI.onuiRequested , за которым последует другое событие fileUpdateRequested, где uiStatus будет иметь значение visible, в таком случае вы переключитесь на ваш пользовательский интерфейс.unavailable, установить request.status в значение currentlyUnavailable.Вы можете найти кое-что из этого в обработчике onFileUpdateRequest примера. Он, на самом деле, обрабатывает лишь проверку uiStatus, так как не пытается выполнить незаметное обновление (как описано ниже, в комментариях):
function onFileUpdateRequest(e) {
fileUpdateRequest = e.request;
fileUpdateRequestDeferral = fileUpdateRequest.getDeferral();
// Попытка незаметного обновления с использованием fileUpdateRequest.file , или вызов
// fileUpdateRequest.updateLocalFile в локальном случае, setting fileUpdateRequest.status
// соответственно, затем вызов fileUpdateRequestDeferral.complete(). В противном случае, если вы
// знаете, что понадобится действие пользователя, исполнение следующего кода.
switch (cachedFileUpdaterUI.uiStatus) {
case Windows.Storage.Provider.UIStatus.hidden:
fileUpdateRequest.status =
Windows.Storage.Provider.FileUpdateStatus.userInputNeeded;
fileUpdateRequestDeferral.complete();
break;
case Windows.Storage.Provider.UIStatus.visible:
// Переключение на интерфейс обновления (настроено в событии activated)
var url = scenarios[0].url;
WinJS.Navigation.navigate(url, cachedFileUpdaterUI);
break;
case Windows.Storage.Provider.UIStatus.unavailable:
fileUpdateRequest.status = Windows.Storage.Provider.FileUpdateStatus.failed;
fileUpdateRequestDeferral.complete();
break;
}
}
Опять же, если произойдет незаметное обновление, пользовательский интерфейс приложения-поставщика никогда не будет показан пользователю. В случае с примером поставщика, так как незаметное обновление не происходит никогда, он всегда проверяет ), сообщая приложению-поставщику, что система делает пользовательский интерфейс видимым. Приложение, на самом деле, может отложить инициализацию своего пользовательского интерфейса до возникновения этого события, так как он не выполняет никаких функций при незаметном обновлении.
После этого событие fileUpdateRequested снова будет вызвано с uiStatus, теперь установленным в значение visible. Отметим, как вышеприведенный код будет вызывать в этом случае request.getDeferral но не будет вызывать его метод complete.
Мы откладывает этот шаг до того момента, когда работа с пользовательским интерфейсом приведет к нужному результату (и, на самом деле, мы оставляем и запрос и отложенный вызов для использования из кода пользовательского интерфейса).
Пользовательский интерфейс обновления данных ответственен за приём любых данных от пользователя, необходимых для выполнения задачи: пользователь может вводить учетные данные, указывать, какую из копий файлов следует сохранить (локальную или удаленную), разрешать переименование конфликтующего файла (при обновлении удалённого файла) и так далее. При обновлении локального файла, осуществляется запись в StorageFile в request.file или вызывается request.updateLocalFile; в случае с обновлением удаленного файла, данные из локальной копии копируются в request.file.
Для того, чтобы завершить обновление, код интерфейса устанавливает request.status в значение complete (или, если произошёл сбой, в другое подходящее значение) и вызывает метод complete отложенной операции. Это изменит статус кнопок, предоставленных системой, расположенных у нижней части экрана, как вы можете видеть на рис. 3.5. и рис. 3.7., а именно, активирует кнопку OK и деактивирует Cancel (Отмена). В примере поставщика обе кнопки просто выполняют эту пару строк кода для этой цели:
fileUpdateRequest.status = Windows.Storage.Provider.FileUpdateStatus.complete;
fileUpdateRequestDeferral.complete();
В целом, взаимодействие между системой и приложением при реализации контракта средства обновления кэшированных файлов выглядит довольно простым и понятным: обработка событий, копирование данных туда, куда нужно и обновление статуса запроса. Настоящая работа по реализации этого контракта заключается в том, чтобы сначала решить, когда вызывать setUpdateInformation и затем предоставить пользовательский интерфейс для поддержки обновления локальных или удаленных файлов при определенных обстоятельствах. Это, конечно, включит в себя интенсивное взаимодействие с вашей серверной системой хранения.
Последний контракт, который мы рассмотрим в этой лекции (ух ты!), это средство выбора контактов. Мы еще не видели этого средства в действии в Windows 8. Поэтому посмотрим на него для начала, а потом разберемся, как это средство используется с одной стороны контракта, и как приложение-поставщик удовлетворяет нужды другой стороны.
Контакт, или контактные сведения, как вы, вероятно, ожидаете, это информация о человеке, которая включает в себя подробности наподобие имени, номера телефона, адреса электронной почты и так далее. Очевидное место, где вам понадобится контакт – это составление электронного письма, как показано на рис. 3.8. Здесь, при прикосновении к элементу управления "+" в правой части полей To (Кому) и Cc (Копия), будет открыто средство выбора контактов, которое по умолчанию относится к приложению Peoples (Люди) Windows 8, как показано на рис. 3.9. (экран-заставка), и на рис. 3.10 (вид в режиме множественного выбора, где я размыл сведения о моих друзьях, и они не станут обвинять меня за привлечение к ним нежелательного внимания!). Как мы видели с интерфейсом средства выбора файлов, приложение-поставщик поддерживает пользовательский интерфейс для центральной части экрана, в то время как Windows поддерживает верхнюю и нижнюю части, заголовок и элемент меню в виде стрелки, направленной вниз, используя информацию из манифеста приложения-поставщика (обратиесь к рис. 3.3). На рис. 3.11. вы можете видеть внешний вид приложения из примера "Приложение для выбора контактов" (http://code.msdn.microsoft.com/windowsapps/Contact-Picker-App-sample-fc6677a1 ) в режиме поставщика, так же как и меню, которое позволяет вам выбирать разных поставщиков (те приложения, которые объявили себя в качестве поставщиков контактов)
Когда я выбираю один или большее количество контактов в любом приложении-поставщике и нажимаю на кнопку Select (Выбрать) в нижней части экрана, эти контакты поступают в первое приложение, в данном случае это Mail (Почта).
Так же, как контракт средства выбора файлов позволяет перемещаться по данным, представленным в виде файлов другим приложением, контракт контактов (скажите это быстро раз десять!) позволяет пользователю просто перемещаться по сведениям о людях, которые вы можете выбирать из любого другого источника.
(рис 3.8) Приложение Mail (Почта) использует средство выбора контактов для выбора получателей
(рис 3.9) Приложение Peoples (Люди), запускающееся в качестве поставщика контактов
(рис 3.10) Пользовательский интерфейс выбора внутри приложения Peoples (Люди), показанный при множественном выделении (сведения о моих друзьях здесь размыты, так как они обычно не ищут славы среди разработчиков). Выделенные объекты собраны в нижней части, в корзине элементов
(рис 3.11) Пользовательский интерфейс примера выбора контактов, когда приложение используется в качестве поставщика, вместе со всплывающим меню в заголовке, которое позволяет выбирать поставщика
Контакты, в целом, используют API в пространстве имен ), свойства которого, такие как name, phoneNumbers, locations, emails, instantMessages, и customFields, вместе с методами getThumbnailAsync и queryCustomFields, предоставляют вам контактную информацию. Выбор контакта осуществляется через интерфейс средства выбора контактов, который очень похож на интерфейс средства выбора файла. Интерфейс запускается с помощью ). После создания экземпляра данного объекта, вы можете установить подпись commitButtonText для первой (левой) кнопки в интерфейсе (как "Select" (Выбор) на предыдущих рисунках). Так же вы можете установить свойство selectionMode в одно из значений перечисления ContactSelectionMode: либо в contact (по умолчанию) либо в fields. В первом случае, возвращается полная контактная информация, в последнем, средство выбора работает с содежимым свойства desiredFields ( http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.contacts.contactpicker.desiredfields.aspx). Обратитесь к документации по данному свойству для того, чтобы узнать подробности.
Когда вы готовы к показу интерфейса, вызовите метод средства выбора контактов pickSingleContactAsync или pickMultipleContactsAsync. Они предоставляют вам обработчик завершения для одного объекта ContactInformation или для вектора из них, соответственно. Как и в случае со средством выбора файлов, обратите внимание на то, что эти API выдают исключения, если вызываются, когда приложение находится в прикрепленном режиме просмотра, очевидно, вы постараетесь избежать подобной ситуации.
Выбор и отображение отдельного контакта показан в Сценарии 1 примера "Приложение для выбора контактов" ( http://code.msdn.microsoft.com/windowsapps/Contact-Picker-App-sample-fc6677a1):
var picker = new Windows.ApplicationModel.Contacts.ContactPicker();
picker.commitButtonText = "Select";
// Открывает для пользователя средство выбор контакта
picker.pickSingleContactAsync().done(function (contact) {
if (contact !== null) {
// Получает информацию о контакте...
}
});
Выбор нескольких контактов (Сценарий 2, js/scenarioMultiple.js) работает похожим образом, лишь использует pickMultipleContactsAsync. В любом случае, вызывающее приложение затем применяет данные ContactInformation так, как ему нужно, например, заполняет поля To (Кому) и Cc (Копия) в приложении Mail (Почта). Однако, свойства этого объекта, помимо name, которое является обычной строкой, обладают некоторой структурой, как показано в следующей таблице.
| Свойство | Тип | Свойства и типы полей |
|---|---|---|
| emails phoneNumbers | Вектор ContactField |
category (ContactFieldCategory), name (строка), type (типа ContactFieldType), value (строка) |
| instantMessages | Вектор ContactInstantMessageField |
То же самое, что и ContactField выше, плюс - displayText, launchUri,service, и userName (все имеют строковой тип) |
| locations | Вектор ContactLocationField |
То же, что и ContactField выше, плюс city, country, postalCode, region, street, и unstructuredAddress (все имеют строковой тип) |
Таким образом, пример принимает объект ContactInformation следующим образом, сначала извлекая индивидуальные свойства вектора:
appendFields("Emails:", contact.emails, contactElement);
appendFields("Phone Numbers:", contact.phoneNumbers, contactElement);
appendFields("Addresses:", contact.locations, contactElement);
И затем перечисляя содержимое этих векторов и в этом случае создавая элементы с их содержимым. Другие приложения, конечно, будут перемещать значения в подходящие поля или в другие части интерфейса приложения, то, что показано здесь, демонстрирует обработку различных категорий:
function appendFields(title, fields, container) {
// Создаёт интерфейс для списка полей контактатого же типа, например, для
// адресов электронной почты или телефонов
fields.forEach(function (field) {
if (field.value) {
// Присоединяет title только если имеется не пустое поле контакта
if (title) {
container.appendChild(createTextElement("h4", title));
title = "";
}
// Отображает категорию около значения поля
switch (field.category) {
case Windows.ApplicationModel.Contacts.ContactFieldCategory.home:
container.appendChild(createTextElement("div", field.value + " (home)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.work:
container.appendChild(createTextElement("div", field.value + " (work)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.mobile:
container.appendChild(createTextElement("div", field.value + " (mobile)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.other:
container.appendChild(createTextElement("div", field.value + " (other)"));
break;
case Windows.ApplicationModel.Contacts.ContactFieldCategory.none:
default:
container.appendChild(createTextElement("div", field.value));
break;
}
}
});
}
Со стороны поставщика, что так же показано в примере "Приложение для выбора контактов" ( http://code.msdn.microsoft.com/windowsapps/Contact-Picker-App-sample-fc6677a1) мы видим тот же шаблон, что и для поставщиков средства выбора файла. Во-первых, приложению-поставщику нужно объявить контракт Выбор контактов (Contact Picker) в манифесте, где задаётся Начальная страница (Start page) для загрузки в контексте средства выбора контактов. В примере начальная страница – это contactPicker.html, которая, в свою очередь, загружает html/contactPickerScenario.html (со связанными с ней файлами JavaScript):
Как и в случае со средством выбора файлов, отдельная начальная страница подразумевает наличие отдельного обработчика активации, в данном случае он ищет вид активации contactPicker (js/contactPicker.js):
function activated(eventObject) {
if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.contactPicker)
{ contactPickerUI = eventObject.detail.contactPickerUI;
eventObject.setPromise(WinJS.UI.processAll().then(function () {
// ...
}));
}
}
Здесь eventObject.detail является объектом ContactPickerActivatedEventArgs (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.activation.contactpickeractivatedeventargs.aspx) (эти имена длинные, но, по крайней мере, предсказуемые!). Как и в случае со всеми соыбытиями активации, он содержит свойства kind, previousExecutionState, и splashScreen для обычных целей. Его свойство contactPickerUI, типа ContactPickerUI (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.contacts.provider.contactpickerui.aspx), в свою очередь, содержит информацию, специфичную для контракта средства выбора контакта:
selectionMode и desiredFields предоставляются вызывающим приложением.addContact, removeContact, и containsContact —для управления тем, что будет возвращено в вызываемое приложение. Эти методы соответствуют действиям типичного пользовательского интерфейса для выбора объектов.contactsRemoved, которое сообщает поставщику, когда пользователь удаляет элемент из корзины объектов, которая расположена вдоль нижней части экрана (вернитесь к рис. 3.10).Внутри поставщика каждый контакт представлен объектом Windows.ApplicationModel.Contacts.Contact. Поставщик создаст объект для каждого предоставляемого им контакта. В примере (js/contactPickerScenario.js), есть массив, который называется sampleContacts и имитирует то, что обычно поступает из базы данных. Этот массив содержит простые JSON-записи, вроде этих:
{
name: "David Jaffe",
homeEmail: "david@contoso.com",
workEmail: "david@cpandl.com",
workPhone: "",
homePhone: "248-555-0150",
mobilePhone: "",
address: {
full: "3456 Broadway Ln, Los Angeles, CA",
street: "",
city: "",
state: "", zipCode: ""
},
id: "761cb6fb-0270-451e-8725-bb575eeb24d5"
},
Каждая запись представлена в интерфейсе примера в виде поля с флагом (сгенерировано функцией createContactUI), что является быстрым и простым способом отображения списка элементов, поддерживающего выбор! Конечно, ваше приложение-поставщик, вероятнее всего, будет использовать для этих целей ListView. В примере лишь сделана попытка сделать всё как можно более простым, чтобы вы могли видеть, что происходит с самим контрактом.
Когда пользователь выбирает контакт, вызывается функция примера addContactToBasket. Это тот самый момент, когда мы создаём реальный объект Contact и вызываем ContactPickerUI.addContact. За обработкой каждого поля следует цепочка вызовов других функций, поэтому посмотрим, как это работает для одного поля homeEmail в записи источника, начиная с addContactToBasket (опять же, в js/contactPickerScenario.js). Оставшиеся значения полей обрабатываются, в значительной мере, тем же способом.
function addContactToBasket(sampleContact) {
var contact = new Windows.ApplicationModel.Contacts.Contact();
contact.name = sampleContact.name;
appendEmail(contact.fields, sampleContact.homeEmail, Windows.ApplicationModel.Contacts.ContactFieldCategory.home);
// добавить другие поля...
// Добавить контакт в корзину
switch (contactPickerUI.addContact(sampleContact.id, contact)) {
// Показать различные сообщения, основанные на результате, который имеет тип
// Windows.ApplicationModel.Contacts.Provider.AddContactResult
}
}
Как вы можете видеть, поле homeEmail передается в функцию, которая называется appendEmail, первый аргумент которой – это вектор (Contact.fields), в который добавляются поля, а третий параметр – это категория (home). Затем эти данные передаются другой функции общего назначения, appendField, где типы полей объединяются, все они используются для создания объекта ContactField, и он добавляется к контакту:
function appendEmail(fields, email, category) {
// Добавляет новый адрес электронной почты к вектору полей контакта
appendField(fields, email,
Windows.ApplicationModel.Contacts.ContactFieldType.email, category);
}
function appendField(fields, value, type, category) {
// Добавляет новое поле нужного типа, либо адрес электронной почты, либо номер телефона
if (value) {
fields.append(new Windows.ApplicationModel.Contacts.ContactField(value,
type, category));
}
}
Коротко говоря, это главным образом касается того, как собраны все поля в контакте, одно за один раз.
Теперь, когда с элемента в списке снято выделение, нужно удалить его из корзины:
function removeContactFromBasket(sampleContact) {
// Программным путём удаляет контакт из корзины элементов
if (contactPickerUI.containsContact(sampleContact.id)) {
contactPickerUI.removeContact(sampleContact.id);
}
}
Похожим образом, когда пользователь удаляет элемент из корзины, поставщик контакта нуждается в обновлении интерфейса, отражающего выделение контактов, что выполняется путём обработки события contactremoved:
contactPickerUI.addEventListener("contactremoved", onContactRemoved, false);
function onContactRemoved(e) {
// Добавьте любой код, который вызывается, когда пользователь удаляет контакт из корзины
var contactElement = document.getElementById(e.id);
var sampleContact = sampleContacts[contactElement.value];
contactElement.checked = false;
}
Вы могли заметить, что мы ничего не сказали о закрытии пользовательского интерфейса, и, на самом деле объект ContactPickerUI не имеет событий для этого. Проще говоря, когда пользователь нажимает на кнопку подтверждения (с любым текстом, предоставленным вызывающим приложением), он получает всё, что поставщик добавил в корзину. Если пользователь нажмёт на кнопку отмены, операция возвратит контакт null. В обоих случаях, приложение-поставщик будет приостановлено, если оно не было запущено до того, как было активировано для получения контакта, будет автоматически закрыто.
Отметим, что как и в случае с поставщиками выбора файла, поставщик контактов должен быть котов к тому, чтобы сохранить состояние сеанса при приостановке, так, чтобы он мог восстановить это состояние при повторном запуске с previousExecutionState установленном в terminated. Хотя это не показано в примере, настоящему приложению-поставщику следует сохранять текущее выделение и позицию просмотра списка, вместе с чем угодно другим, касающимся состояния сеанса, и восстанавливать всё это, когда нужно, в обработчике activated.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.