Archives de Catégorie: Windows Phone 7

Toutes les actualités sur le WP7 de Microsoft

[WP7] HttpWebRequest et problèmes d’encodage

Introduction

Lorsque vous réalisez une HttpWebRequest en Windows Phone 7, vous risquez d’avoir une réponse dans un encodage différent d’UTF8. Ce qui peut entrainer des caractères illisibles à la place des lettres accentuées (ex : “?” au lieu de “é”).

Pour remédier à cela, on va réencoder notre chaîne grâce à la classe System.Text.Encoding.

Explication par le code

String resultString;
HttpWebResponse response = 
      (HttpWebResponse)request.EndGetResponse(result);
//On définit l'encodage de base de notre chaîne
var source = Encoding.GetEncoding("iso-8859-1");
//On définit l'encodage de de destination
var dest = Encoding.UTF8;

/*Lors de la création de notre StreamReader, 
* on précise l'encodage de départ*/
using (StreamReader streamReader1 =
      new StreamReader(response.GetResponseStream(),
          source))
{
      resultString = streamReader1.ReadToEnd();
 }
 if (!String.IsNullOrEmpty(resultString))
{                    
      /*On récupère notre chaîne sous forme d'un tableau 
      de bytes*/
      byte[] sourceBytes = source.GetBytes(resultString);
      //On convertit notre tableau dans l'encodage souhaité
      byte[] destBytes = Encoding.Convert(source,
         dest, sourceBytes);
      //Et on reforme notre chaîne de caractère
      resultString = Encoding.UTF8.GetString(destBytes, 0,
         destBytes.Length);
 }

En résumé :

  • On créé un StreamReader avec l’encodage de base
  • On transforme la chaîne récupérée en tableau de bytes
  • On convertit ce tableau de bytes dans l’encodage souhaité
  • On le retransforme en String

Par JC VASSELON

[WP7] Comment faire des screenshots depuis votre téléphone ?

