Retour d’expérience : Passage d’une app Windows Phone Silverlight 8.0 à 8.1

La nouvelle mouture de Windows Phone 8.1 présentée hier à la Build est aussi riche en features pour les utilisateurs que pour les développeurs.

Afin de profiter des nouveautés offertes par WP8.1 j’ai donc voulu mettre une de mes applications les plus complexes à niveau.

Cette application est un bon test puisqu’elle contient des librairies C++/CX ainsi que du code C# plus classique.

Une fois Visual Studio 2013 Update 2 RC installé et exécuté, je me lance dans l’aventure!

Transformation !

Pour plus de sécurité, il est de bon ton de faire une copie du dossier de son app. On ne sait jamais.

Puis, on commence la migration de notre application vers Silverlight 8.1 (oui on a plus peur de dire Silverlight).

Pour ce faire, il suffit de cliquer droit sur la solution puis dans le menu lancer « retarget solution ».

Il est également possible d’effectuer cette migration projet par projet.

Une fenêtre apparaît ensuite nous signalant que la conversion est à sens unique.

Retarget

Il ne nous reste plus qu’à cliquer sur OK et, quelques secondes plus tard, notre projet est mise à jour :

RetargetProject

Pour l’instant tout va bien !

On se lance !

Trêve de plaisanterie, il est temps de lancer notre application fraîchement convertie.

La compilation se passe sans problème mais au lancement de l’app c’est le drame !

Elle plante lamentablement.

En regardant d’un peu plus près cela ne semble pas trop grave puisque c’est une exception sur chemin de fichier qui ne lui plait pas.

Plus précisément le chemin d’un fichier contenu dans le XAP et chargé par la commande GetFileFromPathAsync

Coté Windows Phone 8, j’obtenais le fichier de la manière suivante :

return await StorageFile.GetFileFromPathAsync("Assets/Icons/gba.png");

Windows Phone 8.1 est plus tatillon et n’accepte que les chemins absolus comme sur un système de fichier Windows classique.
En ce sens, on se rapproche de Windows 8.
Plus de ‘/’ donc mais des ‘\’ sans oublier le signe ‘@’ devant la chaine si l’on est fainéant (evite que le ‘\’ soit vu comme un caractère d’échappement et d’écrire « \\ »).

return await StorageFile.GetFileFromPathAsync(
  Path.Combine(
    Windows.ApplicationModel.Package.Current.InstalledLocation.Path,
    @"Assets\Icons\gba.png"
));

Notez, que ce code marche également parfaitement sur Windows Phone 8.0.

Une fois corrigé, l’application se lance normalement.

Conclusion

Le passage d’une application WP Silverlight 8.0 à 8.1 s’est effectuée rapidement et sans problème majeur.
La petite mésaventure du « GetFileFromPathAsync » invite néamoins à la prudence et aux tests intensifs avant une première mise en production.

Vous trouverez d’autres différences entre SL8 et SL8.1 sur cette page :
http://msdn.microsoft.com/en-us/library/windowsphone/develop/dn642084(v=vs.105).aspx

Sans device 8.1, difficile à dire s’il on bénéficie de meilleur performance pour notre app.
En revanche, on a désormais accès aux toutes nouvelles API de Windows Phone Silverlight 8.1 ce qui ouvre de nouvelles possibilités que je vais m’empresser de mettre en oeuvre. Pas vous ?

1  

Utiliser des librairies C++/CX dans Windows Phone, c’est swag !



L’ajout de components C++/CX dans un projets Windows Phone en C# permet d’associer la puissance du C++/CX à la facilité d’utilisation du C#.

C’est bon, c’est frais, c’est swag.

L’intégration de ces librairies s’effectue comme un assembly .NET classique.
Il suffit d’ajouter une référence pointant vers le fichier d’extension « .winmd » de la librairie.
Ce fichier permet de faire le lien entre notre projet et la dll C++/CX et contient des métadatas (comme son extension le laisse deviner).

Exemple avec la librairie MOGA

L’API MOGA permet de connecter à votre application une manette de jeu bluetooth.
J’ai personnellement testé le modèle Moga HERO (gentiment prêté par Mr. Alex Danvy).

Pour intégrer l’API Moga, on doit ajouter la référence à sa librairie, le fichier « Moga.Windows.Phone.winmd ».

AddReferenceMoga

Une fois référencé on peut regarder le chemin de la librairie dans la fenêtre propriétés de la référence.

MogaProperties

