Persistez facilement vos données sur Windows Phone 7 avec l’Isolated Storage Application Settings

janvier 10, 2012

Bonjour à tous !

Aujourd’hui, je partage un code inspiré par un helper sur lequel j’avais mis la main il y a longtemps, mis en ligne par Microsoft :)

Il permet d’utiliser une partie de l’IsolatedStorage (nommé ApplicationSettings) pour pouvoir sauvegarder des données.

Voici un rappel visuel des trois options liées au stockage local sur Windows Phone

Dans le cas de l’Application Settings, nous pouvons stocker des chaînes de caractère en leur attribuant une clef (système Key/Value Pair), qui va aussi nous permettre par la suite de récupérer l’objet.
Du coup, pour pouvoir gérer d’autres types objets, nous allons passer par une étape de sérialisation/désérialisation.

Nous partons sur une classe statique, qui va accueillir les méthodes suivantes :

public static T GetDataContractObject<T>(string key)
public static void SaveDataContractObject<T>(string key, T objectToSave)
public static void DeleteObject(string key)
public static void DeleteAll()
private static string Serialize(object objectToSerialize)
private static T Deserialize<T>(string objectToDeserialize)

Le résultat final dans une application finale Windows Phone est vraiment très simple à utiliser :


// Considérons une liste de chaîne de caractères

List<string> myCollection = new List<string>() { "valeur", "valeur 2", "valeur 3"};

// On sauvegarde l'objet
IsolatedStorageHelper.SaveDataContractObject("MyCollectionKey", myCollection)

// On récupère l'objet

List<string> myCollectionCopy = IsolatedStorageHelper.GetDataContractObject<List<String>>("MyCollectionKey")

// On supprime l'objet

IsolatedStorageHelper.DeleteObject("MyCollectionKey");

// On vide l'ApplicationSettings

IsolatedStorageHelper.DeleteAll();

Difficile de faire plus simple hein ;)

Avant ça, codons et tentons de comprendre ce helper.

On crée notre classe statique

public static class IsolatedStorageHelper

Ensuite, on va rajouter la référence vers la bibliothèque System.Runtime.Serialization, et ajouter les using :

using System.Runtime.Serialization;
using System.IO.IsolatedStorage;
using System.IO;

Commençons par la méthode Serialize : elle prend en paramètre l’objet a sérialiser.
Ce qui se passe : on sérialise l’objet, c’est à dire qu’on le transforme en un flux de données, représenté ici par la classe MemoryStream . Grâce à la classe StreamReader, on pourra représenter l’objet sous la forme d’une chaîne de caractère.

private static string Serialize(object objectToSerialize)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                DataContractSerializer serializer = new DataContractSerializer(objectToSerialize.GetType());

                serializer.WriteObject(ms, objectToSerialize);
                ms.Position = 0;

                using (StreamReader reader = new StreamReader(ms))
                {
                    return reader.ReadToEnd();
                }
            }
        }

Ensuite, cette classe sera utilisée par SaveDataContractObject, une classe qui prend en paramètre une clef, et l’objet à persister. On remarquera l’utilisation du générique T, qui représente la classe, le type de l’objet.

public static void SaveDataContractObject<T>(string key, T objectToSave)
        {
            string serializedObject = Serialize(objectToSave);
            IsolatedStorageSettings.ApplicationSettings[key] = serializedObject;
        }

Maintenant, on va créer le mécanisme inverse, en désérialisant la chaîne de caractère vers un flux de données.

private static T Deserialize<T>(string objectToDeserialize)
        {
            using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(objectToDeserialize)))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                return (T)serializer.ReadObject(ms);
            }
        }

Et la méthode liée

public static T GetDataContractObject<T>(string key)
        {
            if (IsolatedStorageSettings.ApplicationSettings.Contains(key))
            {
                string serializedObject = IsolatedStorageSettings.ApplicationSettings[key].ToString();
                return Deserialize<T>(serializedObject);
            }

            return default(T);
        }

On peut éventuellement compléter notre helper avec ces deux méthodes pour centraliser complètement la gestion de l’Isolated Storage :

 public static void DeleteObject(string key)
        {
            IsolatedStorageSettings.ApplicationSettings.Remove(key);
        }

        public static void DeleteAll()
        {
            IsolatedStorageSettings.ApplicationSettings.Clear();
        }

Voilà c’est tout !
J’ai remarqué que pas mal de personnes posaient des questions relatives au stockage isolé sur Windows Phone, si vous voulez persister des données facilement et sans vous prendre la tête, ce helper est fait pour vous :)

