• Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 29/04/2014

Le contrôle de cartes (Map)

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Avant de commencer à regarder le contrôle Map, il faut savoir qu’il est différent entre la version du SDK pour développer pour Windows Phone 7 et celui pour développer pour Windows Phone 8. Le premier est le contrôle de cartes de Microsoft alors que pour Windows Phone 8, c’est celui réalisé par Nokia qui est utilisé. Ils se ressemblent cependant fortement dans leurs utilisations, mais celui de Nokia a une particularité intéressante lui permettant de faire des choses hors connexion que ne permet pas celui de Microsoft. Mais rassurez-vous, nul besoin de posséder un téléphone Nokia pour pouvoir s’en servir, il fonctionne pour tous les téléphones Windows Phone 8.

Je vais présenter ici le contrôle de Windows Phone 8, mais sachez qu'il est également possible d'utiliser l'ancien contrôle avec des applications Windows Phone 8. Il s’agit d’un contrôle très complet et bien pratique : le contrôle Map. Il permet d’embarquer une carte dans notre application. Nous allons pouvoir afficher la carte de France, la carte d’Europe, etc … définir des positions, calculer des itinéraires … Bref, tout plein de choses qui peuvent servir à nos téléphones équipés de GPS.
Découvrons à présent ce fameux contrôle.

Présentation et utilisation

Pour utiliser le contrôle Map, le plus simple est de le faire glisser depuis la boite à outils pour le mettre par exemple à l’intérieur de la grille, comme indiqué à la figure suivante.

Le contrôle de carte dans la boite à outils
Le contrôle de carte dans la boite à outils

Visual Studio nous ajoute le contrôle et nous pouvons voir dans la fenêtre de design une mini carte du monde (voir la figure suivante).

Le contrôle map dans le designer
Le contrôle map dans le designer

Après l’ajout du contrôle, si nous regardons le XAML, Visual Studio nous a ajouté l’espace de nom suivant :

xmlns:Controls="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"

ainsi que le XAML positionnant le contrôle :

<Controls:Map />

Personnellement, je change le « Controls » en « carte » et je donne le nom « Carte » à mon contrôle :

<carte:Map Name="Carte" />

Si vous démarrez l’émulateur tout de suite, vous allez avoir un problème. Visual Studio nous lève une exception de type :

Une exception de première chance de type 'System.Windows.Markup.XamlParseException' s'est produite dans System.Windows.ni.dll

Mais si on fouille un peu dans les InnerException, on trouve plutôt :

Access to Maps requires ID_CAP_MAP to be defined in the manifest

En fait, pour utiliser le contrôle de cartes, nous devons déclarer notre application comme utilisatrice du contrôle de cartes. Cela permettra notamment aux personnes qui veulent télécharger notre application de savoir qu’elle utilise le contrôle de carte. On appelle cela les capacités. Pour déclarer une capacité, nous allons avoir besoin de double-cliquer sur le fichier WMAppManifest.xml (sous Properties dans l’explorateur de solutions) et d’aller dans l’onglet Capacités. Ensuite, il faut cocher la capacité ID_CAP_MAP, comme l'indique la figure suivante.

Activer la capacité d'utilisation de carte
Activer la capacité d'utilisation de carte

Et voilà, maintenant en démarrant l’émulateur, nous allons avoir une jolie carte du monde (si vous êtes connectés à Internet) - voir la figure suivante.

La carte s'affiche dans l'émulateur
La carte s'affiche dans l'émulateur

Interactions avec le contrôle

Et si nous prenions le contrôle de la carte ?
On peut tout faire avec cette carte, comme se positionner à un emplacement précis grâce à des coordonnées GPS, ajouter des marques pour épingler des lieux, zoomer, dé-zoomer, etc.
Pour illustrer tout cela, nous allons manipuler cette fameuse carte. Première chose à faire, nous allons afficher des boutons pour zoomer et dé-zoomer. Utilisons par exemple une barre d’application pour ce faire et intégrons-y les deux icônes suivantes, présentes dans le répertoire du SDK :

  • add.png

  • minus.png

