A distributed system is one in which the failure of a computer you didn’t even know existed can render your own computer unusable.

— Leslie Lamport

Publicités

En vrac mercredi 18 janvier 2012

J’ai reçu un mail hier de Rodrigo et Gabriela. Non, il ne s’agit pas des cousins de l’autre côté de l’île, il s’agit de deux mexicains qui habitent en Irlande ; plus important, c’est un duo de guitaristes exceptionnel ! Vous les avez peut-être entendu à Solidays en 2010 ou, plus récemment, dans la BO du dernier Pirates des Caraïbes.

Mais, pour quoi j’en parle ici ? Parce que dans leur mail ils annoncent la sortie d’un nouvel album et je trouve que leur modèle de distribution est très intéressante et très « XXIème siècle ». Je m’explique.

Leur album sort dans 3 jours. Pour entretenir le lien avec leurs adeptes en ces jours si importants, ils ont organisé une session Ustream pour discuter avec eux. En même temps, ils ont mis en pré-vente sur leur site ledit album. Des prix tout à fait raisonnables : €8,99 (c’est-à-dire, €0,99 le morceaux, ou le « prix iTunes ») pour la version numérique, €1 de plus si vous voulez des supports CD ou DVD avec le making-of ou €11 de plus si vous voulez le vinyl—ouais, un vrai LP, et vous avez le droit à la version numérique avec en plus.

Mais, si vous n’êtes pas encore fan, comment pouvez-vous être sûr que vous voulez vraiment acheter l’album ? Je ne sais pas trop comment ou pourquoi, mais on fait un saut de l’autre côté de l’Atlantique pour atterrir sur le site de NPR—la radio publique américaine—qui leur dédie un article et, au passage, permet d’écouter gratuitement et dans son intégralité le nouvel album.

Je trouve l’ensemble très intéressant. Certes, ils ne proposent pas leur album en téléchargement gratuit, comme l’ont déjà fait d’autres groupes, mais d’un autre côté, je ne vois pas pourquoi ils le feraient. Il y a du travail derrière la production d’un album et ce travail mérite d’être rémunéré.

Dans un tout autre registre, j’ai appris sur le blog de Joel Spolsky que la ville de New York prépare pour la rentrée prochaine (automne 2012) un lycée spécialisé dans le génie logiciel. Qu’est-ce ?

Si j’ai bien compris, la Academy for Software Engineering sera un lycée comme un autre dans la ville de New York. Sauf que, en plus d’apprendre les connaissances de base dont ils ont besoin pour continuer ses études par la suite, les élèves apprendront aussi à développer des logiciels.

Ce qui est particulièrement intéressant, donc, c’est qu’il s’agit d’un lycée général auquel n’importe quel élève de 3ème peut soumettre sa candidature. Son recrutement se fera sur dossier et la motivation comptera plus que les notes. Cela devrait permettre à des élèves motivés d’apprendre à écrire des logiciels et, potentiellement, rejoindre le marché du travail dès l’obtention du bac.

Certes, cette dernière phrase peut choquer plus d’un, mais soyons réalistes : aux États-Unis comme en Europe (et probablement ailleurs aussi) on manque cruellement de bons développeurs et ce n’est pas le fait d’avoir fait 2, 4, 5, 6, 8 ans de formation après le bac qui va faire qu’on va en avoir dans le court terme. Par contre, ne me mal interprétez pas : une bonne formation est définitivement nécessaire et ce genre d’établissement pourrait très bien faire l’affaire.

Apprendre que l’on se propose de créer aujourd’hui des lycées qui vont enseigner une grande partie de ce que pour moi ont été des études supérieures, me fait penser à mes cours de physique du lycée justement. À l’époque (ouch, je viens de faire le calcul), je trouvais ça rigolo d’apprendre des concepts qui étaient tout simplement inconnus siècles, voire parfois quelques dizaines d’années auparavant. Dans combien de temps pensez-vous que la programmation deviendra une compétence courante du bachelier moyen ?

Driver Azure Table Storage pour LINQPad

Vous connaissez LINQPad ? Scott Hanselman le recommande systématiquement dans liste annuelle d’outils pour développeurs. En ce qui me concerne, je m’en sers pratiquement tous les jours—enfin, quand je développe.

linqpad

À la base, c’est un logiciel qui vous permet d’exécuter des requêtes Linq :