Le chemin qui pointe vers cette librairie contient un dossier ARM.

Lorsque l’on lance notre application en mode « Device » tout se passe bien mais dès que nous changeons la cible vers un de nos émulateurs, le compilateur nous assène un message peut avenant :

« There was a mismatch between the processor architecture of the project being built « x86″ and the processor architecture, « ARM », of the implementation file « D:\Sam\WP8\PurpleCherryX\Librairies\Moga\ARM\Moga.Windows.Phone.dll » for « D:\Sam\WP8\PurpleCherryX\Librairies\Moga\ARM\Moga.Windows.Phone.winmd ». This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and implementation file, or choose a winmd file with an implementation file that has a processor architecture which matches the targeted processor architecture of your project. »

Vilain !

A contrario d’une application .NET, les libraries sont compilées pour une cible particulière:

  • - ARM pour un téléphone.
  • - X86 pour un émulateur.

Si on regarde au même niveau d’arborescence que le dossier ARM contenant la librairie Moga, vous trouverez également un dossier x86.
C’est toujours le cas pour les librairies C++/CX.

On va donc tout simplement enlever la référence actuelle de la librairie MOGA ARM pour la remplacer par celle contenue dans le dossier x86.

Voila ça compile maintenant !

Sauf que…

Lorsque je repasse la cible de compilation en « Device » l’erreur revient mais dans l’autre sens.

Si vous êtes de nature patiente et guillerette vous allez changer de nouveau la référence de la librairie pour la faire pointer vers le dossier ARM (ou x86 selon le cas) autant de fois que nécessaire.

Pour ma part, qui suis plutôt du type de nature illustré ci-dessous, j’ai préféré la solution expliquée en détail dans le prochain paragraphe.

Angoisse

Tout est bien qui finit bien

Oyez l’amis !
Voici donc la solution qui te permettra de continuer le développement tout en restant sain d’esprit.
Il existe sans doute d’autre façon de faire, alors n’hésitez pas à commenter l’article !

Tout d’abord on se rend dans le répertoire de notre application en effectuant dans Visual Studio un bouton droit de la souris sur le projet C# puis « Open folder in file explorer ».

Dans ce dossier, on cherche le fichier du projet en cours « MonProjet.csproj » que l’on ouvre avec un gros Notepad.

A l’intérieur, on recherche la référence à notre fichier « Moga.Windows.Phone.winmd » qui nous conduit vers une une balise « HintPath ».

    <Reference Include="Moga.Windows.Phone, Version=255.255.255.255, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\Librairies\Moga\ARM\Moga.Windows.Phone.winmd</HintPath>
    </Reference>

Dans la balise « HintPath », on remplace le dossier ARM (ou x86 selon votre référence) par la variable « $(Platform) » qui rendra le chemin dynamique selon le type de cible visé (ARM ou x86)

    <Reference Include="Moga.Windows.Phone, Version=255.255.255.255, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\Librairies\Moga\$(Platform)\Moga.Windows.Phone.winmd</HintPath>
    </Reference>



On enregistre, puis on recharge le projet et le tour est joué !

0  

Viewer d’image Windows Phone 8 pour grosse feignasse

Voilà un titre qui parlera à beaucoup d’entre vous (dont moi) et qui a le mérite d’être clair sur les intentions de cet article.
Le développeur, par essence, est paresseux et il est prêt à des trésors d’espièglerie pour faire coder des morceaux de son applications par d’autres.

Windows Phone est plutôt très aimable de ce coté là car il propose quantité de classes « Launchers » et « Choosers » qui permettent de simplifier la vie du développeur tout en sécurisant son application.

L’objectifs de ces classes est de lui proposer des services systèmes prêt à l’emploi, dont l’utilisation est très aisée. Cela permet, par exemple la sélection d’une image dans la Média Library, l’écriture d’un mail, etc…

http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff769542(v=vs.105).aspx

Mais savez-vous qu’il existe d’autres « Launchers » cachés dans l’API Windows Phone ?

En tant que grosse feignasse, vous êtes désormais à l’écoute.

Un Viewer d’image ?

Il arrive souvent dans une application d’avoir besoin d’afficher une image.

Dialogues typiques :

- Tu peux rajouter un viewer de l’image du produit ?

- Héhé fastoche. Je crée une page XAML avec un contrôle Image. Hop ! c’est fini.

- Tu peux aussi rajouter un menu pour sauver l’image et, disons, la mettre en Ecran de démarrage parce que bon c’est cool non ?