N’hésitez pas à aller faire un tour dans le chapitre de la barre d’application si vous avez un doute sur la marche à suivre.
Voici donc le XAML de la barre d’application utilisée :

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Assets/Icones/add.png" Text="Zoom" Click="Zoom_Click"/>
        <shell:ApplicationBarIconButton IconUri="/Assets/Icones/minus.png" Text="Dé-zoom" Click="Dezoom_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Comme d’habitude, prenez garde au chemin des icônes.
Et le code-behind associé :

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void Zoom_Click(object sender, EventArgs e)
    {
        Carte.ZoomLevel++;
    }

    private void Dezoom_Click(object sender, EventArgs e)
    {
        Carte.ZoomLevel--;
    }
}

Vous pouvez voir que nous pouvons facilement zoomer ou dé-zoomer en utilisant les boutons de la barre d’application, et tout ça grâce à la propriété ZoomLevel. De plus, nous pouvons nous déplacer sur la carte en faisant glisser notre doigt (ou notre souris !), comme indiqué à la figure suivante.

Déplacement et zoom pour afficher la carte de Paris
Déplacement et zoom pour afficher la carte de Paris

Tout au long de l’utilisation, sur un téléphone, nous pouvons également zoomer et dé-zoomer grâce au geste qui consiste à ramener ses deux doigts vers le centre de l’écran ou au contraire à les écarter pour dé-zoomer (geste que l’on appelle le Pinch-to-zoom ou le Stretch-to-zoom). C’est par contre un peu plus difficile à faire à la souris dans l’émulateur, sachant que nous avons quand même la possibilité de zoomer en double-cliquant... Mais cela sera quand même bien plus simple dans l'émulateur avec la barre d'application. ;)

Nous pouvons aussi centrer la carte à un emplacement précis. Pour cela, on utilise la propriété Center qui est du type GeoCoordinate. Il s’agit d’une classe contenant des coordonnées GPS, avec notamment la latitude et la longitude. Nous pouvons utiliser des coordonnées pour centrer la carte à un emplacement désiré. Par exemple :

public MainPage()
{
    InitializeComponent();
    Carte.ZoomLevel = 17;
    Carte.Center = new GeoCoordinate { Latitude = 48.858115, Longitude = 2.294710 };
}

qui centre la carte sur la tour Eiffel. On n’oubliera pas d’inclure l’espace de nom permettant d’utiliser le type GeoCoordinate :

using System.Device.Location;

La carte est également disponible en mode satellite (ou aérien), il suffit de changer la propriété CartographicMode du contrôle pour passer en mode aérien avec :

Carte.CartographicMode = MapCartographicMode.Aerial;

Disponible avec l’import d’espace de nom suivant :

using Microsoft.Phone.Maps.Controls;

Vous pouvez voir le résultat à la figure suivante.

La carte en mode aérien
La carte en mode aérien

Le mode route, par défaut, correspond à la valeur d’énumération MapCartographicMode.Road, sachant qu’il existe également la valeur Terrain et Hybrid.
Nous pouvons inclure des points de repères dans la carte grâce à la propriété LandmarksEnabled en la passant à True. À la figure suivante, la même carte sans et avec points de repères.

La carte peut afficher des points de repères
La carte peut afficher des points de repères

Vous noterez la différence ;) .
Vous pouvez également indiquer un degré d’inclinaison grâce à la propriété Pitch. Voici par exemple à la figure suivante la carte avec un degré d’inclinaison de 0 et de 45.

La carte sans et avec un degré d'inclinaison
La carte sans et avec un degré d'inclinaison

Nous pouvons également inclure les informations piétonnes, comme les escaliers grâce à la propriété PedestrianFeaturesEnabled en la passant à True (voir la figure suivante).

La carte avec les informations piétonnes
La carte avec les informations piétonnes

Epingler des points d’intérêt