linq_query

Mais ça permet aussi d’exécuter des bouts de code arbitraires, juste pour voir ce que ça donne :

code_sample

La fonctionnalité que j’utilise le plus c’est définitivement celle-ci, mais ces derniers temps je travaille de plus en plus avec Windows Azure et notamment Table Storage. Bien que LINQPad supporte des requêtes sur SQL Azure, j’étais un peu embêté quand il s’agissait d’effectuer rapidement des requêtes sur le Table Storage. Du coup, j’ai décidé d’écrire un driver pour le supporter !

Le driver permet d’ajouter des comptes de stockage (storage accounts) en tant que connexions LINQPad :

add_storage_account

Une fois que vous avez ajouté le compte—qui peut être votre compte de développement local, si l’émulateur est activé, ou un compte sur le cloud—vous le verrez apparaitre dans la liste de connexions avec ses tables et leurs colonnes.

linqpad_tables

La suite c’est du LINQPad “comme d’habitude”. Enfin, si vous avez vos habitudes, sinon vous pouvez commencer par un clic droit sur une table pour insérer une des requêtes par défaut.

Evidemment, pour ce faire, il faut télécharger le driver. Vous avez deux choix :

Petit warning : on est en version 1.0.0-beta. Ça veut dire que je n’ai pas testé le code, mais qu’il a l’air de marcher. Si vous utilisez Azure Table Storage et LINQPad et que vous voulez tester le driver, tout feedback est bienvenu !

WP7 Mango : Faut-il faire des mises à jour ? Et que se passe-t-il si je les fais ?

mangoPour le grand public ce sera Windows Phone 7.5—parce que le marketing l’a décidé ainsi. Pour nous, développeurs, ce que nous connaissons aujourd’hui sous le nom code “Mango” sortira bientôt sous le nom officiel Windows Phone OS 7.1—parce que chez Microsoft on sait nommer ses classes et ses méthodes, mais pas ses produits.

Un nouvel OS arrive, mais nous savons bien que Microsoft fait toujours un effort surhumain pour faire en sorte que les vielles applications marchent correctement sur les nouvelles versions de ses systèmes d’exploitation, donc il est pertinent de se poser la question :

Dois-je mettre à jour mon application 7.0 puisqu’elle marchera automatiquement sous 7.1 ?

La réponse est un peu dans la question : non, vous n’êtes pas obligés de faire de mise à jour. Enfin, sauf si vous vouliez que votre application marche mieux et ce avec seulement une ligne de code (lignes des accolades non comprises) !

Je m’explique. Vous vous souvenez du temps que vous avez passé pour faire en sorte que votre application survive le tombstoning correctement ? Vous devez enregistrer l’état de l’application quand elle est désactivée—peu importe la raison—et le restaurer quand est réactivée—peu importe la raison. Ça rend votre application plus agréable à utiliser, mais vous aurez probablement remarqué que la réactivation peut parfois être longue.

Il se trouve que sous Mango, vos processus ne sont plus systématiquement tués par l’OS et donc l’état n’est plus systématiquement perdu ce qui rend sa restitution à chaque réactivation un peu redondant. Heureusement, nous avons un moyen de déterminer si les données sont toujours là et donc nous ne sommes plus obligés de restaurer l’état de l’application que si c’est absolument nécessaire.

private void Application_Activated(object sender, ActivatedEventArgs e)
{
  if (!e.IsApplicationInstancePreserved)
  {
    var state = PhoneApplicationService.Current.State;

    if (state.ContainsKey("myStateData"))
    {
      this.data = (string)state["myStateData"];
    }
  }
}

La valeur de la propriété IsApplicationInstancePreserved sera true si l’application, et donc son état, étaient déjà en mémoire. Dans ce cas, il n’y a plus besoin de rien faire, dans le cas contraire on restaure l’état comme on le faisait avant. Et voilà, en une ligne vous avez accéléré le chargement de votre application.

Et maintenant que vous avez une application Mango, que se passe-t-il sur le Marketplace ?

Microsoft a veut visiblement limiter la fragmentation et veut pousser les utilisateurs à faire la migration de leur OS donc une fois que vous avez publié une version Mango de votre application, vous ne pourrez plus mettre à jour la version 7.0 sur le Marketplace. Bien ? Mal ? Je trouve que c’est une mesure un peu drastique, mais je suis plutôt à faveur.