J’ai réalisé une application qui utilise la caméra pour écrire des sms en marchant (TextWalker : http://www.windowsphone.com/fr-FR/apps/18279149-e3fe-460c-8c35-29316f41bab1).

Et je voulais faire un screenshot de cette application sans avoir le vilain fond blanc de l’émulateur de l’appareil photo. Je voulais donc directement faire cette impression écran depuis mon téléphone.

Et Zozo (http://zozofromelk.wordpress.com/2012/01/22/screenshot-of-your-windows-phone-7-app-on-device/) m’a sauvé !
En effet, il a créé un behavior a intégrer au sein de son application pour faire des impressions écrans avec un Double Tap sur l’écran (double clic avec le doigt) que vous pouvez télécharger ici : http://bit.ly/znSvNE ou depuis son article ci-dessus.

Une fois récupéré, il vous suffit d’ajouter la classe ScreenShotBehavior à votre projet ainsi qu’au sein des pages où vous souhaitez réaliser vos impressions écrans :

<i:Interaction.Behaviors>
        <local:ScreenShotBehavior />
    </i:Interaction.Behaviors>

Une fois que l’application est lancée, il vous suffira de taper deux fois sur votre écran pour retrouver votre screenshot dans les images du téléphone.

Remarque, il a encadré le code de son behavior entre dans un directive préprocesseur “#DEBUG” qui fait qu’il n’est fonctionnel qu’en mode débug.

Par JC VASSELON

Pimp ma listbox

On va découvrir ici comment éditer et transformer une ListBox. L’exemple sera fait sur du Windows Phone, mais pourra être facilement répliqué en WPF, Silverlight, … En Xaml quoi !

Je vais rapidement rappeler les bases d’une ListBox et nous passerons ensuite sur quelques exemples concrets (ListBox horizontale, ListBox wrappée).

Pour cet article, j’ai utilisé Expression Blend, qui permet d’éditer les styles rapidement et facilement :
Un clic droit sur la ListBox, et dans le menu additionnal template vous pourrez éditer facilement les éléments que je signale.

Structure d’une ListBox

Comme son nom l’indique, une ListBox sert à afficher une liste d’éléments, une collection d’objets. En plus de son template, elle est constituée de trois autres templates qui permettent de la personnaliser plus précisément. C’est à eux que l’on va s’intéresser.

Illustrons ces différents templates, avec un exemple simple : une boîte de bonbons :

  • ItemTemplate : La forme sous laquelle vont s’afficher les objets contenus dans la collection. Le bonbon brut.
  • ItemContainerStyle : Le conteneur de l’item. le papier autour du bonbon. C’est sur lui qu’on gèrera l’espacement entre chaque item.
  • ItemsPanel : Le conteneur général. La boîte.

ListBox Horizontale

En partant de cette base, comment faire une ListBox Horizontale ?

  1. On édite l’ItemsPanel, on veut un conteneur horizontal. On passe donc la propriété Orientation du StackPanel sur “Horizontal”;
  2. On revient à notre ListBox, on édite les propriétés liées au Layout, on passe HorizontalScrollBarVisibility sur “Auto” ou “Visible” et VerticalScrollBarVisibility sur “Disabled” (Si vous êtes sur Blend, et que vous ne trouvez pas ces propriétés, cliquez sur la petite flèche en bas de Layout);
  3. On édite l’ItemContainerStyle en mettant une marge à droite sur notre bordure (12px est le nombre magique en général) pour pas que les éléments soit collés;
  4. Il ne vous reste plus qu’à éditer l’ItemTemplate en quelques clics en fonction de vos goûts.

Et nous avons une belle ListBox horizontale !

ListBox Wrapped

Pour rappel, le wrap est le système qui permet à des éléments de revenir à la ligne automatiquement et de s’adapter à différentes tailles d’écrans :

image

(oui ce sont des carottes wrappées)

Donc si on a bien suivi, qu’a-t-on à faire ?

  1. Modifier l’ItemsPanel, oui ! On supprime le StackPanel qu’on remplace par le WrapPanel disponible dans le Silverlight Toolkit For Windows Phone (disponible via Nuggets);
  2. Régler l’HorizontalScrollBarVisibility et la VericalScrollBarVisibility en fonction du sens dans lequel vous voulez faire aller vos éléments (verticaux ou horizontaux);
  3. Editer l’ItemContainerStyle avec une marge en bas et à droite pour “décoller” les éléments entre eux;
  4. Et éditer l’ItemTemplate en fonction de vos goûts !

Conclusion

On a donc vu rapidement la structure d’une ListBox et comment faire facilement différents types de listes.

Par JC VASSELON

Récupérer et parser du HTML

Ma problématique était de récupérer des images depuis un site web. Il me fallait donc charger le contenu du site, puis chercher à l’intérieur du code source, les adresses des images qui m’intéressaient. Voyons tout cela plus en détail.

Récupérer une page nécessitant une authentification

Au départ, j’avais besoin de travailler sur les images des albums Facebook. Un album privé était donc difficilement accessible sans connexion. La manière la plus simple que j’ai trouvée a été d’utiliser le contrôle Web Browser en WPF.

J’ai donc créé un WebBrowser dans mon xaml que j’ai appelé WebBro. La navigation peut se faire depuis le code behind en une ligne :

WebBro.Navigate(VotreUrl);

Ensuite lorsque je suis sur la bonne page, je commence par récupérer et stocker le code HTML de la page :

dynamic dom = WebBro.Document;
string htmlText = dom.documentElement.innerHTML;

Le mot-clef dynamic simplifie le code mais vous pouvez très bien ajouter une référence à MSHTML et caster le WebBro.Document en mshtml.HTMLDocumentClass.
Nous avons donc récupéré le HTML de la page et l’avons stocké dans la string htmlText. Il nous restera plus qu’à travailler dessus pour rechercher les éléments que l’on souhaite. Nous verrons cela dans la troisième partie de cet article.

Récupérer une page “classique”

Pour une application WP7, il me fallait récupérer des images depuis un site mais pour cela il fallait tout automatiser. Le WebBrowser n’était donc pas imaginable.
J’ai utilisé un WebClient pour requêter le site et récupérer directement le HTML de la page.

_wc = new WebClient();
_wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(_wc_DownloadStringCompleted);
_wc.DownloadStringAsync(new Uri(VotreURL + _localAppItem.Name,
                                                UriKind.Absolute));

Vu que c’est du Windows Phone, tout est asynchrone, il faut donc s’abonner à l’évènement DownloadStringCompleted. Lorsque cet évènement est levé, nous pouvonsrécupérer le contenu de notre page HTML dans notre méthode _wc_DownloadStringCompleted(object o, DownloadStringCompletedEventArgs args).
Le code HTML de la page se trouvant dans la variable args.Result.

Parser le HTML grâce aux Regex

Les Regex… Ces petites bêtes que l’on souhaite éviter et qui ne se laisse pas dompter facilement.
Les Regex ou expressions régulières sont un moyen de chercher des éléments textuels respectant une certaine forme, un certain pattern.
Dans mon cas, pour chercher des images dans du code HTML, il me fallait récupérer toutes les chaines commençant par “src= »” et finissant avec une extension particulière “.jpg”, “.jpeg”, “.png”,…

Pour plus de lisibilités, j’ai donc défini deux chaînes, une pour le début et une pour la fin de la séquence à trouver :

string start = "src=\"";
string end = "\\.(jpg|png)";

Sur la chaîne start, le ‘\’ permet d’échapper le guillemet.
Sur la chaîne end, les ‘\\’ permettent d’échapper un ‘\’ qui permet d’échapper le ‘.’. En effet, dans une regex, le point est un caractère particulier.

Nous pouvons maintenant créer notre Regex :

Regex r = new Regex(start + "(.*?)" + end);

Nous créons donc une String que nous passons au constructeur de notre Expression Régulière. On retrouve notre chaîne de début et notre chaîne de fin avec au milieu cet assemblage étrange : « (.*?) » :

  • les parenthèse signifient un groupement de caractères
  • Le point signifie n’importe quel caractère
  • l’étoile signifie une à plusieurs fois le caractère précédent
  • le point d’interrogation permet de répéter l’expression précédente

Une fois cette Regex créée, il ne nous reste plus qu’à l’utiliser sur notre code html :

MatchCollection matches = r.Matches(htmlText);

A chaque fois qu’une occurrence sera trouvée, elle sera ajoutée à la collection matches qu’il nous suffit de parcourir avec un foreach pour récupérer les liens des images.

foreach (Match matche in matches)
            {
                VotreMethodePourTraiter(matche.Groups[1].Value);
            }

Bonus : Télécharger les images

Idem, je vais montrer avec deux possibilités. Synchrone en WPF et asynchrone pour WP7.

De manière synchrone

private void GetPics()
        {
                int i = 0;
                foreach (string url in urlBrutImg)
                {
                    System.Drawing.Image myImg;
                    WebRequest req = WebRequest.Create(url);
                    WebResponse response = req.GetResponse();
                    myImg = System.Drawing.Image.FromStream(response.GetResponseStream());
                    myImg.Save("D:\\NomDuRepertoire\\" + i+".jpg");
                    i++;
                }
        }

Chaque fois qu’une de mes Regex étaient valide, j’ajoutais l’url à une liste. C’est cette liste, nommée urlBrutImg que je parcours dans le foreach.
Je fais une WebRequest sur l’url obtenue, je génère mon image avec le Stream que me retourne la WebRequest. Il ne me reste plus qu’à enregistrer cette image sur mon disque dur, et d’incrémenter i pour ne pas écraser la précédente !

De manière asynchrone

Ici, chaque fois que ma Regex matchait, j’ajoutai l’Uri obtenue dans une Queue, ce type de liste permet de retirer un élément lors de sa lecture. J’ai réutilisé le WebClient qui m’a permis de télécharger le code HTML de manière asynchrone pour télécharger les images. Il m’a juste fallu m’abonner à un nouvel évènement :

_wc.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompleted);

J’ai ensuite ajouté une méthode Dequeue() pour retirer mes éléments au fur et à mesure que je récupérais mes images.

private void Dequeue()
        {
            if (_queuedItems.Count > 0)
            {
                Uri uri = _queuedItems.Dequeue();
                _wc.OpenReadAsync(uri, uri);
            }
        }

Et le code de ma méthode OpenReadCompleted pour enregistrer l’image :

private void OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            try
            {
                if (e.Error == null && e.Cancelled == false)
                {
                    Uri uri = e.UserState as Uri;
                    BitmapImage image = new BitmapImage();

                    image.SetSource(e.Result);
                    //Traitement de l’image                  
                }
            }
            catch (Exception ee)
            {}
            Dequeue();
        }