- PFFFFFFF! Bon je rajoute le menu puis petite sauvegarde dans la Media Librairy puis c’est quoi la doc sur le LockScreen déjà ?

- Oui mais on aimerait pouvoir la zoomer également. Avec une inertie dans les déplacements. Ca gère le GIF au fait ?.

- …

Décès du dévéloppeur.

Farniente-Man à la rescousse !

Afin d’éviter un certain nombre d’AVC à mes concitoyens et amis développeurs voici donc le moyen d’afficher en deux lignes de code un Viewer d’image PNG, JPEG, ICO, TIFF, BMP et GIF (mais pas animé).

L’astuce consiste à enregistrer votre image dans l’isolatedStorage puis de faire un appel à LaunchFileAsync qui s’occupera d’appeler le viewer d’image du système.

// Lecture de l'image
var file = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync("ScreenShotPaddleGBA.png");
// Lancement du viewer
bool isLaunched = await Launcher.LaunchFileAsync(file);

et voila !

Viewer

Encore plus de paresse !

Ah mes amis ! Je vous vois satisfait de ce petit trick qui va vous permettre de faire plus de veille sur « Bonjour Madame » !
Sachez qu’il est aussi possible de lancer facilement d’autres viewers système en choisissant d’autres types de fichier : lecture de son, de vidéo, de fichier zip, texte et autres fichiers Office sont également possibles.

Vous trouverez la liste exhaustive des extensions réservées par le système dans ce lien.

http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj207065(v=vs.105).aspx

Pour conclure, aucun risque que votre viewer soit remplacer par une autre app car ces extensions de fichier ne sont utilisables que par le système.

A très bientôt pour de nouvelles avenZZZZZZZZZZZZ…

0  

Session Techdays 2014 : Animations et Transitions dans Windows Phone

Cette année, pour les Techdays, Jean-Sebastien Dupuy et moi-même vous avons présenté une session sur les animations à base de Storyboard et les transitions entre pages.

Vous êtes quelques-uns à m’avoir demandé les slides et le code source de la démo de cette session.
Les voici donc en attendant que le vidéo de la session soit disponible sur le site des Techdays.

Le code de l’app :
Télécharge le code source

Amusez-vous bien !

Si vous désirez rester du coté du Toolkit pour les transitions je vous engage à jeter un coup d’oeil à l’excellent article de mon grand ami et confrère, le vénérable Julien LoPresti.

2  

Un Content ON/OFF pour ToggleSwitch et CheckBox

Le ToggleSwitch est véritablement un superbe contrôle.
Enfin, au niveau design.
Car malheureusement, la personne qui l’a crée déteste manifestement le XAML.

Pour preuve, lorsque j’utilise ce contrôle dans mes applications pour créer des pages de paramétrage par exemple, je dois toujours passer par du code pour changer le label affichées à l’écran (texte « Off » sur l’image).

ToggleSwitch

C’est rageant vous ne trouvez pas ?

Changer le label par le code

Normalement pour changer le label, on affecte une chaine de caractère à la propriété Content du ToggleSwitch.
Pour que cela s’effectue lors des actions de switch on s’abonne aux événements Checked / Unchecked.
Selon l’événement on changera la propriété Content par la chaîne de caractères qui correspond à une valeur vrai ou Fausse.

NDLR : Dans cette exemple on considère que l’on désire changer des chaines de caractères mais l’on pourrait tout à fait mettre un objet quelconque dans Content (Control, Texte, Valeur,…)

Ce qu’il nous faudrait, et on se demande pourquoi cela na pas été géré ainsi depuis le début, ce sont deux propriétés « Content » supplémentaires qui correspondraient aux valeurs Vrai et Fausse.

Changer le label par le XAML

Pour rajouter ces deux propriétés, que l’on nommera « ContentOn » et « ContentOff » au contrôle ToggleSwitch, je vous propose de créer un Behavior (c’est à dire une extension des capacités d’un contrôle) à l’aide d’un assembly fournit par Blend.

NDLR : On aurait put réalisé un behavior d’une autre façon mais cette méthode me semble la plus simple et la plus extensible.

Vous pouvez référencer facilement cet assembly (System.Windows.Interactivity) dans votre app car elle est proposée comme extension dans le « Reference Manager » (accessible par le menu Project -> Add Reference).

Interactivity

On va pouvoir ajouter une classe « ToggleSwitchBehavior » qui héritera de « Behavior »