Une des grandes forces du contrôle Map est qu'on peut dessiner n'importe quoi par dessus, pour par exemple épingler des points d’intérêts sur la carte. Cela permet de mettre en valeur certains lieux et pourquoi pas afficher une information contextuelle complémentaire. Le principe est simple, il suffit d’ajouter des coordonnées GPS et une punaise apparaît automatiquement à cet emplacement.
Cela ne se fait par contre pas tout seul, mais grâce au Windows Phone Toolkit que nous venons de voir. Ajoutez une référence à celui-ci, comme nous venons de le faire, et utilisons la classe PushPin qui représente une telle épingle. Il faut dans un premier temps importer l’espace de nom :

xmlns:toolkitcarte="clr-namespace:Microsoft.Phone.Maps.Toolkit;assembly=Microsoft.Phone.Controls.Toolkit"

puis ajouter des éléments PushPin :

<carte:Map Name="Carte">
    <toolkitcarte:MapExtensions.Children>
        <toolkitcarte:Pushpin GeoCoordinate="48.842276, 2.321747" />
        <toolkitcarte:Pushpin GeoCoordinate="48.858115, 2.294710" />
        <toolkitcarte:Pushpin GeoCoordinate="48.873783, 2.294930" />
    </toolkitcarte:MapExtensions.Children>
</carte:Map>

Et nous aurons ce résultat (voir la figure suivante).

Les punaises pour épingler des points d'intérêts
Les punaises pour épingler des points d'intérêts

Pour avoir le même rendu avec le code behind, il suffit d’avoir le XAML suivant :

<carte:Map Name="Carte" />

Et le code suivant :

public MainPage()
{
    InitializeComponent();
    Carte.ZoomLevel = 12;
    Carte.Center = new GeoCoordinate { Latitude = 48.863134, Longitude = 2.320518 };

    var elts = MapExtensions.GetChildren(Carte);
    MapExtensions.Add(elts, new Pushpin(), new GeoCoordinate { Longitude = 2.321747, Latitude = 48.842276 });
    MapExtensions.Add(elts, new Pushpin(), new GeoCoordinate { Longitude = 2.294710, Latitude = 48.858115 });
    MapExtensions.Add(elts, new Pushpin(), new GeoCoordinate { Longitude = 2.294930, Latitude = 48.873783 });
}

La punaise est bien sûr stylisable à souhait, car celle-là est un peu triste. Prenez par exemple celle-ci, permettant de remplacer la grosse punaise par un petit point bleu :

<phone:PhoneApplicationPage.Resources>
    <ControlTemplate x:Key="PushpinControlTemplate" TargetType="toolkitcarte:Pushpin">
        <Ellipse Fill="{TemplateBinding Background}"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Width="20"
                    Height="20"
                    Stroke="{TemplateBinding Foreground}"
                    StrokeThickness="3" />
    </ControlTemplate>

    <Style TargetType="toolkitcarte:Pushpin" x:Key="PushpinControlTemplateEllipse">
        <Setter Property="Template" Value="{StaticResource PushpinControlTemplate}" />
        <Setter Property="Background" Value="Blue" />
        <Setter Property="Foreground" Value="White" />
    </Style>
</phone:PhoneApplicationPage.Resources>

Que nous pourrons utiliser ainsi :

<carte:Map Name="Carte">
    <toolkitcarte:MapExtensions.Children>
        <toolkitcarte:Pushpin GeoCoordinate="48.842276, 2.321747" Style="{StaticResource PushpinControlTemplateEllipse}" />
        <toolkitcarte:Pushpin GeoCoordinate="48.858115, 2.294710" Style="{StaticResource PushpinControlTemplateEllipse}" />
        <toolkitcarte:Pushpin GeoCoordinate="48.874956, 2.350690" Style="{StaticResource PushpinControlTemplateEllipse}" />
    </toolkitcarte:MapExtensions.Children>
</carte:Map>

ou la même chose avec le code behind :