Une fois que vous avez votre BitmapImage, à vous de voir si vous voulez l’enregistrer dans l’IsolatedStorage ou l’afficher.
On fini par un Dequeue, pour continuer à vider notre collection et télécharger les images manquantes.

Conclusion

Dans cet article nous aurons vu plusieurs manières de récupérer du code HTML puis comment obtenir certains éléments similaires grâce aux Regex.

Par JC VASSELON

[WP7] Utiliser Skydrive dans vos projets WP7

Dernièrement, Microsoft a annoncé la sortie du Live SDK. Cette boite à outil va nous permettre à nous développeur de faire interagir nos applications avec les services du Live. Cela va de l’authentification à nos services, en passant par WLM, l’accès aux calendriers et contacts mais aussi au SkyDrive !
SkyDrive, cet espace de stockage dans le cloud que j’avais évoqué dans cet article : https://onefor4.wordpress.com/2011/10/25/tip-intgrez-skydrive-windows-7/ va ainsi devenir encore plus intégré et nous permettre de faciliter les échanges inter-utilisateurs, inter-applications et inter-plateformes !
Nous allons voir ici quelques opérations basiques pour démarrer.

Prérequis et ressources :

Inscription de l’application

Avant toute chose, il va falloir inscrire votre application ici : https://manage.dev.live.com/AddApplication.aspx pour obtenir un identifiant et des credentials.