La dernière version 7.0 de votre application sera toujours disponible—bugs et tout—pour les utilisateurs qui sont toujours sur cette plateforme. Tous ceux qui auront migré ne pourront télécharger que la version Mango.

Les utilisateurs qui avaient déjà installé votre application et qui migrent sous Mango, recevront une notification de mise à jour et pourront installer la nouvelle version de votre application. Ceux qui décident de rester sous 7.0 n’auront pas de mise à jour.

Les métadonnées seront communes aux deux versions de l’application en revanche, y compris les screenshots. Microsoft recommande donc d’être très clairs dans les descriptions des applications et de signaler les fonctionnalités qui ne seront disponibles que sous Mango. Ils suggèrent aussi que vous deveniez ambassadeur-malgré-vous en mettant dans vos descriptions un lien vers http://wpupgrade.ms/mangome pour inciter les utilisateurs à faire la mise à jour du téléphone. Pour les screenshots, vous êtes encouragés à utiliser un watermark identifiant les images qui montrent des fonctionnalités disponibles “Uniquement sous WP 7.5”.

Création de nouvelles pages et navigation sur Windows Phone 7

Ceci est le sixième billet d’une série sur Windows Phone 7 dans laquelle je construis une application permettant de surveiller la disponibilité de vélos des stations Vélib’ à Paris. Pour rappel, voici la table de matières:

  1. Introduction et installation des outils
  2. Création d’une application et utilisation du contrôle Bing Maps
  3. Le GPS et les services de géolocalisation
  4. Consommation d’un service OData
  5. Ajout de punaises sur une carte Bing Maps
  6. Création de nouvelles pages et navigation
  7. Création de vignettes sur la page d’accueil

Un modèle de programmation Web

Screenshot 2

Vous avez surement remarqué l’absence de fenêtres dans notre application Windows Phone 7. Cela aurait pu vous marquer, notamment si vous êtes habituées au développement sur environnements desktop, voire aussi sur des anciennes versions de Windows Mobile.

Si je devais faire un parallèle entre le développement sur Windows Phone 7 et une autre plateforme, je me verrais obligé de le comparer au développement Web—en tout cas pour ce qui est des applications Silverlight ; les application XNA sont une toute autre histoire.

En effet, si vous examinez le code généré dans le fichier App.xaml.cs vous trouverez qu’au démarrage de l’application on commence par déclarer comme élément racine un objet de type PhoneApplicationFrame. C’est dans ce frame que vont être affichées par la suite vos pages, qui dérivent de la classe PhoneApplicationPage. Le similitudes ne se limitent pas au frame et aux pages. Vous verrez qu’on retrouvera des concepts tels que les URIs, les hyperliens, etc.

Une nouvelle page

Hier nous avons permis à l’utilisateur d’afficher des stations sur sa carte. Il est même capable d’appuyer sur l’une de ces dernières et, de notre côté, nous savons identifier la station qu’il a sélectionnée, mais il nous manque quelque chose pour en afficher les détails.

Ajoutez à votre projet un nouvel élément. Sélectionnez une nouvelle Windows Phone Portrait Page parmi les templates Silverlight for Windows Phone.

Add New Item - UnVelo.Wp7

La page créée ressemble à la page principale lorsque nous avons démarré le projet.

Cette fois-ci, nous allons travailler sur le modèle par défaut en remplaçant certaines valeurs et en le complétant avec les contrôles nécessaires pour obtenir un affichage comme celui sur la capture au début de ce billet.

Commençons par modifier la partie du haut :

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
    <TextBlock x:Name="ApplicationTitle" Text="UN VELO.FR"
               Style="{StaticResource PhoneTextNormalStyle}"/>
    <TextBlock x:Name="PageTitle" Text="{Binding Number, StringFormat='Station {0}'}"
               Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

Le nom de l’application sera une valeur constante, en revanche, le titre devra afficher le numéro de station. Nous utilisons donc une expression de binding pour afficher la propriété Number de la classe Station. Petite nouveauté “Mango” : comme on utilise enfin Silverlight 4, nous avons accès à la propriété StringFormat de l’expression de binding, ce qui nous permet de personnaliser un peu le texte affiché sans passer par des convertisseurs ou des piles de TextBlocks.

