Conception de Windows 8
Blog Windows Store pour les développeurs
IEBlog Français
Blog Visual Studio
Blogs de l'équipe Windows
Blog Windows Live
Télécharger Windows 8 Release Preview
Centre de développement : applications de style Metro
Suivez-nous @windevs
Conférence BUILD de Windows //build/
Développement d’applications Metro
Le style Metro consiste notamment à créer des vues claires et précises permettant de mettre le contenu au premier plan et d'offrir aux utilisateurs la possibilité d'accomplir plus facilement leurs tâches. Dans ce billet, nous expliquons notamment comment créer de superbes galeries à partir du contenu présent dans les fichiers et dossiers de l'utilisateur, stockés dans le système de fichiers local. La possibilité d'afficher du contenu local est une exigence essentielle pour les nombreuses applications de galerie qui permettent aux utilisateurs de parcourir et d'exploiter leurs contenus, qu'il s'agisse de photos, de vidéos, de musique ou de documents. Windows 8 offre des outils permettant de concrétiser cet objectif de façon simple, efficace et personnalisable.
Pour vous le prouver, je vais prendre l'exemple de l'application PhotoJournal, un journal photo en ligne permettant aux utilisateurs d'afficher et de gérer leurs photos et leurs vidéos à travers une vue chronologique. L'image ci-dessous montre la page d'arrivée de l'application. Entre autres vues, cette application crée notamment une présentation chronologique du système de fichiers : elle affiche ainsi les photos récemment publiées dans l'application et stockées dans son dossier de données local. Elle offre également une vue des résultats de recherche, qui permet à l'utilisateur de trouver des photos spécifiques. Dans ce billet, j'explique en détail les deux étapes permettant à l'application de créer ces vues :
Figure 1 : application PhotoJournal
Pour créer la vue, la première étape consiste à préparer la source de données. Pour créer une vue basée sur le système de fichiers, le mieux est de bâtir une requête de fichiers chargée de renvoyer les fichiers souhaités. Les requêtes de fichiers constituent un moyen simple et rapide d'accéder à des données locales. En outre, elles sont actualisées en permanence en fonction de l'état du système de fichiers.
Comme vous pouvez le constater sur ce graphique, les utilisateurs possèdent parfois de très gros volumes de données, notamment des photos numériques accumulées au fil du temps, depuis la démocratisation des appareils numériques et des smartphones. En raison de l'importance de ces volumes de données, vous devez créer des vues suffisamment pertinentes dans un large éventail de scénarios, visant aussi bien l'utilisateur moyen, qui possède seulement quelques milliers de photos, que le passionné de photo qui en possède plusieurs dizaines de milliers. Pour cela, vous devez exploiter les avantages des filtres et des champs dynamiques, pour que la taille de chaque vue reste gérable. Je vous recommande de ne pas affecter par défaut à votre vue de galerie une liste plate de l'intégralité du contenu de l'utilisateur, mais plutôt de réfléchir quelques instants à ce à quoi doit ressembler votre requête. Ainsi, il peut suffire de suivre l'arborescence du système de fichiers pour fragmenter la collection en jeux de données plus petits.
Figure 2 : nombre de fichiers par utilisateur et par type de contenu
Au moment de définir la requête, réfléchissez aux principaux avantages offerts par votre application et à la façon dont ces atouts s'appliquent au scénario que vous souhaitez mettre en œuvre : qu'est-ce qui fait qu'un élément spécifique revêt une importance particulière pour l'utilisateur dans le contexte de votre application ? PhotoJournal a pour principal intérêt d'afficher de façon dynamique des photos et d'utiliser les données complémentaires les concernant. Ainsi, la date de prise de vue et le contexte (lieu, par exemple) constituent des champs dynamiques utiles pour présenter les photos à l'utilisateur, sous forme de filtre, de critère de tri ou de libellé.
Voici quelques exemples de vues que vous pouvez utiliser dans une application de type galerie. Toutes peuvent être créées à l'aide des API fournies par Windows 8.
Vous pouvez implémenter tous les exemples examinés à l'aide de la syntaxe de recherche avancéeAQS (Advanced Query Syntax), un langage pris en charge par le champ de recherche de l'Explorateur de fichiers et par la fonctionnalité de recherche de fichiers. AQS est un outil très puissant qui permet d'affiner les résultats en fonction des métadonnées et du contenu. Grâce à cet outil, vous pouvez récupérer uniquement les données dont vous avez besoin et les organiser comme vous le souhaitez à l'aide des API de requête. AQS exploitant l'index du système, vous pouvez récupérer les résultats et afficher vos vues bien plus rapidement qu'en filtrant manuellement l'ensemble de fichiers.
Dans notre cas, PhotoJournal met l'accent sur les photos récentes et utilise AQS pour afficher sur sa page d'accueil uniquement les images prises il y a moins d'un mois. Pour que l'application donne l'impression d'afficher une chronologie, le mieux est d'utiliser une liste plate triée par date.
// Create a new file query from the pictures library and apply the AQS filtervar picturesLibrary = Windows.Storage.KnownFolders.picturesLibrary;var options = new Windows.Storage.Search.QueryOptions(Windows.Storage.Search.CommonFileQuery.orderByDate, [".jpg", ".png"]);// Use the app search filter to enforce business logicoptions.applicationSearchFilter = "System.Photo.DateTaken:> System.StructuredQueryType.DateTime#LastMonth";var fileQuery = picturesLibrary.createFileQueryWithOptions(options);
// Create a new file query from the pictures library and apply the AQS filtervar fileTypeFilter = new string[] { ".jpg", ".png" };var options = new Windows.Storage.Search.QueryOptions(Windows.Storage.Search.CommonFileQuery.OrderByDate, fileTypeFilter);// Use the appl search filter to enforce business logicoptions.ApplicationSearchFilter = "System.Photo.DateTaken:> System.StructuredQueryType.DateTime#LastMonth";var fileQuery = Windows.Storage.KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(options);
En revanche, dans les résultats de recherche, les éléments recherchés par l'utilisateur doivent avoir la priorité. Par conséquent, au lieu d'utiliser le même filtre de date que dans la vue chronologique, l'application exploite la chaîne de requête de l'utilisateur pour filtrer la requête par mot clé et afficher uniquement les données pertinentes. Voici comment procéder.
// Create a new file query from the pictures library and apply the AQS filtervar picturesLibrary = Windows.Storage.KnownFolders.picturesLibrary;var options = new Windows.Storage.Search.QueryOptions(Windows.Storage.Search.CommonFileQuery.orderByDate, ["*"]);// Use the text provided in the search box as the user search filteroptions.userSearchFilter = queryText;var fileQuery = picturesLibrary.createFileQueryWithOptions(options);
// Create a new file query from the pictures library and apply the AQS filtervar fileTypeFilter = new string[] { "*" };var options = new Windows.Storage.Search.QueryOptions(Windows.Storage.Search.CommonFileQuery.OrderByDate, fileTypeFilter);// Use the application search filter to enforce business logicoptions.UserSearchFilter = queryText;var fileQuery = Windows.Storage.KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(options);
Une fois que vous avez défini la requête, quelques lignes de code suffisent pour la rendre affichable dans un contrôle ListView. Pour cela, la solution la plus simple consiste à utiliser l'objet StorageDataSource. Pour créer un objet StorageDataSource, vous devez fournir des informations supplémentaires, en fonction de ce à quoi doit ressembler votre vue.
Lorsque vient le moment de créer la source de données, vous devez décider du type de miniature souhaité. Windows Runtime vous offre le choix entre plusieurs modes de miniature, selon ce que vous souhaitez en faire. Le mode de miniature définit un certain nombre de paramètres, par exemple le recadrage. PhotoJournal est une galerie d'images et spécifie donc le mode picturesView dans le constructeur StorageDataSource. Par conséquent, toutes les miniatures sont renvoyées recadrées, avec le même rapport hauteur-largeur de 0,7 (pour que la présentation soit plus harmonieuse, le nombre d'or est utilisé). picturesView est le standard largement accepté pour l'affichage de photos dans une application de style Metro.
La taille des miniatures indique la taille prévue de la miniature. Le système met en cache les miniatures à des tailles prédéfinies utilisées dans l'ensemble du système : le fait d'utiliser l'une de ces tailles permet à votre application d'adopter l'apparence du style Metro et de s'affranchir des ralentissements liés à un redimensionnement. Voici une liste des modes de miniature disponibles et des tailles que nous recommandons d'utiliser pour chacun d'entre eux.
Mode de miniature
Taille de requête recommandée
Valeur renvoyée
PicturesView/VideosView
190
Miniature recadrée de 190 x 130 pixels (lorsque cela est possible) et de rapport hauteur-largeur fixe. Le système met en cache les miniatures à cette taille, ce qui améliore les performances.
MusicView
256
Miniature carrée de 256 x 256 pixels (lorsque cela est possible). Le système met en cache les miniatures à cette taille, ce qui améliore les performances.
DocumentsView/ListView
40
Miniature carrée de 40 x 40 pixels (lorsque cela est possible) et de rapport hauteur-largeur fixe.
SingleItem
16, 32, 48, 96, 256, 1024
Miniature du fichier affichée avec le même rapport hauteur-largeur que le fichier d'origine, où la taille demandée correspond à la taille du bord le plus long. Pour améliorer les performances, six tailles différentes sont mises en cache.
Tableau 1 : tailles recommandées pour les miniatures
Vous pouvez faire en sorte que les miniatures utilisées avec votre source de données fonctionnent quel que soit le facteur d'échelle actuel. Pour cela, transmettez l'option Windows.Storage.FileProperties.ThumbnailOptions.useCurrentScale au constructeur de la source de données. Ainsi, il vous suffit de demander la taille correspondant à la présentation de votre application, à un facteur d'échelle de 100 %. Si vous spécifiez une taille nulle, cela signifie que vous n'avez pas l'intention d'utiliser des miniatures dans votre vue et celles-ci ne sont donc pas récupérées.
Maintenant que je sais à quoi ma collection doit ressembler, je peux créer l'objet de source de données qui pourra être utilisé par le contrôle ListView pour afficher ma galerie.
// Set data source optionsvar dataSourceOptions = { // Options to retrieve thumbnails mode: Windows.Storage.FileProperties.ThumbnailMode.picturesView, requestedThumbnailSize: 190, // 0 means that thumbnails should not be retrieved thumbnailOptions: Windows.Storage.FileProperties.ThumbnailOptions.useCurrentScale};// Create the data sourcevar dataSource = new WinJS.UI.StorageDataSource(fileQuery, dataSourceOptions);
Nous sommes convaincus que de nombreuses applications utiliseront des configurations StorageDataSource similaires, qui reflèteront les usages courants fonctionnant parfaitement avec chaque type de contenu (les images, par exemple). Pour faciliter votre travail, nous fournissons des préréglages chargés de configurer StorageDataSource en utilisant ces configurations optimales, le tout dans une seule ligne de code. Dans le cas de PhotoJournal, je peux obtenir le même résultat qu'avec le code que nous venons d'examiner, mais en utilisant beaucoup moins de lignes de code. Pour cela, j'utilise la configuration par défaut pour les images.
// Shorthand datasource (similar shorthand constructors are available for Videos,// Music and Documents)var dataSource = new WinJS.UI.StorageDataSource("Pictures");
En C#, la sémantique liée à la création d'une source de données diffère quelque peu. Plutôt que de créer un objet StorageDataSource, vous devez utiliser la classe FileInformationFactory. Le code suivant vous montre comment utiliser cet objet pour obtenir un vecteur de fichiers virtualisé, qui correspond à la construction que vous pouvez utiliser avec un contrôle GridView.
var fileInformationFactory = new FileInformationFactory( fileQuery, Windows.Storage.FileProperties.ThumbnailMode.PicturesView, size, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, true);
Le contrôle StorageDataSource et la classe FileInformationFactory fonctionnent parfaitement si votre application utilise uniquement des champs dynamiques courants intégrés au système de fichiers. Si jamais vous avez besoin d'appliquer une logique métier aux données affichées, vous pouvez créer votre propre source de données dans le système de fichiers.
Si vous agrégez des données issues de plusieurs sources (cloud, base de données personnalisée et système de fichiers), par exemple, votre application doit implémenter sa propre source de données. En outre, si vous avez besoin de filtrer, de trier ou de regrouper des éléments en fonction d'une logique métier propre à l'application (date de publication de la photo dans PhotoJournal, par exemple), vous devez également disposer de votre propre source de données. Vous pouvez quand même utiliser les API de modèle de données pour interroger les données, mais vous devez ajouter votre propre couche de filtrage et gérer la manière dont les données sont virtualisées et renvoyées au contrôle ListView, ce qui nécessite un peu plus de travail.
S'il est indispensable de collecter uniquement les contenus pertinents pour éviter de submerger l'utilisateur de données inutiles, pour que votre application sorte véritablement du lot et se distingue, vous devez concevoir une présentation élégante et esthétique. Comme toujours, la présentation exacte dépend de l'objectif de votre application et du type de contenu à afficher.
PhotoJournal est axé sur les photos. Aussi, il suit les recommandations des principes du style Metro : contenu mis au premier plan, risques de distraction limités et expérience utilisateur rapide et fluide. Pour voir comment ces recommandations sont mises en œuvre dans la pratique, essayez le sélecteur de fichiers :
Voici à quoi ressemble une recherche dans PhotoJournal après mise en œuvre de ces principes de conception.
Figure 3 : mise en avant des images et des propriétés correspondant à la requête de recherche de l'utilisateur
Les autres types de contenus obéissent à d'autres règles. Par exemple, dans une application musicale, l'important est de pouvoir identifier rapidement le titre du morceau ou de l'album et le nom de l'artiste, tout en affichant également la pochette, pour accélérer la navigation. D'un autre côté, les documents ne se prêtent pas facilement aux représentations visuelles enrichies, car ils sont avant tout basés sur du texte. Dans ce cas, utilisez un modèle d'élément qui accorde une plus grande importance au texte et aux détails de chaque élément.
Si votre vue suit un modèle hiérarchique où les fichiers et les dossiers sont affichés ensemble, pensez à différencier de façon visuelle les deux types d'éléments. Pour cela, vous pouvez suivre les modèles définis par le sélecteur de fichiers de Windows. L'utilisateur voit ces modèles dans l'ensemble du système, ce qui les rend d'autant plus familiers et identifiables. En les utilisant, vous permettez à votre application de s'intégrer de façon transparente à Windows 8.
Figure 4 : utilisation de superpositions dans le sélecteur de fichiers, pour différencier les dossiers
Une fois que vous avez décidé à quoi doit ressembler votre vue, vous pouvez procéder à son affichage.
En matière d'affichage des vues, la notion de modèle d'élément fait partie des concepts essentiels. Lorsque vous créez une vue, tous les éléments affichés sont mis en forme en utilisant un modèle commun, qui accueille les informations relatives à l'élément (miniature, nom, date, etc.).
En JavaScript, avec le contrôle ListView, le meilleur moyen de définir l'apparence de vos modèles consiste à utiliser du code CSS basé sur des classes ListView.
.imageGallery .win-container{ margin-right: 10px; margin-bottom: 10px;}.imageGallery .win-item{ width: 190px; height: 130px; overflow: hidden; background-color: #333333;}.imageGallery .win-item img { width: 190px; height: 130px; overflow: hidden;}
Pour afficher des éléments dans votre vue, l'approche la plus courante consiste à écrire une méthode de modèle d'élément que le contrôle ListView appelle pour créer chacun des éléments de la vue. Cette méthode est appelée une fois pour chaque élément, lorsque celui-ci doit être affiché à l'écran. On parle dans ce cas de « rendu par programme ». Pour chaque élément, vous créez des éléments DOM à la volée, par programmation.
La gestion de la qualité des miniatures peut s'avérer complexe. La première fois que l'utilisateur affiche une photo sur le système, Windows renvoie une miniature rapide à faible résolution pour améliorer la réactivité, puis crée ensuite une miniature de plus haute résolution. Si vous souhaitez vous affranchir de cette complexité et laisser le système gérer ces événements, vous pouvez utiliser la fonction d'assistance LoadThumbnail du contrôle StorageDataSource, qui fait abstraction des détails et veille simplement à ce que la miniature demandée soit insérée dans l'élément d'image fourni. Le rendu des éléments se limite alors à quelques lignes de code :
function storageRenderer(itemPromise, element) { var img, itemStatus; if (element === null) { // dom is not recycled, so create inital structure element = document.createElement("div"); element.className = "FileTemplate"; element.appendChild(document.createElement("img")); } img = element.querySelector("img"); img.style.opacity = 0; return { // returns the placeholder element: element, // and a promise that will complete when the item is fully rendered renderComplete: itemPromise.then(function (item) { // now do cheap work (none here, so we return item ready) return item.ready; }).then(function (item) { // wait until item.ready before doing expensive work return WinJS.UI.StorageDataSource.loadThumbnail(item, img).then(function (image) { // perform any operation that requires the thumbnail to be available }); }) };}
Une fois que vos styles et votre dispositif de rendu sont prêts, vous n'avez plus qu'à initialiser le contrôle ListView. Votre application de galerie est alors prête :
var container = document.getElementById("listviewDiv");var listViewOptions = { itemDataSource: dataSource, itemTemplate: storageRenderer, layout: new WinJS.UI.GridLayout(), selectionMode: "single"};var listViewControl = new WinJS.UI.ListView(container, listViewOptions);
Remarque : les développeurs JavaScript peuvent utiliser une approche similaire à celle mise en œuvre dans l'exemple XAML que nous allons examiner dans un instant. Ainsi, ils peuvent effectuer un rendu déclaratif grâce au code HTML. Pour en savoir plus, voir l'exemple StorageDataSource. Le rendu déclaratif est plus facile à appréhender, mais le rendu par programme offre plus de flexibilité.
En XAML, vous utilisez une approche qui diffère légèrement de la méthode JavaScript évoquée ci-dessus. Cette approche consiste à écrire une partie du code XAML représentant le modèle à utiliser, puis à lier ce modèle au contrôle GridView ou ListView. On parle dans ce cas de « rendu déclaratif ». Dans ce cas, le contrôle ListView se charge lui-même de créer les éléments pour tous les objets de la vue et utilise les liaisons pour remplir ces éléments en utilisant les données adéquates. Vous pouvez lier au contrôle n'importe quelle propriété exposée par l'objet FileInformation.
<UserControl.Resources> <local:ThumbnailConverter x:Key="thumbnailConverter"/> <DataTemplate x:Key="Custom190x130ItemTemplate"> <Grid Width="190" Height="130"> <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="190" Height="130"> <Image Source="{Binding Path=Thumbnail, Converter={StaticResource thumbnailConverter}}" Width="190" Height="130"/> </Border> </Grid> </DataTemplate> <!-- Collection of items displayed by this page --> <CollectionViewSource x:Name="itemsViewSource"/></UserControl.Resources><!-- Horizontal scrolling grid --><GridView x:Name="itemGridView" AutomationProperties.AutomationId="ItemGridView" AutomationProperties.Name="Items" Grid.Row="1" Margin="0,-4,0,0" Padding="116,0,40,46" ItemsSource="{Binding Source={StaticResource itemsViewSource}}" ItemTemplate="{StaticResource Custom190x130ItemTemplate}" SelectionMode="None"/>
Si une propriété doit être traitée avant d'être liée (c'est par exemple le cas du flux de miniatures, qui ne peut pas être directement lié à un objet Image et qui doit au préalable être décodé), vous pouvez déclarer des convertisseurs de valeur pour procéder au traitement.
internal class ThumbnailConverter : IValueConverter{ public object Convert(object value, Type targetType, object parameter, string culture) { if (value != null) { var thumbnailStream = (IRandomAccessStream)value; var image = new BitmapImage(); image.SetSource(thumbnailStream); return image; } return DependencyProperty.UnsetValue; } public object ConvertBack(object value, Type targetType, object parameter, string culture) { throw new NotImplementedException(); }}
Vous pouvez ensuite configurer le contrôle GridView avec le VirtualizedItemsVector obtenu auprès de l'objet FileInformationFactory.
itemsViewSource.Source = fileInformationFactory.GetVirtualizedFilesVector();
Vous êtes maintenant prêt à vous lancer et à créer de superbes vues du système de fichiers. Néanmoins, votre travail ne se limite pas à créer une seule vue. Dans l'idéal, une application doit répondre aux besoins de l'utilisateur et adapter chaque écran à l'opération qu'il souhaite accomplir. En écrivant du code supplémentaire, vous pouvez par exemple proposer des options permettant de faire pivoter des données ou d'utiliser le zoom sémantique, pour améliorer l'expérience utilisateur.
Dans l'exemple de l'application PhotoJournal, je peux très bien décider d'ajouter des champs dynamiques basés sur le modèle d'appareil photo ou la date. Ces champs dynamiques me permettent de restreindre le périmètre de la requête de fichiers à l'aide d'AQS et de présenter systématiquement à l'utilisateur un nombre d'images qui reste gérable. À chaque fois que le champ dynamique change, il me suffit d'exécuter une nouvelle requête et d'intervertir les sources de données dans mon contrôle ListView, puis d'abandonner l'ancienne requête et la source de données, afin d'éviter une trop forte consommation de ressources de la part de l'application. Je peux facilement ajouter ces champs dynamiques à la vue de recherche examinée plus haut. Voici le résultat :
Figure 5 : Ajoutez des champs dynamiques à votre vue pour aider les utilisateurs à filtrer les contenus
Le zoom sémantique constitue une autre solution très utile pour permettre à votre application de se démarquer, au prix de quelques lignes de code. Si les champs dynamiques ne fonctionnent pas correctement dans votre application ou si les vues dynamiques contiennent toujours un nombre d'éléments trop important, sachez que le zoom sémantique vous permet d'afficher une vue agrégée du contenu, que l'utilisateur peut évaluer d'un simple coup d'œil.
Figure 6 : zoom sémantique
Dans ce billet, vous avez découvert comment utiliser StorageDataSource et VirtualizedItemsVector avec des contrôles JavaScript ou XAML, afin de créer des vues enrichies du système de fichiers pour votre application de galerie. Quelques rappels :
Vous êtes maintenant prêt à créer une formidable application de galerie !
--Marc Wautier, chef de projet, Expérience utilisateur Windows
Lien
Type
Points forts
Prise en main : ajout d'un contrôle ListView
Guide de prise en main destiné aux développeurs
Explique comment créer un contrôle générique ListView
Exemple StorageDataSource et GetVirtualizedFilesVector
Exemple de code
Montre comment utiliser StorageDataSource et GetVirtualizedFilesVector pour créer une vue basée sur des fichiers locaux
Modèles d'éléments pour les présentations en grille
Fragments de code
Fournit des exemples de modèles d'éléments pour le contrôle ListView
StorageDataSource et FileInformationFactory
Guide de référence des API
Guide de référence des API StorageDataSource et FileInformationFactory