image

Rendez vous ensuite dans API Settings pour configurer votre application comme étant une application mobile.

image

Lire et écrire sur SkyDrive

Pour l’exemple, nous allons réaliser l’application suivante qui consiste à ajouter un nouveau fichier dans un dossier de SkyDrive.

image

C’est une simple page, contenant :

  • Un SignInButton que nous détaillerons plus tard et qui permet de s’authentifier sur le live.
  • Un ListPicker (que vous trouverez notamment dans le Silverlight for WP7 Toolkit) qui nous permettra de choisir dans quel dossier enregistrer notre fichier.
  • Une textbox pour donner un nom au fichier et une autre pour ajouter du contenu à ce fichier.

Le code

Pour utiliser SkyDrive, on aura besoin de s’authentifier sur le Live. Le SDK nous donne heureusement un contrôle déjà existant pour cela : le SignInButton ! Sourire
Ajoutez les DLL Microsoft.Live et Microsoft.Live.Controls à votre projet.

Rajoutez le namespace suivant dans votre xaml :

xmlns:my="clr-namespace:Microsoft.Live.Controls;
assembly=Microsoft.Live.Controls"

Vous pouvez maintenant utiliser le SignInButton qui vous permettra de vous authentifier sur le live et de choisir les différents droits que vous souhaitez que l’utilisateur vous donne.

<my:SignInButton 
ClientId="" 
Scopes="wl.signin wl.skydrive wl.skydrive_update" 
SessionChanged="SignInButton_SessionChanged" />

