Developpement Windows Phone - partie 26
Développer avec le GPS Windows Phone (Services de localisation)
Cet article fait partie d’une série d’articles sur le développement Windows Phone. Il s’agit d’une traduction des articles se trouvant sur la MSDN.
Sommaire
Bien débuter et fondamentaux
- Bien démarrer (Hello World)
- Créer une interface utilisateur pour Windows Phone en XAML
- Utiliser des contrôles
- Types de contrôles
- Contrôles Panorama et Pivot
- Travailler avec du texte sur le Windows Phone
- Mise en page sur l’écran
- Orientations de l’écran
- Saisie tactile
- Navigation
- Exécuter votre application en arrière-plan (tombstoning)
- Notifications Push pour le développement Windows Phone
- Publier votre application sur le Marketplace
Visuels et média
Travailler avec les données
- Obtenir des données dans vos applications Windows Phone
- Data binding
- Isolated storage
- Accéder à un service Web REST
- Consommer des données Windows Azure
Sondes et autres fonctionnalités spécifiques au téléphone
- Lanceurs et choosers
- Détecter des mouvements (accéléromètres)
- Développer avec le GPS Windows Phone (Services de localisation)
- Développer avec l’appareil photo du Windows Phone
Développer avec le GPS Windows Phone (Services de localisation)
Silverlight pour Windows Phone inclut un espace de nom qui fournit la gestion et la surveillance en temps réel du service de localisation du téléphone. Le service de localisation fournit l'information la plus précise possible sur l'actuelle position du téléphone en utilisant une combinaison de données Wi-Fi et du réseau cellulaire, ainsi que des données GPS (Global Positionning System). Le service de localisation récupère la latitude, longitude, altitude, vitesse de déplacement, de direction et expose ses données au travers de l'espace de nom System.Devices.Location.
Créer une application Windows Phone utilisant la localisation signifie que certaines fonctionnalités de votre application seront sujettes à la disponibilité des données de localisation. Parce que le service de localisation se base sur les signaux satellites et cellulaires, il est important d'écrire du code qui gère les scénarios suivants:
- L'utilisateur a coupé le GPS ou le récepteur cellulaire sur le téléphone.
- La force du signal est trop faible pour recevoir les données de localisation.
- Le service de localisation est encore en cours d'initialisation ou attend que les données de localisation arrivent jusqu'au téléphone.
Nous allons coder cette application d'une façon qui autorise l'utilisateur à gérer ces scénarios.
Cette QuickApp est une application qui reçoit des données du service de localisation de Windows Phone 7, et positionne les données sur une carte pendant qu'il donne une information visuelle des données. Cet article est divisé en chapitres:
- Créer une interface utilisateur pour la lecture du service de localisation
- Obtenir des données du service de localisation en temps réel
- Considérations finales
Téléchargez le code complet de cette application ici.
Procédure pas à pas
Pour voir un exemple de ce quickstart exécuté sur un réel téléphone, et pour construire cette application en suivant une vidéo plutôt que de continuer à lire cet article, vous pouvez visionner la vidéo suivante. La page pour cette vidéo sur Channel 9 a plusieurs options de téléchargement si vous voulez une version en haute définition ou si vous voulez une version lisible sur des périphériques mobiles.
Créer une interface utilisateur pour la lecture du service de localisation
Lancez Visual Studio et démarrez un projet de type "Silverlight for Windows Phone". Créez alors une interface utilisateur qui contient les éléments suivants:
- Six champs TextBlock avec des libellés compréhensifs pour la lecture de la longitude, de la latitude, de la vitesse et des données de statut.
- Un objet Map sur lequel nous placerons un Pushpin affichant la position actuelle du téléphone.
o Note: Vous aurez besoin d'une clé Bing Maps pour pouvoir utiliser le contrôle carte. Pour en obtenir une, suivez les instructions de l'article Obtenir une clé Bing Maps. Une fois que vous avez une clé, qui est simplement une chaine de caractères, entrez-la en tant que valeur pour la propriété CredentialsProvider dans le code XAML qui définit l'objet Map. Un exemple est fourni dans l'article Accédez au contrôle grâce à une clé Bing Maps.
- Deux Buttons avec un libellé explicite qui vont être utilisés pour démarrer et arrêter le service de localisation ainsi que démarrer le positionnement en temps réel du téléphone sur la carte.
- Des TextBlocks pour afficher les informations récupérées.
Une proposition du positionnement de ces éléments est fournie ci-dessous. Ici, la valeur par défaut des six TextBlocks est changée pour montrer le format et les paramètres utilisés par le service de localisation (par exemple, "Mètres par secondes" est la valeur par défaut pour la vitesse ainsi c'est clair).
XAML
- <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,12">
- <TextBlock Height="30" Margin="12,6,395,0" Name="textBlock1" Text="Long:"
- VerticalAlignment="Top" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,42,0,0" Name="textBlock2"
- Text="Lat:" VerticalAlignment="Top" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="71,6,0,0"
- Name="longitudeTextBlock" Text="Long" VerticalAlignment="Top" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="53,42,0,0"
- Name="latitudeTextBlock" Text="Lat" VerticalAlignment="Top" />
- <my:Map Height="352" HorizontalAlignment="Left" Margin="12,322,0,0" Name="myMap"
- VerticalAlignment="Top" Width="421" CredentialsProvider="KEY" ZoomLevel="1" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,196,0,0"
- Name="textBlock3" Text="Status:" VerticalAlignment="Top" />
- <TextBlock Height="66" HorizontalAlignment="Left" Margin="78,196,0,0"
- Name="statusTextBlock" Text="Status TextBlock w/TextWrapping="Wrap""
- VerticalAlignment="Top" Width="355" TextWrapping="Wrap" />
- <Button Content="Track Me On Map" Height="72" HorizontalAlignment="Left"
- Margin="0,256,0,0" Name="trackMe" VerticalAlignment="Top" Width="255"
- Click="trackMe_Click" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,78,0,0"
- Name="textBlock4" Text="Speed:" VerticalAlignment="Top" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="78,78,0,0"
- Name="speedreadout" Text="Meters Per Second" VerticalAlignment="Top" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,114,0,0"
- Name="textBlock6" Text="Course:" VerticalAlignment="Top" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="84,114,0,0"
- Name="coursereadout" Text="Heading in Degrees (0=N)" VerticalAlignment="Top"
- Width="339" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,150,0,0"
- Name="textBlock5" Text="Altitude:" VerticalAlignment="Top" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="93,150,0,0"
- Name="altitudereadout" Text="Altitude in Meters (0=Sea Level)"
- VerticalAlignment="Top" />
- <Button Content="Stop LocServ" Height="72" HorizontalAlignment="Left"
- Margin="235,256,0,0" Name="startStop" VerticalAlignment="Top"
- Width="209" Click="startStop_Click" />
- </Grid>
Pour faire fonctionner le XAML ci-dessus, vous devez définir l'espace de nom "my:Map" qui est utilisé en ajoutant un attribut xmlns sur le tag phone:PhoneApplicationPage en haut du document XAML qui définit l'UI, tel que:
XAML
- <phone:PhoneApplicationPage
- ...
- xmlns:my=
- "clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps">
En dernier recours, si Visual Studio ne l'a pas fait pour vous lorsque vous avez ajouté les éléments, ajoutez une référence vers Microsoft.Phone.Controls.Maps. Silverlight pour Windows Phone utilise l'assembly Microsoft.Phone.Controls.Maps pour fournir une fonctionnalité de cartographie, qui doit être référencée par votre application avant que l'un de ses types, évènements ou méthodes ne puisse être utilisés par votre code.
Pour ajouter Microsoft.Phone.Controls.Maps à votre application:
- Dans l'explorateur de Solution, cliquez à l'aide du bouton droit de la souris sur le nœud Références de votre projet et sélectionnez Ajouter référence.
- Sélectionnez Microsoft.Phone.Controls.Maps depuis la liste et cliquez sur OK.
Note: Visual Studio devrait automatiquement ajouter l'attribut xmlns et la référence vers Microsoft.Phone.Controls.Maps dès que vous ajoutez le contrôle Map sur l'interface en la glissant depuis la barre d'outils sur le designer.
Obtenir des données du service de localisation en temps réel
Dans l'explorateur de solutions, ouvrez le fichier xaml.cs file, et ajoutez les trois lignes de code suivantes en haut de la page.
C#
- using Microsoft.Phone.Controls.Maps;
- using System.Device.Location;
- using System.Threading;
Visual Basic
- Imports Microsoft.Phone.Controls.Maps
- Imports System.Device.Location
- Imports System.Threading
Cela permet à votre code C# d'accéder au contrôle Bing Maps, à l'API du service de localisation et au modèle de Threading, tout ce que nous utiliserons pour cette application.
L'utilisation du service de localisation nécessitera de la coordination au travers de quelques méthodes et c'est pourquoi nous allons ajouter quelques membres à la classe principale. Le plus notable sera l'objet GeoCoordinateWatcher, qui reçoit constamment les informations du service de localisation dès qu'il est démarré par la méthode TryStart() , que nous allons étudier dans un moment. Nous allons appeler notre watcher. Dès que notre guetteur (watcher) a été démarré, il va essayer de récupérer des informations depuis le service de localisation et exposer ces informations via ses différentes propriétés, ainsi que déclencher l'évènement PositionChanged lorsque l'information de position est mise à jour (c’est-à-dire lorsque le périphérique change de lieu).
Ce membre doit être au niveau de la classe car plusieurs méthodes vont être impliquées dans la lecture des données service de localisation pour gérer les différents évènements que le service va déclencher.
En complément, nous surveillerons si l'utilisateur a activé le tracking sur la carte, et il s'agira encore un membre de classe. Finalement, nous utiliserons le même objet Pushpin tout au long de l'application car nous n'en avons besoin que d'un seul, et nous mettrons à jour sa position continuellement dans différentes méthodes, et sera donc instancié au niveau de la classe. Les trois membres ressemblent à ceci dans le code:
C#
- public partial class MainPage : PhoneApplicationPage
- {
- GeoCoordinateWatcher watcher;
- bool trackingOn = false;
- Pushpin myPushpin = new Pushpin();
- ...
Visual Basic
- Public Partial Class MainPage
- Inherits PhoneApplicationPage
- Private watcher As GeoCoordinateWatcher
- Private trackingOn As Boolean = False
- Private myPushpin As New Pushpin()
- ...
Comme indiqué, il y aura plusieurs méthodes qui utiliseront notre watcher. Pour être exact, il y en aura trois:
- Une méthode qui gère l'évènement PositionChanged du watcher et analyse les nouvelles données. Nous appellerons cette méthode watcher_PositionChanged.
- Une méthode qui gère l'évènement StatusChanged, qui se déclenche lorsque le service de localisation change d'état (inactif, initialisation, réception de données, etc.). Nous appellerons cette méthode watcher_StatusChanged.
- Une méthode qui gèrera le démarrage du watcher par lui-même dans son propre thread. Bien que cette méthode ne fasse qu'une ligne de code, nous voulons compartimenter ce code pour qu'il puisse être lancé dans son propre thread et ne bloque pas l'application pendant que le service de localisation s'initialise. Nous appellerons cette méthode startLocServInBackground.
Nous pourrions vouloir faire cela dès que l'application démarre donc mettons-les dans le constructeur, la méthode MainPage() .
Deux lignes supplémentaires sont nécessaires pour instancier le watcher et mettre à jour l'interface à propos de l'initialisation du service de localisation, laissant la méthode MainPage() ressembler à ceci:
C#
// Constructor
public MainPage()
{
InitializeComponent();
// instantiate watcher, setting its accuracy level and movement threshold.
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High); // using high accuracy;
watcher.MovementThreshold = 10.0f; // meters of change before "PositionChanged"
// wire up event handlers
watcher.StatusChanged += new
EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
watcher.PositionChanged += new
EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
// start up LocServ in bg; watcher_StatusChanged will be called when complete.
new Thread(startLocServInBackground).Start();
statusTextBlock.Text = "Starting Location Service...";
}
Visual Basic
' Constructor
Public Sub New()
InitializeComponent()
' instantiate watcher, setting its accuracy level and movement threshold.
watcher = New GeoCoordinateWatcher(GeoPositionAccuracy.High)
' using high accuracy;
watcher.MovementThreshold = 10.0F
' meters of change before "PositionChanged"
' wire up event handlers
watcher.StatusChanged += New EventHandler(Of GeoPositionStatusChangedEventArgs)(watcher_StatusChanged)
watcher.PositionChanged += New EventHandler(Of GeoPositionChangedEventArgs(Of GeoCoordinate))(watcher_PositionChanged)
' start up LocServ in bg; watcher_StatusChanged will be called when complete.
New Thread(startLocServInBackground).Start()
statusTextBlock.Text = "Starting Location Service..."
End Sub
Il est maintenant temps d'implémenter les gestionnaires d'évènements. Commençons avec watcher_StatusChanged. watcher donnera son statut à la méthode watcher_StatusChanged en utilisant un objet GeoPositionStatusChangedEventArgs passé en argument. Cet objet a une propriété enum appelée GeoPositionStatus qui fournit des noms compréhensibles pour les différents états du service de localisation. Tout ce que nous voulons faire dans notre application est de notifier l'utilisateur lorsque le statut a changé et nous pouvons accomplir cela avec un seul switch qui gère les quatre valeurs possibles GeoPositionStatus:
C#
- void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
- {
- switch (e.Status)
- {
- case GeoPositionStatus.Disabled:
- // The Location Service is disabled or unsupported.
- // Check to see if the user has disabled the Location Service.
- if (watcher.Permission == GeoPositionPermission.Denied)
- {
- // The user has disabled the Location Service on their device.
- statusTextBlock.Text = "You have disabled Location Service.";
- }
- else
- {
- statusTextBlock.Text = "Location Service is not functioning on this device.";
- }
- break;
- case GeoPositionStatus.Initializing:
- statusTextBlock.Text = "Location Service is retrieving data...";
- // The Location Service is initializing.
- break;
- case GeoPositionStatus.NoData:
- // The Location Service is working, but it cannot get location data.
- statusTextBlock.Text = "Location data is not available.";
- break;
- case GeoPositionStatus.Ready:
- // The Location Service is working and is receiving location data.
- statusTextBlock.Text = "Location data is available.";
- break;
- }
- }
Visual Basic
Private Sub watcher_StatusChanged(sender As Object, e As GeoPositionStatusChangedEventArgs)
Select Case e.Status
Case GeoPositionStatus.Disabled
' The Location Service is disabled or unsupported.
' Check to see if the user has disabled the Location Service.
If watcher.Permission = GeoPositionPermission.Denied Then
' The user has disabled the Location Service on their device.
statusTextBlock.Text = "You have disabled Location Service."
Else
statusTextBlock.Text = "Location Service is not functioning on this device."
End If
Exit Select
Case GeoPositionStatus.Initializing
statusTextBlock.Text = "Location Service is retrieving data..."
' The Location Service is initializing.
Exit Select
Case GeoPositionStatus.NoData
' The Location Service is working, but it cannot get location data.
statusTextBlock.Text = "Location data is not available."
Exit Select
Case GeoPositionStatus.Ready
' The Location Service is working and is receiving location data.
statusTextBlock.Text = "Location data is available."
Exit Select
End Select
End Sub
Maintenant que nous pouvons voir ce qui va se passer lorsque le service de localisation sera activé et que l'évènement StatusChanged est déclenché, implémentons la méthode qui s'occupe de l'initialisation et que l'on appellera startLocServInBackground. Cette méthode consistera en une seule ligne qui appelle la méthode TryStart de notre watcher, en spécifiant un timeout de 60 secondes pour l'initialisation. Parce que la méthode TryStart est synchrone, cela pourrait bloquer l'application sur une longue période si nous ne l'avions pas la lancer en arrière-plan, d'où l'utilisation d'un nouveau thread dans l'appel de startLocServInBackground dans la méthode MainPage. Le thread que nous avons démarré dans MainPage sera automatiquement arête avec la période de timeout de la method TryStart ou lorsque le watcher est initialisé avec succès et l’évènement watcher_StatusChanged est déjà implémenté pour gérer tout ce qui passe après l’initialisation :
C#
- void startLocServInBackground()
- {
- watcher.TryStart(true, TimeSpan.FromMilliseconds(60000));
- }
Visual Basic
- Private Sub startLocServInBackground()
- watcher.TryStart(True, TimeSpan.FromMilliseconds(60000))
- End Sub
Avant que nous n'implémentions watcher_PositionChanged, câblons deux boutons qui vont affecter le comportement de l'application lorsque le téléphone change de position. Nous appellerons ces deux boutons "Track Me On Map" et "Stop LocServ," et dans le XAML, vous verrez que nous avons spécifié des gestionnaires pour l'évènement Click pour chacun de ces boutons, nommés respectivement trackMe_Click et startStop_Click.
Ces deux méthodes sont liées à l'activation et la désactivation du service de localisation, la valeur booléenne trackingOn ainsi que watcher_PositionChanged met à jour ou non l'objet carte pour afficher la position actuelle du téléphone. De plus, un petit travail d'interface est fait pour modifier l'affichage du bouton et fournir un message de statut.
Un appel intéressant à l'objet Map est fait dans la méthode trackMe_Click. Si l'utilisateur spécifie qu'il veut voir sa position actuelle sur la carte, le niveau de zoom sur la carte passe de 1.0 (zoom arrière pour montrer la planète entière) à 16.0 (zoom au niveau des rues).
L'activation du service de localisation se fait encore en arrière-plan en utilisant startLocServInBackground. Les deux méthodes ressemblent à ceci:
C#
private void trackMe_Click(object sender, RoutedEventArgs e)
{
if (trackingOn)
{
trackMe.Content = "Track Me On Map";
trackingOn = false;
myMap.ZoomLevel = 1.0f;
}
else
{
trackMe.Content = "Stop Tracking";
trackingOn = true;
myMap.ZoomLevel = 16.0f;
}
}
private void startStop_Click(object sender, RoutedEventArgs e)
{
if (startStop.Content.ToString() == "Stop LocServ")
{
startStop.Content = "Start LocServ";
statusTextBlock.Text = "Location Services stopped...";
watcher.Stop();
}
else if (startStop.Content.ToString() == "Start LocServ")
{
startStop.Content = "Stop LocServ";
statusTextBlock.Text = "Starting Location Services...";
new Thread(startLocServInBackground).Start();
}
}
Visual Basic
Private Sub trackMe_Click(sender As Object, e As RoutedEventArgs)
If trackingOn Then
trackMe.Content = "Track Me On Map"
trackingOn = False
myMap.ZoomLevel = 1.0F
Else
trackMe.Content = "Stop Tracking"
trackingOn = True
myMap.ZoomLevel = 16.0F
End If
End Sub
Private Sub startStop_Click(sender As Object, e As RoutedEventArgs)
If startStop.Content.ToString() = "Stop LocServ" Then
startStop.Content = "Start LocServ"
statusTextBlock.Text = "Location Services stopped..."
watcher.[Stop]()
ElseIf startStop.Content.ToString() = "Start LocServ" Then
startStop.Content = "Stop LocServ"
statusTextBlock.Text = "Starting Location Services..."
New Thread(startLocServInBackground).Start()
End If
End Sub
Enfin, il y a la méthode watcher_PositionChanged qui gère l'évènement PositionChanged,. Cette méthode reçoit des informations à propos de la position actuellement dans l'objet GeoPositionChangedEventArgs, qui est passé en argument. Cet objet contient les informations à propos du périphérique, sa longitude, sa latitude, sa vitesse (en mètres par seconde), sa direction (une valeur flottante entre 0 et 360) et même l'altitude (en mètres au dessus du niveau de la mer). Nous allons afficher toutes ces données dans l'écran que nous avons créé plus tôt.
De plus, si l'utilisateur a cliqué le bouton "Track Me On Map", alors la variable trackingOn est définie à "true," et que la carte est zoomée au niveau de la rue, ainsi, dans la méthode watcher_PositionChanged nous vérifions la valeur trackingOn' et centrons la carte sur notre position actuelle si sa valeur est true. La méthode finale ressemble à ceci:
C#
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//update the TextBlock readouts
latitudeTextBlock.Text = e.Position.Location.Latitude.ToString("0.0000000000000");
longitudeTextBlock.Text = e.Position.Location.Longitude.ToString("0.0000000000000");
speedreadout.Text = e.Position.Location.Speed.ToString("0.0") + " meters per second";
coursereadout.Text = e.Position.Location.Course.ToString("0.0");
altitudereadout.Text = e.Position.Location.Altitude.ToString("0.0");
// update the Map if the user has asked to be tracked.
if (trackingOn)
{
// center the pushpin and map on the current position
myPushpin.Location = e.Position.Location;
myMap.Center = e.Position.Location;
// if this is the first time that myPushpin is being plotted, plot it!
if (myMap.Children.Contains(myPushpin) == false) {myMap.Children.Add(myPushpin);};
}
}
Visual Basic
Private Sub watcher_PositionChanged(sender As Object, e As GeoPositionChangedEventArgs(Of GeoCoordinate))
' met jour les liblls
latitudeTextBlock.Text = e.Position.Location.Latitude.ToString("0.0000000000000")
longitudeTextBlock.Text = e.Position.Location.Longitude.ToString("0.0000000000000")
speedreadout.Text = e.Position.Location.Speed.ToString("0.0") & " meters per second"
coursereadout.Text = e.Position.Location.Course.ToString("0.0")
altitudereadout.Text = e.Position.Location.Altitude.ToString("0.0")
' met jour la carte si l'utilisateur a demand connaitre sa position.
If trackingOn Then
' centre la punaise et la carte sur la position actuelle
myPushpin.Location = e.Position.Location
myMap.Center = e.Position.Location
' si c'est le premire fois que la punaise est plante, on la plante!
If myMap.Children.Contains(myPushpin) = False Then
myMap.Children.Add(myPushpin)
End If
End If
End Sub
Cette application est maintenant complète et vous avez un exemple de programme qui est structuré pour répondre à l'initialisation synchrone du service de localisation, pour garder trace du statut courant du service (qui est sujet à la qualité des signaux GPS et cellulaire) et aussi pour positionner une punaise sur une carte Bing qui affiche à la demande les rues ou la vue satellite.
Considérations finales
- Nous utilisons le paramétrage high-accuracy dans cette application ce qui consommé rapidement la batterie du telephone. Essayez d’utiliser GeoPositionAccuracy.Default autant que possible, comme par exemple lorsque votre application recherche dans les environs.
- Le paramétrage MovementThreshold est un élément clé de la consommation de la batterie, parce qu'en général, les données de position sont conséquentes et l’application ne devrait pas répondre à chaque fluctuation des données. Si l’évènement PositionChanged est déclenché trop fréquemment, la batterie et le temps de processeur seront consommés avec gâchis.
- Parce que le service de localisation utilise une combinaison de données Wi-Fi, cellulaires et GPS, la position devient de plus en plus précise après que le service ait été démarré et au fur et à mesure qu'il reçoit des données. Les informations de réseau Wi-Fi et cellulaire sont généralement reçues en quelques secondes pendant que les données GPS peuvent prendre plusieurs minutes.
- L'objet GeoCoordinateWatcher déclenche des évènements dans le thread UI. Cela signifie que vous voudrez réaliser le moins de travail possible dans les fonctions de gestionnaires des évènements PositionChanged et StatusChanged, ou n'importe quel autre évènement GeoCoordinateWatcher. Si un traitement important a besoin d'être fait lorsque ces évènements sont déclenchés, ayez des fonctions de traitement agissant dans des threads d'arrière-plan pour éviter que l'interface ne se fige.
Voir aussi
- Source Code For This QuickApp
- System.Device.Location Namespace (Reference Docs)
- Bing Maps Silverlight Control (Reference Docs)
- Location for Windows Phone (How To Docs)