En raison de ce qui semblerait être un bug dans le contrôle Bing Maps, je n’ai pas pu placer tous mes contrôles dans le ContentPanel fourni pour cet effet. J’ai donc modifié le LayoutRoot :

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Grid.Resources>
        <local:StationConverter x:Key="StationConverter" />
    </Grid.Resources>
<!-- ... -->

En y ajoutant une ligne, j’ai pu placer la carte juste au dessus du ContentPanel :

<map:Map Margin="12,0,12,0" Grid.Row="1" Grid.ColumnSpan="2" Height="250"
        CredentialsProvider="{StaticResource BingMapsKey}" ZoomLevel="16"
        Center="{Binding Converter={StaticResource StationConverter}}">
    <map:MapItemsControl x:Name="pins"
        ItemsSource="{Binding Converter={StaticResource StationConverter}}">
        <map:MapItemsControl.ItemTemplate>
            <DataTemplate>
                <map:Pushpin Location="{Binding Location}" />
            </DataTemplate>
        </map:MapItemsControl.ItemTemplate>
    </map:MapItemsControl>
</map:Map>

<!--ContentPanel - place additional content here-->
<StackPanel x:Name="ContentPanel" Grid.Row="2" Margin="12,12,12,0">
    <TextBlock Text="{Binding Name}"
               FontFamily="{StaticResource PhoneFontFamilySemiBold}"
               FontSize="{StaticResource PhoneFontSizeLarge}"
               Foreground="{StaticResource PhoneAccentBrush}" />
    <TextBlock Style="{StaticResource PhoneTextLargeStyle}"
      Text="{Binding AvailableBikes, StringFormat='Vélos disponibles : {0}'}" />
    <TextBlock Style="{StaticResource PhoneTextLargeStyle}"
      Text="{Binding FreeSpaces, StringFormat='Places disponibles : {0}'}" />
</StackPanel>

Le code ci-dessus est moins complexe qu’il ne parait. Certes, la présence d’un converter dans les bindings vous perturbe certainement un peu et le rendu fait par le designer est assez décevant :

ViewHolder2

Tous les bindings ont été faits en supposant qu’un objet de type Station se trouve dans le DataContext de la page. Or nous n’avons pas encore défini de station, donc rien ne s’affiche.

On a besoin d’une station !

Naviguer entre les pages

Revenons sur la page principale ; plus spécifiquement, dans le gestionnaire de l’évènement Tap des punaises :

private void OnStationTap(object sender, GestureEventArgs e)
{
    var station = (sender as Pushpin).DataContext;
}

Nous avons une station dont nous voulons afficher les détails.

Pas question ici de créer une nouvelle instance de la classe que nous venons de créer. Je vous rappelle que nous sommes dans un modèle type Web et nous allons tout simplement naviguer entre les pages comme nous l’aurions fait avec des documents HTML.

Pour cela, nous avons à notre disposition un NavigationService qui est accessible par la propriété homonyme de vos pages. Ce service propose une méthode Navigate qui prend en paramètre un URI relatif vers la destination :

private void OnStationTap(object sender, GestureEventArgs e)
{
    var station = (sender as Pushpin).DataContext;

    NavigationService.Navigate(new Uri("/DetailsPage.xaml", UriKind.Relative));

    // Dans la version ci-dessous on spécifie l'assembly (UnVelo.Wp7)
    // Cela peut-être utile si vos pages se trouvent dans un assembly
    // différent de celui en cours d'exécution
    // NavigationService.Navigate(new Uri("/UnVelo.Wp7;component/DetailsPage.xaml", UriKind.Relative));
}

Le code ci-dessus (les deux versions) produit l’effet désiré : le téléphone change de page et affiche celle que nous avons préparée plus haut. De plus, la page principale a été placée sur la pile de navigation, ce qui nous permet d’y retourner via le bouton Précédent.

En revanche, le code ne fait que ça : passer la page ; et comme nous n’instancions pas la page nous-mêmes, il n’y a, a priori pas de moyen de passer des données via des propriétés de la page comme on a l’habitude de le faire sous d’autres plateformes.

Transmission de données entre les pages

Il y a deux manières de passer des données en mémoire d’une page à une autre : la query string (comme dans le Web) et l’état courant du PhoneApplicationService.

Comme dans une page web, on peut passer des informations dans l’URI de la page appelée, par exemple, dans la page principale on écrit :