Quelques liens sur la persistance des données sur Windows Phone :

0

Nokia présente les Lumia 800 & 710 !

octobre 26, 2011

L’univers Nokia est en pleine effervescence aujourd’hui, et ce jusqu’à demain grâce au Nokia World qui a lieu à Londres.

Cet évènement est majeur pour Nokia, mais aussi pour Microsoft, car le partenariat entre les deux géants représente une réelle opportunité côté économique : celle pour Nokia de prendre part à la nouvelle donne instaurée par les systèmes d’exploitation (on ne voit plus un téléphone comme une entité device/OS unique, mais comme un terminal qui accueille un système précis), et celle pour Microsoft de diffuser Windows Phone et de bénéficier de l’appui de la firme finlandaise en termes de qualité des composants, qualité de la couche logicielle, mais aussi d’un appui marketing/communication vraiment des plus importants.

Lors de ce Nokia World qui a donc débuté aujourd’hui, a été présenté les deux premiers téléphones nés de l’alliance Nokia-Microsoft : les Lumia 710 et Lumia 800, sous Windows Phone 7.5 (Mango).

Le channel YouTube de Nokia permet de faire les présentations :

Nokia Drive, Maps & Music

Côté apports logiciels, on notera évidemment la présence de Nokia Drive, logiciel de navigation GPS qui semble vraiment prometteur ! Enfin une vraie application GPS avec une voix intergeangable :)
Il est aussi possible de switcher entre une vue 2D et une vue 3D, ainsi que de choisir une vue adaptée au jour ou à la nuit selon la situation.

Nokia Maps est une autre application de navigation « turn by turn », un peu à la manière de Bing Maps, mais qui ajoute des points d’intérêts.


On a aussi l’application Nokia Music, qui permet de lire des fichiers audios, mais aussi d’acheter des morceaux sur la boutique en ligne Nokia MP3.
Le côté Mix Radio donne accès à des chaines, ou d’en créer une, voire de créer des « mix » uniques à partir de morceaux présents sur la bibliothèque.

Côté hardware, le Lumia 800 reste classique, tout en étant assez massif (plus profond et plus lourd que l’Omnia 7 de Samsung par exemple), mais accueille un écran AMOLED équipé de la technologie ClearBlack, et embarque un stockage de 16Go ainsi qu’un appareil photo 8mp et le fameux objectif Carl Zeiss.

Sur le Lumia 710, plus modeste, on trouve un écran TFT plus classique (toujours avec ClearBlack cependant !), et avec un appareil photo 5mp doté d’objectif plus commun.

Notez que The Phone House propose déjà le Lumia 800 à partir de 99€ avec abonnement, et 499€ sans (dont 50€ remboursés si acheté avec fin décembre).

Pour suivre le Nokia World en live c’est ici !
Pour découvrir le Nokia Lumia 800 c’est plutôt là.

0

Nokia Developer Training à Paris !

septembre 24, 2011

Nokia Developer Training

Bonjour à tous !

Hier j’ai eu la chance d’assister au Nokia Developer Training qui se tenait à CAP 15, au Quai de Grenelle à Paris.
Le but de cette conférence était de faire découvrir le développement d’application sur Windows Phone 7.5.
A ma grande surprise, la conférence était animée notamment par Rob Miles et Andy Wigley !

Andy Wigley


Sa fiche MVP
Son blog chez Appa Mundi
Rob Miles


Sa fiche MVP
Son blog

Le lieu de réception était vraiment sympa, nous avons été bien accueillis.
On a d’abord eu une introduction de Nokia (par Julien Amouroux je crois) sur le partenariat avec Microsoft, les enjeux actuels, etc. On nous a bien fait comprendre que les applications étaient le « nerf de la guerre », qu’un smartphone était smart grâce à celles-ci, et que de ce fait les développeurs avaient une place de choix dans la « bataille ». Ce champ lexical guerrier ne me plait guère, et il n’est pas adapté – à mon sens – à un public de développeurs : le partage, l’humilité et la quête du savoir sont des valeurs clefs du bon développeur d’après moi.

Outre Rob Miles et Andy Wigley, Gregg Lebovitz (un « Nokia Champion », semblable à un MVP à la sauce finlandaise) était là pour assurer les questions qui pouvait se poser entre le développement sous les systèmes Nokia comparé à celui sur Windows Phone.