var elts = MapExtensions.GetChildren(Carte);
MapExtensions.Add(elts, new Pushpin { Style = Resources["PushpinControlTemplateEllipse"] as Style }, new GeoCoordinate { Longitude = 2.321747, Latitude = 48.842276 });
MapExtensions.Add(elts, new Pushpin { Style = Resources["PushpinControlTemplateEllipse"] as Style }, new GeoCoordinate { Longitude = 2.294710, Latitude = 48.858115 });
MapExtensions.Add(elts, new Pushpin { Style = Resources["PushpinControlTemplateEllipse"] as Style }, new GeoCoordinate { Longitude = 2.350690, Latitude = 48.874956 });

Ce qui donne le résultat affiché à la figure suivante.

Les punaises ont du style !
Les punaises ont du style !

D’où vient cette punaise OpenClassrooms ? Simplement de mon autre style utilisant une image comme punaise :

<ControlTemplate x:Key="PushpinControlTemplateImage" TargetType="toolkitcarte:Pushpin">
    <Image Source="http://open-e-education-2013.openclassrooms.com/img/logos/logo-openclassrooms.png" Width="60" Height="60" />
</ControlTemplate>

<Style TargetType="toolkitcarte:Pushpin" x:Key="PushpinControlTemplateImageStyle">
    <Setter Property="Template" Value="{StaticResource PushpinControlTemplateImage}" />
</Style>

Le logo d'OpenClassrooms pour indiquer l’emplacement des locaux d'OpenClassrooms ? C’est pas la classe ça ? :p
Ajouter des punaises en spécifiant des coordonnées dans le XAML, c’est bien. Mais nous pouvons également le faire par binding !
Voyez par exemple avec ce XAML :

<carte:Map Name="Carte">
    <toolkitcarte:MapExtensions.Children>
        <toolkitcarte:Pushpin GeoCoordinate="{Binding PositionTourEiffel}" Style="{StaticResource PushpinControlTemplateEllipse}" />
    </toolkitcarte:MapExtensions.Children>
</carte:Map>

La propriété GeoCoordinate de l’objet Pushpin est liée à la propriété PositionTourEiffel, qui sera du genre :

private GeoCoordinate positionTourEiffel;
public GeoCoordinate PositionTourEiffel
{
    get { return positionTourEiffel; }
    set { NotifyPropertyChanged(ref positionTourEiffel, value); }
}

Que l’on pourra alimenter avec :

public MainPage()
{
    InitializeComponent();
    Carte.ZoomLevel = 12;
    Carte.Center = new GeoCoordinate { Latitude = 48.863134, Longitude = 2.320518 };
    PositionTourEiffel = new GeoCoordinate { Longitude = 2.321747, Latitude = 48.842276 };
    DataContext = this;
}

N’oubliez pas d’implémenter correctement INotifyPropertyChanged, mais bon, vous savez faire maintenant. ;)
On peut même leur rajouter un petit texte grâce à la propriété Content :

<toolkitcarte:Pushpin GeoCoordinate="{Binding PositionTourEiffel}" Content="{Binding Texte}" />

Avec :

private string texte;
public string Texte
{
    get { return texte; }
    set { NotifyPropertyChanged(ref texte, value); }
}

Et :

Texte = "La tour Eiffel";

Ce qui donne la figure suivante.

Légende sur les punaises
Légende sur les punaises

Notez que j’ai retiré le style ellipse bleue car ce style ne prenait pas en charge la propriété Content. Pour ce faire, il faudrait modifier le style pour avoir, par exemple :

<ControlTemplate x:Key="PushpinControlTemplate" TargetType="toolkitcarte:Pushpin">
    <StackPanel>
        <ContentPresenter Content="{TemplateBinding Content}" />
        <Ellipse Fill="{TemplateBinding Background}"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Width="20"
            Height="20"
            Stroke="{TemplateBinding Foreground}"
            StrokeThickness="3" />
    </StackPanel>
</ControlTemplate>

Et le contrôle serait :

<carte:Map Name="Carte">
    <toolkitcarte:MapExtensions.Children>
        <toolkitcarte:Pushpin GeoCoordinate="{Binding PositionTourEiffel}" Style="{StaticResource PushpinControlTemplateEllipse}" Content="{Binding Texte}" Foreground="Black" />
    </toolkitcarte:MapExtensions.Children>