private void OnStationTap(object sender, GestureEventArgs e)
{
    var pin = ((sender as Pushpin).DataContext as Pin);

    var uri = string.Format("/DetailsPage.xaml?id={0}", pin.Station.Number);
    NavigationService.Navigate(new Uri(uri, UriKind.Relative));
}

Et dans la page détails on peut récupérer la valeur avec le code suivant :

public override void OnNavigatedTo(NavigationEventArgs e)
{
    var number = NavigationContext.QueryString["id"];

    // ...

    base.OnNavigatedTo(e);
}

La méthode OnNavigatedTo de la page est appelée lorsque l’on arrive sur une page—son opposé est OnNavigatedFrom, qui est appelée juste avant que l’on quitte une page. Dans cette méthode on pourrait récupérer les différentes valeurs qui ont été passées dans la query string pour les assigner aux contrôles de la page ou pour effectuer des data bindings.

Toutefois, dans l’exemple qui nous concerne, nous voulons passer bien plus d’informations que juste le numéro de station, et nous avons toutes ces informations encapsulées dans une instance de la classe Station. De plus, il peut exister des cas où les données qui doivent être partagées entre les pages ne peuvent pas être transformées en chaine de caractères pour être concaténées à l’URI de la page. Pour ces cas-là, la classe PhoneApplicationService mantient un état courant dans lequel on peut stocker des objets pour les partager entre plusieurs pages :

private void OnStationTap(object sender, GestureEventArgs e)
{
    var pin = ((sender as Pushpin).DataContext as Pin);

    PhoneApplicationService.Current.State["currentStation"] = pin.Station;

    NavigationService.Navigate(new Uri("/DetailsPage.xaml", UriKind.Relative));
}

Les lignes ci-dessus enregistrent dans un emplacement de l’état courant de l’application, que l’on appelle arbitrairement currentStation, la station contenue dans le DataContext de la punaise qui a été appuyée par l’utilisateur. Du côté de la page de détails, quelques lignes suffiront à récupérer cet objet pour afficher les informations qu’il contient :

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    object station;

    if (PhoneApplicationService.Current.State.TryGetValue("currentStation", out station))
    {
        this.DataContext = station;
    }

    base.OnNavigatedTo(e);
}

On aurait pu se contenter de faire tout simplement l’assignation :

this.DataContext = PhoneApplicationService.Current.State["currentStation"];

Mais l’utilisation de la méthode TryGetValue maintenant nous permettra de prévoir des cas où il n’y a pas de valeur pour la clé currentStation à l’avenir.

Grâce aux bindings que nous avions déclarés lorsque nous avons créé la page de détails, le fait d’assigner un objet de type Station à son DataContext suffit à afficher la page comme on le voulait.

Details_page

Si vous essayez d’exécuter le code il vous manquera cependant la classe StationConverter que nous avons utilisée pour simplifier certains bindings :

public class StationConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var station = value as Station;

        if (null == station)
        {
            return value;
        }

        if (typeof(GeoCoordinate) == targetType)
        {
            return new GeoCoordinate((double)station.Latitude, (double)station.Longitude);
        }
        else if (typeof(IEnumerable) == targetType)
        {
            return new object[] { new Pin { Station = station } }.AsEnumerable();
        }

        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Nous approchons de la fin de la série. Maintenant que nous avons réussi à afficher les détails d’une station dans une page, nous allons profiter d’une des nouveautés de “Mango” qui nous permettra de modifier, à partir du code uniquement, des vignettes sur la page d’accueil pour y afficher, par exemple, le nombre de vélos disponibles dans une station. Mais ce code-là, on le verra demain.

Ajout de punaises sur une carte Bing Maps

Ceci est le cinquième billet d’une série sur Windows Phone 7 dans laquelle je construis une application permettant de surveiller la disponibilité de vélos des stations Vélib’ à Paris. Pour rappel, voici la table de matières:

  1. Introduction et installation des outils
  2. Création d’une application et utilisation du contrôle Bing Maps
  3. Le GPS et les services de géolocalisation
  4. Consommation d’un service OData
  5. Ajout de punaises sur une carte Bing Maps
  6. Création de nouvelles pages et navigation
  7. Création de vignettes sur la page d’accueil

On a besoin de punaises

Hier nous nous sommes arrêtés sur une liste stations. Chaque station est représentée par un objet de type Station qui contient des propriétés telles que le numéro de station, son adresse, le nombre de vélos et de places disponibles, mais surtout, ses coordonnées géographiques.

