Astuce : Tuile a 5 faces (Windows 8.1)

by Nicolas Calvi 22. août 2013 21:50

Voici une petite astuce qui peut être intéressante pour les développeurs Windows 8.1 (et par extension sur Windows 8 ça marche aussi). Comment faire pour créer une tuile ayant 5 Templates différents qui défilent et ce dans les trois tailles (carré, rectangle et large).

En fait ce n'est pas si sorcier que ça. Tout d'abord il déterminer les Templates que vous voulez afficher, pour cela vous pouvez les consulter tous les Templates sur MSDN, ce qui vous donnera une bonne vision de ce qu'il est possible de faire avec les tuiles sous Windows 8.1.

Vous avez trouvé de quoi vos tuiles vont être constituées ? Il n'y à plus qu'a la mettre en place.

Pour définir votre tuile dynamique, il faut générer un XML qui décrit ce que vous voulez afficher et les données qui les constituent. Pour cela deux moyen de créer ce XML. La première façon est de récupérer le template d'une tuile avec la fonction statique :

XmlDocument template = Windows.UI.NotificationsTileUpdateManager.GetTemplateContent(TileTemplateType type)

Vous pouvez donc récupérer les templates un par un et les modifier avec les API XML.

La seconde façon de faire est de constituer son XML en mode "texte" classique, de le charger dans une instance XmlDocument. Les deux techniques reviennent au même, donc après c'est une question de point de vue et de façon d'implémenter les choses.

Si l'on veut par exemple avoir 5 templates différents de tuile "wide", il faut créer un XML de cette forme (on peut bien sûr alterner les différents Templates, aucune nécessiter d'avoir 5 fois le même) :

<tile>
  <visual>
    <binding template="TileWideImage" branding="None">
      <image id="1" src="ms-appx:///Assets/Tiles/Tile00.png"/>
    </binding>
  </visual>
</tile>

Cette portion de XML permet de générer une face de notre tuile, elle sera de type "TileWideImage" et utilisera une ressource image qui est contenue dans mon applicatif. Vous remarquez l'attribut "branding" sur l'élément "binding", il permet de ne pas afficher le logo de l'application dans le coin inférieur gauche, il faut donc le mettre à "None" et c'est réglé.

Ceci est donc une des faces de notre tuile dynamique, si on veut donc avoir jusqu'a 5 faces, il faut générer 5 XML de ce type. Mais une fois générer, comment on les affecte ? Voici comment on procède :

// Considérons nos 5 XML correspondant
// aux 5 faces de notre tuile dynamique
XmlDocument tile1 = ... ;
XmlDocument tile2 = ... ;
XmlDocument tile3 = ... ;
XmlDocument tile4 = ... ;
XmlDocument tile5 = ... ;

// Création de l'instance qui permet
// d'affecter notre tuile dynamique
TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();

// On active le défilement de nos faces
tileUpdater.EnableNotificationQueue(true);

// On transforme nos XML en TileNotification
// qui représente une face convertie dans
// le système de tuile dynamique Windows 8
TileNotification tn1 = new TileNotification(tile1);
TileNotification tn2 = new TileNotification(tile2);
TileNotification tn3 = new TileNotification(tile3);
TileNotification tn4 = new TileNotification(tile4);
TileNotification tn5 = new TileNotification(tile5);

// Ensuite on supprime toutes les définitions
// de face déjà enregistrées pour cette tuile
tileUpdater.Clear();

// Pour finir on ajoute nos nouvelles définitions
tileUpdater.Update(tn1);
tileUpdater.Update(tn2);
tileUpdater.Update(tn3);
tileUpdater.Update(tn4);
tileUpdater.Update(tn5);

Bien sûr j'ai volontairement décomposé toutes les étapes pour bien comprendre ce qu'il se passe. Une fois que vous avez fait toutes ses opérations, votre tuile dynamique est prête et s'exécute sans soucis. 

Dans le cas décrit ci-dessus, cela ne crée qu'une tuile dynamique en mode "Wide". Pour gérer les autres tailles ("square" et "large"), il vous suffit d'ajouter d'autres Templates, Windows 8 fera le tri au moment de l'affichage.