</carte:Map>

Pour le résultat, observez la figure suivante.

Le style avec une légende
Le style avec une légende

Et si on a plusieurs punaises à lier ? On pourrait être tentés d’utiliser le binding, mais à ce jour ce n’est pas fonctionnel. Peut-être un bug à corriger ?
On peut quand même s’en sortir grâce au code-behind. La première chose à faire est de définir un MapItemsControl possédant un template :

<carte:Map Name="Carte">
    <toolkitcarte:MapExtensions.Children>
        <toolkitcarte:MapItemsControl>
            <toolkitcarte:MapItemsControl.ItemTemplate>
                <DataTemplate>
                    <toolkitcarte:Pushpin GeoCoordinate="{Binding}" Style="{StaticResource PushpinControlTemplateEllipse}" />
                </DataTemplate>
            </toolkitcarte:MapItemsControl.ItemTemplate>
        </toolkitcarte:MapItemsControl>
    </toolkitcarte:MapExtensions.Children>
</carte:Map>

Il faudra ensuite lier par exemple une liste de positions à ce MapItemsControl via code-behind :

List<GeoCoordinate> maListeDePositions = new List<GeoCoordinate> 
{ 
    new GeoCoordinate { Longitude = 2.321747, Latitude = 48.842276 },
    new GeoCoordinate { Longitude = 2.294710, Latitude = 48.858115 },
    new GeoCoordinate { Longitude = 2.294930, Latitude = 48.873783 }
};

MapItemsControl mapItemsControl = MapExtensions.GetChildren(Carte).OfType<MapItemsControl>().FirstOrDefault();
mapItemsControl.ItemsSource = maListeDePositions;

Comme ceci, cela fonctionne et donne le résultat que vous voyez sur la figure suivante.

Plusieurs punaises liées par code-behind
Plusieurs punaises liées par code-behind

Personnellement, j’adore ces punaises ! :)

Elles peuvent également réagir à un clic, ce qui nous laisse l’opportunité d’afficher par exemple des informations complémentaires sur cette position. On utilisera l’événement Tap du contrôle Pushpin. Sauf qu’en général, si on utilise une liste de positions comme on l’a fait juste au-dessus, nous ne disposons pas d’informations suffisantes pour savoir quelle punaise a été cliquée. Ce qu’on peut faire à ce moment-là, c’est utiliser la propriété Tag du contrôle, qui est une espèce d’objet fourre-tout pour passer des informations.

Illustrons ce point en créant une nouvelle classe :

public class MaPosition
{
    public GeoCoordinate Position { get; set; }
    public string Informations { get; set; }
}

Puis, changeons notre propriété MaListeDePositions pour avoir une liste d’objets MaPosition :

private IEnumerable<MaPosition> maListeDePositions;
public IEnumerable<MaPosition> MaListeDePositions
{
    get { return maListeDePositions; }
    set { NotifyPropertyChanged(ref maListeDePositions, value); }
}

Qui sera alimentée de cette façon :

MaListeDePositions = new List<MaPosition> 
{ 
    new MaPosition { Position = new GeoCoordinate { Longitude = 2.321747, Latitude = 48.842276 }, Informations = "Tour Eiffel"},
    new MaPosition { Position = new GeoCoordinate { Longitude = 2.294710, Latitude = 48.858115 }, Informations = "Tour Montparnasse"},
    new MaPosition { Position = new GeoCoordinate { Longitude = 2.294930, Latitude = 48.873783 }, Informations = "Arc de triomphe"}
};

Puis changeons le XAML pour avoir :

<carte:Map Name="Carte">
    <toolkitcarte:MapExtensions.Children>
        <toolkitcarte:MapItemsControl ItemsSource="{Binding PositionList}">
            <toolkitcarte:MapItemsControl.ItemTemplate>
                <DataTemplate>
                    <toolkitcarte:Pushpin GeoCoordinate="{Binding Position}" Style="{StaticResource PushpinControlTemplateEllipse}" Tag="{Binding Informations}" Tap="Pushpin_Tap" />
                </DataTemplate>
            </toolkitcarte:MapItemsControl.ItemTemplate>
        </toolkitcarte:MapItemsControl>
    </toolkitcarte:MapExtensions.Children>