En réalité, cette journée était un sorte de Jump Start pour Windows Phone « Mango », suite à celui sur Windows Phone 7 animé par le même duo.

Côté thèmes abordés, on avait

Silverlight sur Windows Phone
Windows Phone Fast Application Switching & Tombstoning
XNA for Windows Phone
Présentation d’une application style to-do list sur Mango, englobant quelques API comme la gestion d’état, le stockage local, les alerts & reminders, les Tiles, etc…
Le Marketplace et la vente d’une application

Etant donné que j’avais déjà développé sur Windows Phone, ce qui m’intéressait réellement c’était le point de vue d’un maître : j’ai été servi, c’est le moins qu’on puisse dire :)
Rob et Andy, comme a leur habitude, on dispensé une formation très agréable : détendue, technique mais souvent accompagnée de métaphores très pertinentes, ponctuée de quelques « jokes ». Leur bonne humeur et leur esprit didactique est vraiment à la hauteur de leur technique.

Ce que j’en retiens surtout côté développement, c’est l’importance du cycle de vie de l’application Windows Phone, qui est propre au device et qui demande une attention particulière : l’application peut-être lancée, mise en arrière plan si un appel survient, réactivée, ou complètement effacée de la mémoire.
Aussi, je n’utilisais absolument pas les Tiles secondaires, qui permettent depuis Mango d’afficher des « tuiles » en plus sur l’écran d’accueil, on peut par exemple mettre une tuile consacrée à un élément d’une To-Do List, un peu comme on peut déjà le faire avec OneNote.

Parmi les choses qui étaient en désaccord avec ce que je pensais, il y avait ce propos de Rob sur la visibilité d’une application sur le Marketplace. Sur Windows Phone, nous avons la possibilité de créer facilement une version d’essai d’une application ou d’un jeux, et il était précognisé d’utiliser cette pratique plutôt que de faire des applications version « light » ou « free », dans le but d’éviter les multiplications inutiles des applications comme on en trouve sur l’App Store par exemple. C’était selon moi quelque chose de très intéressant et d’important, et ça ajoutait à la qualité du Marketplace Windows Phone.
Cependant, Rob a fait une remarque très pertinente : une application payante qui possède une version d’essai n’apparaît pas dans la catégorie « gratuits » du Marketplace, et comme une partie des utilisateurs ne regardent QUE les applications gratuites, on prive celle-ci d’une visibilité certaine.
Parmi d’autres remarques utiles, Rob nous a dit que changer de temps en temps la catégorie de l’application permettait de toucher à chaque fois une partie du marché différente, et d’en déduire celle dans laquelle elle se portait le mieux, ce qui sert entre autre à relancer un peu l’application, puisqu’il faut savoir que la plus grand nombre de téléchargement aura lieu dans les premières semaines suivant la sa publication sur le Marketplace.

A la fin de cette journée, nous avons été tous très gâtés par Microsoft, qui nous a gentillement remis des devices Samsung Focus AT&T débloquables avec un code qu’ils nous ont fourni !
J’avoue avoir été un peu « déçu » de ne pas avoir de prototype Nokia à la place, mais c’était inattendu et vraiment génial de leur part. Tout les ingrédients étaient réunis pour que les développeurs présents gardent un très bon souvenir de cette journée, et c’est un pari réussi par Nokia & Microsoft. Je suis vraiment pressé de pouvoir mettre la main sur un Nokia sous Mango :)

Encore merci à Nokia pour cette super journée, passée en plus en compagnie d’un de mes meilleurs amis, nous étions au premier rang tels de bons élèves ^^

0

Les couleurs d’accent de Windows Phone

septembre 9, 2011

Voici un bref article qui liste les couleurs d’accent disponibles sous Windows Phone 7.1 (beta), sans les couleurs personnalisées des opérateurs :)

Pour ceux qui ne le savent pas, on peut aisément récupérer la couleur d’accent choisie par l’utilisateur en faisant appel aux ressources :

Côté XAML =>

<TextBlock Foreground="{StaticResource PhoneAccentBrush}" />

Côté C# =>

SolidColorBrush accentColor = Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush;

Windows Phone Accent Colors

0

Utiliser le LongListSelector

août 25, 2011

LongListSelectorBonjour à tous !
Après un long silence, je refais surface en vous proposant de découvrir un contrôle du dernier Silverlight for Windows Phone Toolkit.

Si vous avez manqué cet évènement, vous pouvez aller de ce pas le télécharger sur codeplex, et jeter un coup d’oeil aux samples.