ItemTemplate Windows 8

by Nicolas Calvi 29. avril 2013 16:00

Un petit post pour vous partager un petit soucis que j'ai rencontrer. Je me suis créer des "ProjectTemplates" et "ItemTemplates" pour mes projets MVVM sous Windows 8. Là où la création du "ProjectTemplate" se passe sans soucis, je me suis aperçu que mes "ItemTemplates" ne s'affichais pas quand je faisais "Nouvel élément".

En fait, Visual Studio 2012 exporte ceci dans son fichier "MyTemplate.vstemplate" :

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item">
  <TemplateData>
    <DefaultName>ItemView.xaml</DefaultName>
    <Name>Black Windows 8 Empty View</Name>
    <Description>Vue pour être utilisée dans une pattern MVVM Black Blog</Description>
    <ProjectType>CSharp</ProjectType>
    <SortOrder>10</SortOrder>
    <Icon>__TemplateIcon.jpg</Icon>
    <PreviewImage>__PreviewImage.jpg</PreviewImage>
  </TemplateData>
  <TemplateContent>
    <References />
    <ProjectItem SubType="Designer" TargetFileName="$fileinputname$.xaml" ReplaceParameters="true">HomeView.xaml</ProjectItem>
    <ProjectItem SubType="Code" TargetFileName="$fileinputname$.xaml.cs" ReplaceParameters="true">HomeView.xaml.cs</ProjectItem>
  </TemplateContent>
</VSTemplate>

Or avec cette définition, l'ItemTemplate ne s'affiche pas dans ma liste. En cherchant un peu je me suis rendy compte qu'il manquait en fait un élément de définition dans le fichier. Si j'ajoute cette ligne dans le fichier de définition :

<TemplateGroupID>WinRT-Managed</TemplateGroupID>

Mon "ItemTemplate" s'affiche bien, il faut juste le savoir.

Localisation Dynamique

by Nicolas Calvi 17. avril 2013 16:32

Une de mes problématiques en ce moment est liée à la localisation de mes applications. En effet, sous Windows Store App, si l'on veut switcher d'une langue à l'autre, il faut soit changer la langue de son OS et relancer l'application (si celle-ci utilise la localisation à partir des ressources) ou alors créer son système custom et tout ça doit être bien intégré dans votre pattern MVVM, or moi je voudrais faire ça à la volé. Bref un vrai casse-tête.

Personnellement j'ai planché un peu sur le problème et j'ai donc trouvé une première solution, certes ce n'est pas spécialement la meilleur ou la plus performante, mais elle offre je pense un bon compromis entre Resources / Binding / MVVM. Voilà en substance ma solution personnelle :