public class ToggleSwitchBehavior : System.Windows.Interactivity.Behavior<ToggleSwitch>
{
  ...
}

Celle-ci à la particularité de pouvoir se greffer au contrôle auquel elle se rattache (ici ToggleSwitch) de cette manière :

<Controls:ToggleSwitch IsChecked="{Binding IsSoundSynchronized, Mode=TwoWay}">
    <i:Interaction.Behaviors>
        <SamuelBlanchardControls:ToggleSwitchBehavior></SamuelBlanchardControls:ToggleSwitchBehavior>
    </i:Interaction.Behaviors>
</Controls:ToggleSwitch>  

On verra plus tard à quoi correspondent les namespaces i et SamuelBlanchardControls

On va ensuite déclarer nos deux propriétés « ContentOn » et « ContentOff » en tant que « Propderty Dependency » (snippet propdp). On aurait pu utiliser des propriétés classiques « get; set; » mais les propdp nous donnent la possibilité de rendre nos propriétés bindable, ce qui est un atout précieux en XAML.

        /// <summary>
        /// Postion On
        /// </summary>

        public string ContentOn
        {
            get { return (string)GetValue(ContentOnProperty); }
            set { SetValue(ContentOnProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ContentOn.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ContentOnProperty =
            DependencyProperty.Register("ContentOn", typeof(string), typeof(ToggleSwitchBehavior), new PropertyMetadata(null));

        /// <summary>
        /// Position Off
        /// </summary>

        public string ContentOff
        {
            get { return (string)GetValue(ContentOffProperty); }
            set { SetValue(ContentOffProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ContentOff.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ContentOffProperty =
            DependencyProperty.Register("ContentOff", typeof(string), typeof(ToggleSwitchBehavior), new PropertyMetadata(null));

On peut désormais attacher notre fonctionnalité de changement de Content à notre Behavior.
Pour se faire on surchage la méthode « OnAttached » de notre Behavior.

        protected override void OnAttached()
        {
            // this.AssociatedObject = ToggleSwitch
            this.AssociatedObject.Checked += AssociatedObject_Checked;
            this.AssociatedObject.Unchecked += AssociatedObject_Unchecked;

            // valuer par défaut
            this.AssociatedObject.Content = this.AssociatedObject.IsChecked == true ? this.ContentOn : this.ContentOff;

            base.OnAttached();
        }

Comme on le voit dans le code l’objet AssociatedObject correspond au control ToggleSwitch et l’on s’abonne donc aux events Checked et Unchecked.
On fixe également une valeur par défaut à notre Content qui correspond à la propriété ContentOn ou ContentOff selon la valeur de la propriété IsChecked de ToggleSwitch.

Les méthodes « AssociatedObject_Checked » et « AssociatedObject_Unchecked »
seront chargées quant à elles d’affecter à Content respectivement les valeurs ContentOn et ContentOff.

        void AssociatedObject_Unchecked(object sender, RoutedEventArgs e)
        {
            this.AssociatedObject.Content = this.ContentOff;
        }

        void AssociatedObject_Checked(object sender, RoutedEventArgs e)
        {
            this.AssociatedObject.Content = this.ContentOn;
        }

voila c’est terminé pour le code !

Intégration dans XAML

Pour pouvoir utiliser ce magnifique Behavior il suffit d’intégrer les deux namespaces suivants en entête de votre page

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:SamuelBlanchardControls="clr-namespace:SamuelBlanchard.Controls"

puis de rajouter à votre ToggleSwitch :

<Controls:ToggleSwitch IsChecked="{Binding IsSoundSynchronized, Mode=TwoWay}">
    <i:Interaction.Behaviors>
        <SamuelBlanchardControls:ToggleSwitchBehavior ContentOff="Normal" ContentOn="Best"/>
    </i:Interaction.Behaviors>
</Controls:ToggleSwitch>

A l’exécution le Content sera automatiquement fixé aux valeurs de ContentOff et ContentOn mais malheureusement cela ne sera pas le cas en mode Design.

ToggleSwitchNormal

Vous trouverez le code du Behavior en cliquant sur ce lien.

PS : J’ai rajouter également un ToggleButtonBehavior qui vous permet de faire la même chose pour les CheckBoxs par exemple. En effet les CheckBoxs ne gèrent par non plus cette fonctionnalité. Cela me fait penser que le créateur des CheckBox n’aime pas non plus le XAML ou est le même créateur que celui du ToggleSwitch ;)


0