Les différents attributs importants :

  • ClientId : C’est l’identifiant de votre application. Vous le retrouverez sur cette page : https://manage.dev.live.com/Applications/Index
  • Scopes : C’est les différents droits que vous souhaitez obtenir. Ici, l’authentification, les droits de lecture sur le SkyDrive, et les droits de mises à jour sur le SkyDrive.
    Tous les scopes ici : http://msdn.microsoft.com/fr-fr/library/hh243646.aspx
  • SessionChanged : C’est l’évènement qui est levé lors de la Connexion / Déconnexion de l’utilisateur

En cliquant sur ce nouveau bouton, on est automatiquement redirigé vers la page de connexion de Windows Live.

image

Vient ensuite une demande d’autorisations :

image

La session est initialisée. Traitons là maintenant et voyons comment accéder aux différents dossiers sur notre SkyDrive. Pour cela, nous allons d’ailleurs créer une classe SkydriveItem qui nous permettra d’organiser plus facilement les différents éléments (fichiers, dossiers, albums).

public class SkydriveItem
    {
        public string Id { get; set; }
        public string Type { get; set; }
        public string Name { get; set; }

        public override string ToString()
        {
            return Name;
        }
    }
  • Id : Unique, il nous permet d’accéder directement à l’élément depuis le code.
  • Type : Il existe trois types d’éléments. Folder, un répertoire; Files, un fichier; Albums, un répertoire “spécial”.
  • Name : Le nom du dossier lisible par un humain Sourire

Revenons au code de notre page. On va ajouter un LiveConnectClient qui nous permettra de garder la session et de réaliser nos différentes opérations ainsi qu’une ObservableCollection de SkydriveItem dans laquelle on ajoutera les différents dossiers et que l’on bindera sur l’ItemsSource de notre ListPicker.

private LiveConnectClient _client;
public ObservableCollection<SkydriveItem> SkydriveItems 
{ get; set; }
        
// Constructor
public MainPage()
{
   SkydriveItems = new ObservableCollection<SkydriveItem>();
   InitializeComponent();
}

Remplissons ensuite la méthode SignInButton_SessionChanged().

private void SignInButton_SessionChanged(object sender, 
                     LiveConnectSessionChangedEventArgs e)
{
//Si on est bien connecté
if (e.Status == LiveConnectSessionStatus.Connected)
{
  //On purge la liste d'éléments
  SkydriveItems.Clear();
  //On initialise notre LiveConnectClient avec la session courante
  _client = new LiveConnectClient(e.Session);
  //On s'abonne à l'événement
  _client.GetCompleted += 
       new EventHandler<LiveOperationCompletedEventArgs>
                                       (OnGetCompleted);
 //On lance l'opération aynchrone
  _client.GetAsync("/me/skydrive/files");
 }
 else if (e.Status == LiveConnectSessionStatus.NotConnected)
 {
   SkydriveItems.Clear();
   _client = null;
 }
 else
   _client = null;
 }

Et on parcourt ensuite la liste des éléments obtenus en ajoutant ceux qui nous intéressent à notre ObservableCollection.

void OnGetCompleted(object sender, 
                LiveOperationCompletedEventArgs e)
{
  if (e.Error == null)
  {
    List<object> data = (List<object>)e.Result["data"];
    foreach (IDictionary<string, object> content in data)
    {
      SkydriveItem nItem = new SkydriveItem();
      nItem.Id = (string)content["id"];
      nItem.Name = (string)content["name"];
      nItem.Type = (string)content["type"];
      if(nItem.Type == "folder")
        SkydriveItems.Add(nItem);
     }
  }
}

Ils apparaissent maintenant dans notre ListPicker après l’authentification.

image

Occupons-nous de l’écriture dans un des dossiers sélectionnés. Abonnons l’évènement Click de notre bouton sur une méthode Save_Click.
Pour cet exemple, j’ai appelé la textbox qui représente le titre du fichier : Tb_Title et celle représentant le contenu : Tb_Text.

Voici le code de la méthode Save_Click() ainsi que le callback une fois l’upload terminé.