</carte:Map>

Notons que la propriété GeoCoordinate de la punaise est liée à la propriété Position de notre classe et que la propriété Tag est liée à la propriété Informations. Ce qui nous permet, dans l’événement associé, de faire :

private void Pushpin_Tap(object sender, GestureEventArgs e)
{
    MessageBox.Show(((FrameworkElement)sender).Tag.ToString());
}

Et nous aurons ce résultat (voir la figure suivante).

Le clic sur une punaise nous déclenche l'affichage d'un message
Le clic sur une punaise nous déclenche l'affichage d'un message

Pratique.

Afficher un itinéraire

Bonne nouvelle, avec Windows Phone 8, il est très facile de calculer et d’afficher un itinéraire entre deux points. Prenons une carte classique :

<maps:Map x:Name="Carte" />

Et calculons dans le code-behind un itinéraire entre des coordonnées de départ, disons la Tour Montparnasse, jusqu’aux Champs-Élysées. Il suffit d’utiliser un objet GeocodeQuery et de faire :

public partial class MainPage : PhoneApplicationPage
{
    private List<GeoCoordinate> coordonnesTrajet;

    public MainPage()
    {
        InitializeComponent();

        Carte.ZoomLevel = 13;
        Carte.Center = new GeoCoordinate { Latitude = 48.863134, Longitude = 2.320518 };
        coordonnesTrajet = new List<GeoCoordinate>();
            
        CalculerItineraire();
    }

    private void CalculerItineraire()
    {
        GeoCoordinate positionDepart = new GeoCoordinate(48.858115, 2.294710);
        coordonnesTrajet.Add(positionDepart);

        GeocodeQuery geocodeQuery = new GeocodeQuery
            {
                SearchTerm = "avenue des champs-élysées, Paris",
                GeoCoordinate = positionDepart
            };

        geocodeQuery.QueryCompleted += geocodeQuery_QueryCompleted;
        geocodeQuery.QueryAsync();
    }

    private void geocodeQuery_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
    {
        if (e.Error == null)
        {
            RouteQuery routeQuery = new RouteQuery();
            coordonnesTrajet.Add(e.Result[0].GeoCoordinate);
            routeQuery.Waypoints = coordonnesTrajet;
            routeQuery.QueryCompleted += routeQuery_QueryCompleted;
            routeQuery.QueryAsync();
        }
    }

    void routeQuery_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
    {
        if (e.Error == null)
        {
            Carte.AddRoute(new MapRoute(e.Result));
        }
    }
}

Dans l’objet GeocodeQuery il suffit de renseigner la destination souhaitée et la méthode QueryAsync() calcule automatiquement le trajet et nous fournit de quoi construire une route à ajouter à la carte grâce à la méthode AddRoute. Remarquez que la destination souhaitée peut également être des coordonnées GPS.

Et nous aurons la figure suivante.

Calcul d'itinéraire entre deux points
Calcul d'itinéraire entre deux points

Il est bien sûr possible d’appliquer la même technique que pour le WebClient afin de pouvoir utiliser les mots-clés await et async. Il suffit de rajouter ces méthodes dans une classe d’extensions :

public static class Extensions
{
    public static Task<IList<MapLocation>> QueryLocationAsync(this GeocodeQuery geocodeQuery)
    {
        TaskCompletionSource<IList<MapLocation>> taskCompletionSource = new TaskCompletionSource<IList<MapLocation>>();
        EventHandler<QueryCompletedEventArgs<IList<MapLocation>>> queryCompleteddHandler = null;
        queryCompleteddHandler = (s, e) =>
        {
            geocodeQuery.QueryCompleted -= queryCompleteddHandler;
            if (e.Error != null)
                taskCompletionSource.TrySetException(e.Error);
            else
                taskCompletionSource.TrySetResult(e.Result);
        };

        geocodeQuery.QueryCompleted += queryCompleteddHandler;
        geocodeQuery.QueryAsync();

        return taskCompletionSource.Task;
    }

