Quand la boite de dialogue n’est plus ton ami.

Bien que j’apprécie l’API WinRT pour développer des apps universelles je ne peux m’empêcher de penser que la peinture n’est pas totalement sèche en ce qui concerne Windows (Phone) 8.1.
L’objet de cet article est de vous montrer un exemple de problème apporté par ce manque de finition mais également un moyen simple de le contourner.

MessageDialog m’a tué

Lorsque je veux communiquer des messages importants à l’utilisateur de mon app je lui affiche une boite de dialogue.
En Silverlight cela se passe comme ceci :

MessageBox.Show("Hello world!");

En WinRT c’est à peine plus compliqué :

var dialog = new MessageDialog("Hello world!");
await dialog.ShowAsync();

Jusqu’ici tout va bien.

Imaginons maintenant qu’un DispatcherTimer ou une Task de votre cru décide de lancer une seconde boite de dialogue pendant que la première est affichée que va t’il se passer ?

En Silverlight, la seconde boite attendra sagement son tour et s’affichera lorsque la première sera fermée.

Nice !

WinRT considère qu’une seule boite de dialogue peut être affichée à la fois n’en déplaise aux travailleurs parallèles. Une exception sera donc levée lors de l’affichage de la seconde boite et fermera votre application.

Sad !

sh nazg durbatulûk

J’aimerai retrouver un comportement de boite de dialogue similaire à celle de Silverlight.
A savoir, lorsque plusieurs boites doivent être affichées au même moment, elles attendront leur tour pour s’afficher.

On va donc mettre en place un sémaphore qui sera chargé d’attendre le tour du ShowAsync de MessageDialog.

static SemaphoreSlim semaphore = new SemaphoreSlim(1);

public static async Task ShowOrWaitAsync(this MessageDialog dialog)
{            
	await semaphore.WaitAsync();

	try
	{
		await dialog.ShowAsync();
	}
	finally
	{
		semaphore.Release();
	}
}

On préférera utiliser ici SemaphoreSlim plutôt qu’un Mutex par exemple pour sa capacité à travailler de manière asynchrone.

La méthode ayant été déclarée comme méthode d’extension de « MessageDialog » grâce au paramètre « this » nous allons pouvoir écrire pour chaque boîte de dialogue à appeler le code suivant :

MessageDialog dialog = new MessageDialog("Test 0");
await dialog.ShowOrWaitAsync();

Elle attendra patiemment son tour en cas de conflit d’affichage.

Merci qui ?

Vous trouverez la solution du projet ici. La classe à inclure dans votre projet s’appelle « DialogManager ».En Bonus, elle gère également les ContentDialog de Windows Phone qui souffre du même mal.

Je trouve vraiment dommage que ce genre de scénario, finalement assez courant, ne soit pas prise en charge nativement par l’API.
Mais Windows 10 arrive bientôt, et j’ai espoir que tous ces petits tracas seront bientôt de l’histoire ancienne.

Win10IsComing

0  

Règle « Association d’URI » pour le concours « Docteur Souris »

Nokia organise via DVLUP un concours qui me tient particulièrement à cœur puisqu’il s’agit de faire une bonne action en amusant les enfants et adolescents des hôpitaux par le biais d’application sur téléphone et tablette (Windows Phone et Windows).

Comme Nokia a également un grand cœur il permettra de gagner des téléphones dernière génération ou des points DVLUP (Chouette !).
Faisant partie du jury je ne pourrai malheureusement pas participer au concours mais je me frotte les mains en pensant aux futures applications présentées.

Trêve de bavardage, pour participer et connaitre les règles du concours rendez-vous directement à cette url :
https://www.dvlup.com/Challenge/599

Une dernière règle importante

Parmi les quelques règles énoncées sur DVULP il en manque une sur le site DVLUP (pour le moment) particulièrement importante :

Les applications doivent impérativement associer une URI du type :
masuperapp:docteursouris
ou masuperapp sera le nom de votre application.

Dans le cas où le nom de votre app correspondrait à un schéma réservé (http: par exemple) vous pouvez le remplacer par le schèma de votre choix.

Mais pourquoi cette règle ?

Le but ultime de ce concours est d’obtenir une application « Docteur Souris » capable de lancer d’autre apps les applications de ce concours.
Or pour lancer des apps à partir d’une autre app, il faut qu’elles soient pourvues d’une association d’URI.

En suivant ce lien vous trouverez, en détail, comment implémenter cette association d’URI :
http://msdn.microsoft.com/fr-FR/library/windows/apps/xaml/hh779670

et un exemple complet ici :
http://code.msdn.microsoft.com/windowsapps/Association-Launching-535d2cec/