private void Save_Click(object sender, RoutedEventArgs e)
{
    string fileName = Tb_Title.Text.Trim();
    byte[] byteArray = 
Encoding.Unicode.GetBytes(Tb_Text.Text.Trim());
    MemoryStream fileStream = new MemoryStream(byteArray);
    _client.UploadCompleted += new 
EventHandler<LiveOperationCompletedEventArgs>(
UploadCompleted); 
    _client.UploadAsync(
((SkydriveItem)LP_Items.SelectedItem).Id, fileName, 
fileStream);
}

void UploadCompleted(object sender, 
LiveOperationCompletedEventArgs e)
{
    if (e.Error == null)
    {
        Dispatcher.BeginInvoke(() =>
        {
            MessageBox.Show("Uploadé !");
        });
    }
}

La méthode « _client.UploadAsync(
((SkydriveItem)LP_Items.SelectedItem).Id, fileName,
fileStream); « 
nous permet d’uploader notre fichier dans le dossier correspondant. Mais attention, nous ne pouvons pas passer directement le nom du dossier mais son Id !

Une fois l’upload terminé, vous retrouverez votre fichier dans le dossier voulu sur votre SkyDrive : http://skydrive.live.com/

Conclusion

Nous avons ici découvert une partie du Live SDK : comment s’authentifier et comment utiliser des opérations basiques sur SkyDrive.
Ce SDK ouvre de formidables opportunités pour interagir avec les fichiers de vos utilisateurs mais aussi faciliter la migration de vos applications.
En effet, pourquoi ne pas sauvegarder le contenu de votre Isolated Storage pour le récupérer ensuite quand l’utilisateur souhaite changer de téléphone ? Ou simplement partager ses données avec un autre ?

Par JC VASSELON

[TIP] Utiliser votre clavier sur l’émulateur WP7

Utilisez la touche “PageUp” / “Page précédente” après avoir cliqué sur un champ dans l’émulateur pour pouvoir utiliser le clavier de votre PC Sourire.

Attention, j’ai l’impression que parfois le clavier ne répond plus dans les autres applications. Vérifiez bien dans ce cas, que vous n’avez pas un champ de sélectionné dans l’émulateur.

Par JC VASSELON

Converter pour un Binding d’une image dans l’Isolated Storage

J’avais une liste d’éléments  de type Item auxquels je voulais attribuer une image différente pour chacun. Cette image se trouvait dans l’Isolated Storage dans un dossier spécifique pour chaque et était amenée à changer. Je ne pouvais pas mettre l’uri de ces images directement dans l’objet Item.
Dans le template de ma ListBox, au lieu de faire un binding de la source de mon image vers une uri, j’ai fait un binding vers le nom de mon Item (par exemple, “Bonjour le Chat”) et j’ai utilisé un converter pour aller récupérer l’image correspondante dans l’Isolated Storage.

image

(la ListBox avec les éléments)

J’ai donc créé mon converter avec la méthode Convert(…) suivante et qui retourne une BitmapImage.

public object Convert(object value, Type targetType, 
            object parameter, CultureInfo culture)
        {
            string path = value.ToString() + "/";
            using (IsolatedStorageFile store = 
                IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (String.IsNullOrEmpty(value.ToString()))
                    return null;
                try
                {
                    using (IsolatedStorageFileStream fileStream = 
                        store.OpenFile(path + 
                        store.GetFileNames(path + "*").First(),
                        FileMode.Open, FileAccess.Read))
                    {
                        var img = new BitmapImage();
                        img.SetSource(fileStream);
                        return img;
                    }
                }
                catch (Exception e)
                {
                    return null;
                }

            }
        }

Avec la méthode store.OpenFile(path + store.GetFileNames(path + « * »).First(), FileMode.Open, FileAccess.Read). Je récupère un stream du premier fichier que je trouve dans le dossier recherché.
La méthode OpenFile est d’ailleurs intéressante puisque je lui passe bien sûr mon path, mais il faut que je liste ensuite tous les fichiers du répertoire (d’où le caractère “*”) avant de pouvoir prendre le premier.

Par JC VASSELON