Côté nouveauté, on a notamment ce fameux LongListSelector, que vous connaissez certainement déjà. Il est vraiment propre à l’univers Windows Phone et vraiment appréciable côté utilisateur.

Pour mettre en place ce composant, nous aurons d’abord besoin d’une classe qui hérite de IEnumerable, qui nous permettra d’introduire une notion de groupe.
C’est effectivement de ça qu’il s’agit : associer des éléments à un groupe.
Celui qui m’intéressait était celui qu’on connait bien, le groupement par ordre alphabétique.

Nous allons d’abord créer cette classe, puis créer une méthode qui permet d’ajouter des objets à des groupes.
Dans mon application, j’ai centralisé la classe et la méthode dans un Helper, une classe statique.

public class Group<T> : IEnumerable<T>
    {
        public Group(string key, IEnumerable<T> items)
        {
            this.Key = key;
            this.Items = new List<T>(items);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            Group<T> that = obj as Group<T>;

            return (that != null) && (this.Title.Equals(that.Title));
        }

        public string Key
        {
            get;
            set;
        }

        public bool HasItems
        {
            get { if (Items.Count == 0) { return false; }else return true; }
        }

        public IList<T> Items
        {
            get;
            set;
        }

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return this.Items.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.Items.GetEnumerator();
        }

        #endregion

Si on regarde les paramètres du constructeur, on voit qu’on a une chaîne de caractère et une collection générique. La chaîne de caractère représente le nom du groupe, et la collection références tous les objets de ce groupe.
En clair, un groupe « P » et une liste de fruits « Pomme », « Poire », « Pêche » par exemple.

Ensuite il faut pouvoir faire des groupes par ordre alphabétique, c’est là qu’intervient la méthode.

Imaginons des objets Fruit avec des propriétés string Nom, string Variété, DateTime DebutSaison et DateTime FinSaison.