Conclusion

Ne passer pas à côté de cette règle importante du concours car elle seul permettra aux enfants de s’amuser sur nos smartphones et tablettes en toute simplicité.
Bon courage et à vos clavier !

PS: Un grand merci à Kévin Trélohan pour l’idée et la mise en place de ce concours avec l’aide toujours efficace et conviviale d’Olivier Lovisa de chez Nokia. Une spécial dédicace pour Franck Nguessan qui s’occupe de la réalisation de l’application lanceur « Docteur Souris ».

0  

Connectivity version 12 pour WP8.1



« Microsoft.Smartdevice.Connectivity » est une librairie fournit par Microsoft qui permet de se connecter à Windows Phone (device ou emulateur) afin gérer les applications développeurs (installation, désinstallation, lancement) et de lire leurs IsolatedStorages.

Si vous êtes intéressé par le fonctionnement de base de cette DLL, vous pouvez relire cet article sur le sujet

La nouvelle version de Connectivity vient d’arriver avec le SDK WP8.1, c’est le moment de faire le point sur les nouveautés.
Malheureusement, cette librairie n’a pas, à ma connaissance, de page officielle mise à jour sur MSDN.
Je vais donc vous livrer les informations que j’ai pu recueillir en la décompilant et qui m’ont servies à mettre à jour mon application IsoStoreSpy.

De nouvelles racines

Globalement, la librairie fonctionne de la même manière que ses prédécesseurs mais une différence de taille est venu se glisser dans cette release.
En effet, Il est possible d’accéder, sous certaines conditions, à d’autre répertoire que celui de l’isolatedStorage.
Dans les cadres des applications WP8.1, trois racines sont désormais accessibles (contre une auparavant) :

- « Local » qui représente l’IsolatedStorage classique (valeur par défaut)
- « Roaming » qui permet d’accéder au données du Roaming
- « Temp » qui est un dossier temporaire

On accède donc à l’IsoStore en ajoutant simplement la chaine d’une des racines à la méthode GetIsolatedStore :

// WP8 old Style:
// var storage = SelectedApplication.GetIsolatedStore();

// WP8.1 new Style :
var storage = SelectedApplication.GetIsolatedStore("Roaming");

Et les apps WP8 ?

Malheureusement, les racines « Roaming » ou « Temp » ne sont pas accessibles des applications WP8 même sur un device WP8.1 (ce qui est facilement compréhensible vu qu’il n’y a pas d’accès niveau code).

Il faut donc une manière simple de détecter qu’une app est compiler pour WP8 ou pour WP8.1.

On va utiliser ici l’exception ArgumentException générer par l’accès aux méthodes de Connectivity dans un contexte de Roaming.

        public bool IsSelectedApplicationIsWP8
        {
            get
            {
                if( _IsSelectedApplicationIsWP8 == null )
                {
                    try
                    {
                        this.SelectedApplication.GetIsolatedStore("Roaming").GetDirectoryListing(string.Empty);
                        _IsSelectedApplicationIsWP8 = false;
                    }
                    catch (ArgumentException)
                    {
                        _IsSelectedApplicationIsWP8 = true;
                    }
                }

                return _IsSelectedApplicationIsWP8.Value;
            }
        }

        private bool? _IsSelectedApplicationIsWP8 = null;

On n’a plus qu’a tester IsSelectedApplicationIsWP8 pour voir si l’on peut afficher ou non les racines complémentaires.

Des chemins d’accès modifiés

Les chemins d’accès au répertoires et fichiers ont également été modifiés par l’utilisation de ces nouvelles racines :

ainsi pour les devices WP8 la racine de base de l’application était la suivante :

string root = @"%FOLDERID_APPID_ISOROOT%\" + APP_PRODUCT_ID + "\"

elle devient celle ci pour WP8.1 :

string root = @"%FOLDERID_APPID_ISOROOT%\" + APP_PRODUCT_ID + "\" + RACINE + "\" 

la chaine APP_PRODUCT_ID represente le GUID de l’app et RACINE une des chaines suivantes :

- « %LOCL% » pour la racine Local
- « %ROAM% » pour la racine roaming
- « %TEMP% » pour la racine temp

Détruire des fichiers

Une coquille c’était glisser dans la dernière version de Connectivity pour WP8. Il était impossible de détruire des fichiers.
C’est ennuyeux mais heureusement cette fonctionnalité est de nouveau opérationnelle !

conclusion

Je me doute que cet article ne servira qu’a très peu de personnes vu la nature des services de cette librairie mais sachez que plusieurs Bothans sont morts pour nous fournir ces informations.
Ca calme.

0  

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