Le principe est de conserver le système de ressource (ici en rouge avec les fichiers "Resources.resw" et "Resources.lang-fr-FR.resw"), de créer une classe de gestion des ressources (ici en rouge avec la classe "ResourcesSwitcher") et pour faire le lien entre les deux par du Binding, un Converter (ici en rouge avec la classe "LanguageConverter"). Je vous laisse regarder en détail le code source (lien du projet en fin d'article), je vais juste faire un focus sur les éléments importants.

Tout d'abord il faut récupérer le contenu des ressources via la classe du Framework "ResourceManager", cela permet d'avoir l'arbre des différents dictionnaires dans les différentes langues :

// Récupération de la map
ResourceMap map = ResourceManager.Current.MainResourceMap.GetSubtree("Resources");

Ensuite, il faut créer les contextes, cela représente au final les langues que l'ont veut traiter, cela sera utile plus tard quand on voudra récupérer des informations dans la "map" des ressources, en spécifiant la langue le Framework pourra allez chercher la bonne valeur dans la bonne langue.

ResourceContext english = new ResourceContext() { Languages = new List<string>() { "en-Us" } };
ResourceContext french = new ResourceContext() { Languages = new List<string>() { "fr-FR" } };

Pour finir, pour récupérer une valeur dans la "map" des ressources il suffit de faire ceci :

map.GetValue("CleDictionnaire", french).ValueAsString

Ça c'est pour la récupération d'un élément localisé dans les ressources par le code. Vous comprenez donc que pour passer d'une langue à l'autre il suffit de changer le deuxième paramètre de "GetValue()", par exemple on peut remplacer le "ResouceContext" "french" par "english".

Ensuite il faut faire le lien entre le XAML et ce mécanisme. Moi j'ai choisi de créer une classe ("ResourcesSwitcher") qui sera une classe avec propriété notifiée qui exposera la "map". Pour créer un singleton de cette classe, je la déclare dans mon "App.xaml" :

<Application x:Class="BlackBlog.DynamicLocalization.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:conv="using:BlackBlog.DynamicLocalization.Converters"
             xmlns:vcore="using:BlackBlog.DynamicLocalization.Views.Core">

    <Application.Resources>
        <ResourceDictionary>

            <!--  Converteur pour récupérer les conversions  -->

            <conv:LanguageConverter x:Key="LanguageConverter" />

            <!--  Singleton pour le moteur de conversion  -->

            <vcore:ResourcesSwitcher x:Key="ResourcesSwitcher" />

        </ResourceDictionary>
    </Application.Resources>
</Application>

Au passage vous remarquerez que j'ai déclaré aussi un Converter. Celui-ci va servir a extraire une valeur (avec le "GetValue()" de la "map") depuis le XAML en utilisant le "ConverterParameter". En gros ça donne ca si on essaye de binder une ressource sur une propriété "Text" d'un "TextBlock" :

<TextBlock FontSize="16"
                Foreground="White"
                Text="{Binding Source={StaticResource ResourcesSwitcher},
                                      Path=Map,
                                      Converter={StaticResource LanguageConverter},
                                      ConverterParameter='Sentence'}" />

Avec ce système on peut donc binder nos ressources sur nos contrôles. Mais à quoi ça sert tout ça me direz-vous ! Et bien justement, si vous y regardez de plus prês, vous remarquez que nous sommes en binding, qui dit binding dit notification. Comme je l'ai souligné plus haut, dans ma classe "ResourcesSwitcher" j'ai exposé la "Map" via une propriété notifiée, donc il suffit de gérer qu'elle langue est sélectionnée actuellement (pour ma part j'ai mis une propriété dans mon "ResourcesSwitcher"), et au moment du changement de langue il suffit de notifier cette propriété, et là le moteur de binding va réévaluer toutes vos binding et repasser par le converter :

public object Convert(object value, Type targetType, object parameter, string language)
{
    // Récupération de la Map de ressource
    ResourceMap map = value as ResourceMap;

    if (map != null && !string.IsNullOrWhiteSpace((string)parameter))
        return (map.GetValue((string)parameter, ResourcesSwitcher.Instance.CurrentLanguage).ValueAsString);

    // Pas de convertion

    return (value);
}

Et dans le converter, je vais chercher la langue en sélection pour l'évaluation de la valeur. Grâce à ça vous avez un système de localisation dynamique.

Ceci est bien sûr une solution parmi tant d'autre, qui a ses avantages et ses défaut, libre à vous l'améliorer. Vous pouvez télécharger le code par ce lien :

BlackBlog.DynamicLocalization.rar (130,97 kb)

Session Kinect - Slides et démos !

by Nicolas Calvi 14. février 2013 12:41

La session "Kinect en moins de 10 minutes (LAN205)" que j'ai partagé avec mes compères du Kinect Genius Bar, Johanna Rowe et Vincent Guigui, a eu du succès, je remercie au passage tous ceux qui sont venu nous voir.

En attendant la Webcast qui arrivera vers le 18 mars, je vous mets en ligne les slides et le code source des démos.

LAN205 - Kinect en moins de 10 Minutes - Demo.rar (15,53 mb)

LAN205 - Kinect en moins de 10 Minutes - Slides.pdf (1,64 mb)

J'espère que cela vous aura plus !

Techdays TV !

by Nicolas Calvi 11. février 2013 16:33

Vous pouvez suivre les Techdays 2013 en live grâce à la Techdays TV ! Ne vous en privez pas :)

Get Microsoft Silverlight

Et si vous êtes sur place, n'hésitez pas à allez voir ma session : Kinect MD10, qui fera un tour d'horizon des fonctionnalités Kinect en 5 exemples de moins de 10 minutes.