La prochaine étape sera donc d’utiliser ces informations pour afficher des punaises sur la carte de la page principale qui donneront un aperçu de l’état de chaque station et qui nous permettront d’accéder à la page de détails de celles-ci.

Pour afficher des punaises sur une carte, vous allez utiliser des calques. Ces calques dérivent de MapLayer et supportent des éléments d’à peu près n’importe quel type:

custom_pins

Dans notre cas, le Pushpin fourni dans l’assembly Bing Maps suffira largement. Ce contrôle possède deux propriétés qui nous intéressent :

Taj_Mahal

  • Content : est une propriété héritée de ContentControl qui permet à la classe Pushpin d’afficher à peu près n’importe quel contenu.
  • Location : est de type GeoCoordinate et représente les coordonnées géographiques de la punaise qui serviront à la positionner sur la carte.
<map:Pushpin Content="Taj Mahal" Location="27.172222,78.042476" />

Cependant, notre modèle de données, la Station, ne correspond pas tout à fait à ce qui est attendu par la classe Pushpin. Avant de continuer, vous aurez donc besoin d’un modèle qui représentera les stations sur la carte. Créez la classe Pin dans votre projet :

public class Pin
{
    public Station Station { get; set; }

    public string Label { get { return this.Station.Number.ToString(); } }

    public GeoCoordinate Location
    {
        get
        {
            return new GeoCoordinate(
                (double)this.Station.Latitude,
                (double)this.Station.Longitude);
        }
    }
}

Transformez maintenant votre collection de Stations en une collection de Pins :

private void GetStationsInRectangleCallbak(IAsyncResult result)
{
    var stations = this._repository.EndExecute<Station>(result);

    var p = from s in stations
            select new Pin { Station = s };

    Dispatcher.BeginInvoke(
        new Action<IEnumerable>((pins) => pinLayer.ItemsSource = pins), p);
}

Et un calque pour afficher le tout

Dans le code ci-dessus, nous avons transformé les stations et assigné cette nouvelle collection au calque de type MapItemsControl qui hébergera les punaises.

<map:Map x:Name="Map"
    CredentialsProvider="{StaticResource BingMapsKey}"
    ZoomBarVisibility="Visible" ZoomLevel="16"
    ViewChangeEnd="OnViewChangeEnd">
    <map:MapItemsControl x:Name="pinLayer">
        <map:MapItemsControl.ItemTemplate>
            <DataTemplate>
                <map:Pushpin Location="{Binding Location}"
                             Content="{Binding Label}"
                             Tap="OnStationTap" />
            </DataTemplate>
        </map:MapItemsControl.ItemTemplate>
    </map:MapItemsControl>
</map:Map>

MapItemsControl qui expose, entre autres, les propriétés ItemsSource et ItemTemplate qui nous permettent de spécifier les éléments à afficher et le template à utiliser pour leur rendu à l’écran, respectivement. Dans l’exemple ci-dessus, chaque punaise sera représentée par un Pushpin dont les propriétés sont liées à celles du modèle créé à l’étape précédente. De plus, la méthode OnStationTap sera appelée lorsque l’on appuie sur les repères :

private void OnStationTap(object sender, GestureEventArgs e)
{
    var station = ((sender as Pushpin).DataContext as Pin).Station;
}

Nous savons que c’est un objet de type Pushpin qui appelle la méthode et que son DataContext est de type Pin. Il ne reste plus qu’à appeler la propriété Station de ce dernier type pour obtenir les information de la station que l’utilisateur a choisi.

Lundi nous passerons cette station à une nouvelle page qui sera chargée d’afficher ses détails.

Consommation d’un service OData avec Windows Phone 7 “Mango”

Ceci est le quatrième billet d’une série sur Windows Phone 7 dans laquelle je construis une application permettant de surveiller la disponibilité de vélos des stations Vélib’ à Paris. Pour rappel, voici la table de matières:

  1. Introduction et installation des outils
  2. Création d’une application et utilisation du contrôle Bing Maps
  3. Le GPS et les services de géolocalisation
  4. Consommation d’un service OData
  5. Ajout de punaises sur une carte Bing Maps
  6. Création de nouvelles pages et navigation
  7. Création de vignettes sur la page d’accueil