public static ObservableCollection<Group<Fruit>> CreateWidgetGroups(ObservableCollection<Fruit> fruits)
        {
            ObservableCollection<Group<Fruit>> groups = new ObservableCollection<Group<Fruit>>();
            string chars = "#abcdefghijklmnopqrstuvwxyz";

            var source = from f in fruits
                         orderby f.Nom
                         select f;

            // On compare chaque caractère avec le premier de chaque objet de notre liste
            // si ils correspondent, on ajoute l'objet au groupe du caractère précédement créé
            foreach (char c in chars)
            {
                Group<Widget> group = new Group<Fruit>(c.ToString().ToLower(), new List<Fruit>());
                foreach (Fruit fruit in source)
                {
                    if (fruit.Nom.Substring(0, 1) == c.ToString() || fruit.om.Substring(0,1) == c.ToString().ToUpper())
                        group.Items.Add(widget);
                    // On centralise les chiffres dans le groupe #
                    else if (c.ToString() == "#")
                    {
                        try
                        {
                            if ((Convert.ToInt32(fruit.Nom.Substring(0, 1)) >= 0))
                            {
                                group.Items.Add(fruit);
                            }
                        }

                        catch (FormatException ex)
                        {
                            System.Diagnostics.Debug.WriteLine(ex.Message);
                        }
                    }

                }
                groups.Add(group);
            }

Voilà, nous avons une méthode capable de créer un groupe pour chaque lettre et d’y ajouter les objets concernés.

Maintenant, il reste encore à créer la vue !
Imaginons que dans notre base de données, nous ayons aussi une illustration de chaque fruit stockée sous forme de chaîne de caractère.

Dans un premier temps, nous allons créer nos DataTemplate, qui servent à décrire la façon dont les données seront affichées.
Rendons nous dans phone:PhoneApplicationPage.Resources pour les créer.

Le contrôle LongListSelector propose un large choix de DataTemplate, mais ici trois suffiront : l’ItemTemplate, le GroupHeaderTemplate et le GroupItemTemplate.

Le premier définit le bloc qui représente chaque fruit, ici une image et nos propriétés énumérées plus haut :

<DataTemplate x:Key="itemTemplate">
            <Grid Margin="12,8,0,8">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Image Height="62" Width="62" Source="{Binding Path=Image, Converter={StaticResource ImageConverter}}"></Image>
                <StackPanel VerticalAlignment="Top" Grid.Column="1">
                    <TextBlock Text="{Binding Nom}" Style="{StaticResource PhoneTextLargeStyle}"  />
                    <TextBlock Text="{Binding Variete}" Style="{StaticResource PhoneTextSmallStyle}"/>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="Début de saison "  Margin="12,3,5,0" Style="{StaticResource PhoneTextSmallStyle}"></TextBlock>
                        <TextBlock Text="{Binding DebutSaison}"  Margin="0,3,0,0" Style="{StaticResource PhoneTextSmallStyle}"/>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="Finde saison "  Margin="12,3,5,0" Style="{StaticResource PhoneTextSmallStyle}"></TextBlock>
                        <TextBlock Text="{Binding FinSaison}"  Margin="0,3,0,0" Style="{StaticResource PhoneTextSmallStyle}"/>
                    </StackPanel>
                </StackPanel>
            </Grid>
        </DataTemplate>

Passons ensuite au GroupHeaderTemplate, qui définit le bloc qui affiche le nom du groupe en tête de chaque groupe :

 <DataTemplate x:Key="groupHeaderTemplate">
            <Border Background="Transparent" Margin="12,8,0,8">
                <Border Background="{StaticResource PhoneAccentBrush}"
                                        Padding="8,0,0,0" Width="62" Height="62"
                                        HorizontalAlignment="Left">
                    <TextBlock Text="{Binding Key}"
                                               Foreground="#FFFFFF"
                                               FontSize="{StaticResource PhoneFontSizeExtraLarge}"
                                               HorizontalAlignment="Left"
                                               VerticalAlignment="Bottom"/>
                </Border>
            </Border>
        </DataTemplate>

Et enfin, passons au GroupItemTemplate, qui définit la manière dont sera affichée la liste des groupes quand on clique sur un « GroupHeader » [qu'on vient de définir plus haut !]

 <DataTemplate x:Key="groupItemTemplate">
            <Border Width="99" Height="99" Margin="6" toolkit:TiltEffect.IsTiltEnabled="True" Background="{Binding HasItems, Converter={StaticResource BackgroundConverter}}" IsHitTestVisible="{Binding HasItems}">
                <TextBlock Text="{Binding Key}"
                          FontSize="{StaticResource PhoneFontSizeExtraLarge}"
                           FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                                           Margin="8,0,0,0"
                                           VerticalAlignment="Bottom"
                           Foreground="{Binding HasItems, Converter={StaticResource ForegroundConverter}}"
                           />
            </Border>
        </DataTemplate>

Je sais que vous avez l’oeil et que vous avez du coup remarqué la présence de Converters, et de cette propriété HasItems.
Vous vous en doutez, il faut que le groupe soit cliquable uniquement quand il contient au moins un item, sans quoi il faut en informer visuellement l’utilisateur et empêcher le click.
C’est à ça que sert la propriété IsHitTestVisible de Border.
HasItems renvoit true si la collection contient plus de zéro élément.

Si vous ne savez pas vraiment utiliser les converters, je vais vous expliquer comment procéder.

Les converters s’utilisent en général lors des bindings, quand vous voulez …convertir [évidemment :) ] la valeur passé par le binding vers une autre.
Ici, HasItems nous envoie un type Bool, et nous voulons selon sa valeur changer le Background ou Foreground, qui sont des types SolidColorBrush.
En faisant en binding sans converter de Background= »{Binding HasItems} », ça plantera à coup sûr, ça revient à écrire Background = true, ou false.
Du coup, nous allons passer par un converter, qui au moment du binding, va prendre notre valeur, et la …convertir [vous vous attendiez à quoi ? :) ] en un autre.

public class BackgroundConverter:IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool hasItems = (bool)value;
            if (hasItems)
                return Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush;
            else return Application.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // Do something with exception
        }
    }

Voilà, notre HasItem nous retournera une SolidColorBrush qui nous permettra de donner un look « activé » ou « désactivé » à nos items :)

Ah aussi, pour ceux qui ne sont pas habitués aux ressources, il faut évidemment référencer ces converters, soit dans l’App.xaml, soit dans les ressources de la page (comme illustré ci-dessous)

xmlns:converters="clr-namespace:MonApplication.NamespaceDuConverter">
<phone:PhoneApplicationPage.Resources>
<converters:BackgroundConverter x:Key="BackgroundConverter"></converters:BackgroundConverter>
...
</phone:PhoneApplicationPage.Resources>

Sans quoi, la ressource n’existerait pas :)

0

Les frame rate counters

février 15, 2011
frame_rate_counter