    public static Task<Route> QueryRouteAsync(this RouteQuery routeQuery)
    {
        TaskCompletionSource<Route> taskCompletionSource = new TaskCompletionSource<Route>();
        EventHandler<QueryCompletedEventArgs<Route>> queryCompleteddHandler = null;
        queryCompleteddHandler = (s, e) =>
        {
            routeQuery.QueryCompleted -= queryCompleteddHandler;
            if (e.Error != null)
                taskCompletionSource.TrySetException(e.Error);
            else
                taskCompletionSource.TrySetResult(e.Result);
        };

        routeQuery.QueryCompleted += queryCompleteddHandler;
        routeQuery.QueryAsync();

        return taskCompletionSource.Task;
    }
}

Et ensuite de faire :

public partial class MainPage : PhoneApplicationPage
{
    private List<GeoCoordinate> coordonnesTrajet;

    public MainPage()
    {
        InitializeComponent();

        Carte.ZoomLevel = 13;
        Carte.Center = new GeoCoordinate { Latitude = 48.863134, Longitude = 2.320518 };
        coordonnesTrajet = new List<GeoCoordinate>();

        CalculerItineraire();
    }

    private async void CalculerItineraire()
    {
        GeoCoordinate positionDepart = new GeoCoordinate(48.858115, 2.294710);
        coordonnesTrajet.Add(positionDepart);

        GeocodeQuery geocodeQuery = new GeocodeQuery
        {
            SearchTerm = "avenue des champs-élysées, Paris",
            GeoCoordinate = positionDepart
        };

        try
        {
            var resultat = await geocodeQuery.QueryLocationAsync();

            RouteQuery routeQuery = new RouteQuery();
            coordonnesTrajet.Add(resultat[0].GeoCoordinate);
            routeQuery.Waypoints = coordonnesTrajet;
            var route = await routeQuery.QueryRouteAsync();
            Carte.AddRoute(new MapRoute(route));
        }
        catch (Exception)
        {
        }
    }
}

Nous pouvons même avoir les instructions de déplacement. Rajoutons une ListBox dans le XAML :

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <maps:Map x:Name="Carte" />
    <ListBox x:Name="ListeDirections" Grid.Row="1">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

Que nous pouvons lier à la liste d’instruction, obtenues ainsi :

try
{
    var resultat = await geocodeQuery.QueryLocationAsync();

    RouteQuery routeQuery = new RouteQuery();
    coordonnesTrajet.Add(resultat[0].GeoCoordinate);
    routeQuery.Waypoints = coordonnesTrajet;
    var route = await routeQuery.QueryRouteAsync();
    Carte.AddRoute(new MapRoute(route));

    List<string> routeList = new List<string>();
    foreach (RouteLeg leg in route.Legs)
    {
        foreach (RouteManeuver maneuver in leg.Maneuvers)
        {
            routeList.Add(maneuver.InstructionText);
        }
    }

    ListeDirections.ItemsSource = routeList;
}
catch (Exception)
{
}

Ce qui donne la résultat affiché à la figure suivante.

Les instructions de l'itinéraire
Les instructions de l'itinéraire

À noter que si vous souhaitez calculer un itinéraire piéton, vous pouvez changer le mode de calcul en modifiant l’objet RouteQuery :

routeQuery.TravelMode = TravelMode.Walking;
  • Le contrôle Map est un contrôle très puissant qui nous permet d’exploiter les cartes du monde entier dans nos applications.

  • Il est possible d’épingler des points d’intérêts grâce aux objets PushPin du toolkit Windows Phone.

  • Le contrôle de carte fournit de quoi calculer un itinéraire, que l’on peut afficher sur la carte.

Exemple de certificat de réussite
Exemple de certificat de réussite