OData

odataMaintenant que votre application est capable de localiser le téléphone et de centrer la carte sur sa position, il est temps de rechercher les stations Vélib’ se trouvant à proximité. Pour cela nous allons utiliser un service OData qui expose des informations sur les vélos de la ville de Paris.

Note : malheureusement, cette pratique n’est pas exactement bien vue par les fournisseurs du service, donc il est possible que dans l’avenir ces instruction ne soient plus valables.

Tout d’abord, qu’est-ce qu’OData ? Il s’agit d’un protocole ouvert pour le partage de données. Il permet d’exposer des jeux de données aux formats ATOM (XML) ou JSON se basant sur une architecture REST. Je ne rentrerai pas dans les détails, mais je vous laisserai visiter le site officiel si vous voulez en savoir plus.

J’ai choisi une source de données OData parce qu’il s’agit d’un protocole léger et facile à consommer, notamment avec l’inclusion dans les Windows Phone Developer Tools 7.1, que je vais continuer à appeler les outils “Mango”, d’un client OData.

Le client OData

Commençons donc par ajouter une référence au service OData :

Add Service Reference

Voilà, c’est fini !

Stations Vélib’ à proximité

Le client en place, il ne reste plus qu’à récupérer les stations Vélib’ à proximité de l’utilisateur.

Si vous regardez la description du service OData de près :

<FunctionImport Name="GetStationsInRectangle" EntitySet="Stations"
                ReturnType="Collection(UnVelo.Station)" m:HttpMethod="GET">
  <Parameter Name="point1" Type="Edm.String" Mode="In" />
  <Parameter Name="point2" Type="Edm.String" Mode="In" />
</FunctionImport>

Vous remarquerez la présence d’une fonction qui retourne les stations présentes dans un rectangle. Ce rectangle est défini par deux points : le coin nord-ouest et le coin sud-est.

Comme nous souhaitons récupérer la liste de stations lorsque la carte affichée change (déplacement, changement de zoom), vous allez vous abonner à l’évènement ViewChangeEnd, qui sera déclenché une fois que le déplacement ou le changement de zoom est fini :

<map:Map x:Name="Map"
    CredentialsProvider="{StaticResource BingMapsKey}"
    ZoomBarVisibility="Visible" ZoomLevel="17"
    ViewChangeEnd="OnViewChangeEnd" />

Dans le gestionnaire de l’évènement, vous allez utiliser un StationRepository qui a été créé automatiquement par Visual Studio à l’étape précédente, pour lancer un appel asynchrone sur la fonction GetStationsInRectangle.

private readonly string GetStationsInRectangleTemplate = "GetStationsInRectangle?point1='{0}'&point2='{1}'";

private GeoCoordinateWatcher _gcw;
private StationRepository _repository;

public MainPage()
{
    InitializeComponent();

    this._gcw = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
    this._gcw.MovementThreshold = 20;
    this._gcw.PositionChanged += this.OnPositionChanged;

    this._repository = new StationRepository(new Uri("http://unvelo.fr/api/v1/"));

    this.Loaded += this.OnPageLoaded;
}

private void OnViewChangeEnd(object sender, MapEventArgs e)
{
    this._repository.BeginExecute<Station>(
        new Uri(
            string.Format(GetStationsInRectangleTemplate, Map.BoundingRectangle.Northwest, Map.BoundingRectangle.Southeast),
            UriKind.Relative),
        this.GetStationsInRectangleCallbak,
        null);
}

Le client OData créé par Visual Studio supporte uniquement les appels asynchrones dans le but d’éviter des exécutions d’appels longs sur le thread de l’interface graphique, étant donné que ceci dégraderait l’expérience utilisateur. Vous devrez donc créer une méthode callback qui sera appelée à la fin de l’exécution asynchrone pour traiter le résultat de celle-ci :

private void GetStationsInRectangleCallbak(IAsyncResult result)
{
    var stations = this._repository.EndExecute<Station>(result);
}

Vous pouvez vérifier que des stations sont effectivement retournées grâce au débuggeur de Visual Studio. Demain nous les convertirons en punaises à afficher sur la carte. D’ici là, vous avez des données avec lesquelles vous pouvez vous amuser. Si vous avez un peu d’expérience avec Silverlight ou WPF, n’hésitez pas à essayer d’afficher ces données par binding sur différents contrôles.