Pour peu que vous ayez déjà lancé l’émulataur de Visual Studio, ou mieux un device réel pendant le développement d’une application, vous avez sans doute remarqué la présence de compteurs sur le côté droit de l’écran.
Ces compteurs sont en fait de très utiles indicateurs de performances, je vais en décrire seulement 3 ici pour le moment :

  • Le Compositor Thread counter(Render Thread) -> avec une valeur exprimée en FPS. Il prend en charge la plupart des animations, en-dessous de 30fps, le compteur vire au rouge. Il opère principalement avec le GPU et le cache bitmap
  • L’UI Thread counter -> aussi exprimé en FPS, il est impacté par la création de composants, les composants recréés (changement de couleur, de marge ou de bordure par exemple). Il vous alertera en tombant sous les 15fps.
  • Le Fill Rate counter -> Il mesure le travail effectué par le GPU, exprimé en terme d’écrans (1 = 480×800). La valeur recommandée est d’environ 2.5, le compteur vire au rouge au-dessus de 3.

Il est vraiment important de prêter une attention particulière à ces compteurs, pour fournir la meilleure expérience utilisateur possible, d’autant plus que Windows Phone 7 brille par sa fluidité.
Nous verrons dans d’autres posts comment s’illustrent ces différents acteurs de performance dans la pratique, avec quelques « best-practices » si vous ne les connaissez pas déjà :)

0

Afficher des icones selon le thème utilisé

janvier 10, 2011

Il est très important de tester son application aussi bien sur le thème « Dark » que le thème « Light », sans quoi cette dernière se verra recalée au moment de la certification !

Pour ceux qui ne savent pas, vous pouvez récupérer les icônes de l’Application Bar dans C:\Program Files\Microsoft SDKs\Windows Phone\v7.0\Icons, en thème « Dark » et « Light ».

L’Application Bar, elle, adaptera ses icônes au thème si vous utilisez les icônes du thème « Dark ». En revanche, si vous utilisez celles du thème « Light », ou que vous utilisez ces icônes en dehors de l’Application Bar, ce ne sera pas le cas.

Pour pouvoir afficher des icônes en fonction du thème choisi, j’ai décidé de créer une fonction booléenne dans App.xaml.cs, qui me retourne vrai si le thème « Light » est le thème courant, faux dans le cas contraire.

public bool LightThemeIsEnabled()
        {
            return (Visibility)Application.Current.Resources["PhoneLightThemeVisibility"] == Visibility.Visible;
        }

Pour l’utiliser, il suffit ensuite d’ajouter une ligne :

                myImage.Source = (App.Current as App).LightThemeIsEnabled() ? new BitmapImage("myIconLight") : new BitmapImage("myIconDark.png);

Pour ceux qui ne connaissent pas cette syntaxe, on peut la traduire par : maVariable = condition ? alors : sinon;

Voilà, cet article parle d’un détail, mais qui a vraiment son importance ^^

PS : ne pas oublier que l’ApplicationBar prendra des icônes sous forme de « Contenu ».

0

Convertir un type string en enum

décembre 20, 2010

Voici une petite astuce que j’ai utilisée aujourd’hui, ce n’est pas grand chose, mais ça peut toujours servir !

Prenons l’exemple de DayOfWeek, qui énumère les jours de la semaine.
Pour « convertir » un type string en DayOfWeek, nous allons utiliser une méthode statique de la classe Enum.

Exemple :

string day = "Monday"
DayOfWeek dayOfWeek =
      (DayOfWeek)Enum.Parse(typeof(DayOfWeek), "Monday", true);

Et voilà =)
J’avais dit que c’était pas grand chose !

0

Lire un flux RSS avec Silverlight

novembre 22, 2010

RSS_icon

J’ai vu vraiment beaucoup de méthodes pour parvenir à lire un flux RSS avec Silverlight, mais étant « partisan du moindre effort », j’aime utiliser la combinaison WebClient/SyndicationFeed, une manière simple et rapide d’y parvenir.

  1. Créer la classe Article
  2. Cette étape est – je pense – facultative : vous pouvez très bien garder un objet de type SyndicationItem et faire votre propre truc avec.

    En tout cas, voici la classe en question, volontairement très restreinte et très simple.

     public class Article
        {
            private string _titre;
            private string _lien;
            private string _contenu;
    
            public string Contenu
            {
                get { return _contenu; }
                set { _contenu = value; }
            }
    
            public Article()
            {
            }
    
            public string Lien
            {
                get { return _lien; }
                set { _lien = value; }
            }
    
            public string Titre
            {
                get { return _titre; }
                set { _titre = value; }
            }
        }
    
  3. Le WebClient
  4. Pour utiliser un flux RSS, nous allons utiliser la classe WebClient, en nous intéressant particulièrement à l’évènement OpenReadCompleted et la méthode OpenReadAsync.

    L’évènement OpenReadCompleted se produit quand la lecture asynchrone du flux est terminée, alors que la méthode OpenReadAsync permet justement d’ouvrir un flux à partir d’une uri passée en paramètre.

    On peut donc déjà créer la méthode ReadRSS(), par exemple en utilisant le flux RSS de ce blog =)

    public void ReadRSS()
            {
                WebClient client = new WebClient();
                client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
                Uri uri = new Uri("http://ashworks.fr/blog/?feed=rss2", UriKind.Absolute);
                client.OpenReadAsync(uri);
            }
    

    Pour ceux qui ne connaissent pas vraiment les raccourcis de Visual Studio, après avoir écrit client.OpenReadCompleted, en appuyant deux fois sur tab, le gestionnaire d’évènement sera créé automatiquement.

  5. La classe SyndicationFeed
  6. Avant toute chose, vous devez ajouter l’assembly System.ServiceModel.Syndication.dll, qui se trouve dans Program Files\MicrosoftSDKs\Silverlight\v4.0\Libraries\Client. Notez qu’elle est aussi présente dans la v3.0.

    Il faut ensuite ajouter le namespace System.ServiceModel.Syndication à votre fichier.

    A ce moment du code, il faudra écrire la logique du gestionnaire d’évènement fraîchement créé.
    C’est ici qu’intervient notre classe SyndicationFeed, qui permet – selon moi – de simplifier un maximum la récupération des données du flux.
    On utilise une liste d’objet Article, un XmlReader qui prendra en paramètre le résultat de OpenReadCompletedEventArgs, qui est notre flux sous forme de stream (ça sonne redondant hein). Ce même XmlReader passera en paramètre de la méthode Load de notre SyndicationFeed.

    Ensuite, nous avons accès à une liste de SyndicationItems, qui représentes toutes vos entrées dans le flux (dans notre cas, les articles de ce blog), avec des propriétés très claires comme le titre, une liste de liens, le contenu (même si pour celui-ci, il faudra fouiller un peu comme dans le code ci-dessous), etc…

     void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
            {
                List<Article> articles;
                XmlReader rssReader = XmlReader.Create(e.Result);
                SyndicationFeed feed = SyndicationFeed.Load(rssReader);
    
                articles = (from article in feed.Items
                           select new Article
                           {
                               Titre = article.Title.Text,
                               Lien = article.Links.First().Uri.ToString(),
                               Content = article.ElementExtensions[2].GetObject<XElement>().Value.ToString()
                           }).ToList();
            }
    

    A vous de voir comment vous voulez utiliser cette liste, mais en mettant un point d’arrêt dans la boucle, vous verrez qu’elle se charge effectivement avec le contenu du flux !

  7. Conclusion
  8. Voilà, c’est tout ! Voyez qu’il est vraiment très simple d’utiliser les flux RSS avec Silverlight, et ça marche très bien aussi sous Windows Phone 7.
    D’ailleurs, si tout ce passe bien, un lecteur RSS de base de ce blog sera disponible sur le MarketPlace (avec un design et des fonctionnalités vraiment minables, mais c’est une question de temps et son but est juste de valider mon compte étudiant sur l’AppHub ^^)

1

Reproduire l’animation des Tiles WP7 avec Turnstile

novembre 16, 2010

WP7_TileSi il y a bien quelque chose de très agréable chez Windows Phone 7, c’est cette animation constante alliée à une fludité assez impressionnante.
Grâce au travail de Roberto Sonnino, nous pouvons utiliser cet effet assez aisément.

Ici, je vais créer un menu à la mode WP7, composé de ces fameuses Tiles, et de cette animation appellée Turnstile.

  1. Installation du composant
  2. Dans un premier temps, il vous faudra télécharger les sources depuis Codeproject.
    Ensuite, il faut ajouter la référence à la dll en faisant un clic droit sur le projet, « ajouter une référence », et dans l’onglet « browse » vous irez retrouver les sources téléchargées précédemment.
    Enfin, pour que vous puissiez utiliser ce composant, il vous faudra ajouter deux xml namespace de la manière suivante :

    xmlns

    Le premier namespace fait référence aux sources de Turnstile, l’autre permet d’utiliser le composant WrapPanel, qui ne vous est sûrement pas inconnu si vous avez déjà touché à Silverlight auparavant.
    On peut ensuite utiliser le contrôle

    Turnstile

  3. Création d’une classe Tile
  4. Puisque qu’une Tile possède des propriété fixes, et peut être considérée comme composite, j’ai choisi de créer une classe dédiée, qui hérite de la classe Canvas.

    Voici sa déclaration

    public class Tile:Canvas
        {
            private ImageBrush _image { get; set; }
            public Uri uri;
    
            public Tile(string name, string imageSource, Uri uri)
            {
    
                this.uri = uri;
    
                #region Canvas
                this.Name = name;
                this.Height = 173;
                this.Width = 173;
                this.Background = new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"]);
                this.Margin = new Thickness(5);
                #endregion
    
                #region Label
                TextBlock label = new TextBlock();
                label.Text = name;
                label.Height = 23;
                label.Foreground = new SolidColorBrush(Colors.White);
                label.Margin = new Thickness(10, 140, 0, 0);
                this.Children.Add(label);
                #endregion
    
                #region Image
                _image = new ImageBrush();
                _image.ImageSource = new BitmapImage(new Uri(imageSource, UriKind.Relative));
                _image.Stretch = Stretch.UniformToFill;
                #endregion
    
                #region Icon
                Canvas icon = new Canvas();
                icon.Height = 80;
                icon.Width = 80;
                icon.Margin = new Thickness(45, 45, 0, 0);
                icon.Background = _image;
                this.Children.Add(icon);
                #endregion
            }
    
  5. Créer une liste de Tiles, et les ajouter à notre composant
  6. C’est le moment de nous attaquer un peu au code-behind de notre application ! Nous allons déclarer nos Tiles en dur, vu que c’est un menu, nous choisissons précisément ce que nous voulons y mettre.
    Nous allons donc créer une liste de Tiles, et la méthode DisplayTiles() qui permettra d’afficher le contenu de celle-ci à l’écran. Nous allons aussi ajouter la logique d’interaction sur l’évènement Click d’une Tile

    La méthode DisplayTiles()

    public void DisplayTiles()
            {
                #region TileList
                Tile registration = new Tile("Registration", "/myApplication;component/Resources/Images/Register.png", new Uri("/Views/targetUri.xaml", UriKind.Relative));
                Tile calendar = new Tile("Schedule", "/myApplication;component/Resources/Images/calendar.png", new Uri("/Views/targetUri.xaml", UriKind.Relative));
                TileList.Add(registration);
                TileList.Add(calendar);
                #endregion
    
                #region Turnstile items and Tiles event handling
                foreach (Tile tile in TileList)
                {
                    tile.MouseLeftButtonDown += new MouseButtonEventHandler(tile_MouseLeftButtonDown);
                    turnstile.Items.Add(tile);
                }
                #endregion
            }
    

    …et la gestion d’évènement

    void tile_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                Tile currentTile = (Tile)sender;
                try
                {
                    NavigationService.Navigate(currentTile.uri);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
    
  7. Animation
  8. Pour finir, il faudra ajouter des éléments propres à l’utilisation du Turnstile, notamment un objet

    EnterMode _currentEnterMode = EnterMode.Enter;

    la méthode AnimateTiles

     private void AnimateTiles(YDirection yDirection, ZDirection zDirection)
            {
                _currentEnterMode = _currentEnterMode == EnterMode.Enter ? EnterMode.Exit : EnterMode.Enter;
                turnstile.AnimateTiles(_currentEnterMode, yDirection, zDirection);
            }
    

    la logique de l’évènement SizeChanged

    SizeChanged += (s, e) => turnstile.AnimateTiles(_currentEnterMode, YDirection.TopToBottom, ZDirection.BackToFront);
  9. Compiler et lancer l’application
  10. Si je n’ai rien oublié, en appuyant sur F5 vous devriez obtenir un résultat satisfaisant, même si c’est assez lent, on accusera l’émulateur. Je n’ai malheureusement pas encore pu tester ce contrôle sur un vrai device.

    Je rajouterai dès que le temps me le permet, une solution complètement barbare pour gérer l’animation selon si l’on arrive sur la page via le lancement de l’application, ou le bouton back (on remarque que l’animation change de sens selon ces critères sur WP7).

    A très bientôt !

8