Desarrollando con el GPS de Windows Phone (Servicios de Ubicación)

Este artículo es una traducción del artículo original encontrado en: http://create.msdn.com/en-US/education/quickstarts/Developing_with_the_Windows_Phone_GPS_%28Location_Services%29 .

 

Silverlight para Windows Phone incluye un espacio de nombres que proporciona un control y monitoreo en tiempo real de los servicios de la ubicación del teléfono. El servicio de ubicación proporciona la mejor información disponible sobre la ubicación actual del teléfono mediante una combinación de Wi-Fi y datos de red de celular, así como datos de un sistema de posicionamiento global (GPS). El servicio de ubicación traza la longitud actual, la latitud, la altitud, la velocidad del recorrido, y la dirección del teléfono, exponiendo el acceso a estos datos a través del espacio de nombres System.Devices.Location.  

 

La creación de una aplicación del teléfono que tenga noción de la ubicación implica que ciertas funcionalidades en tu aplicación estarán sujetas a la disponibilidad de datos de ubicación. Debido a que los Servicios de Ubicación se basan en señales de celular y de satélites, es importante escribir código que maneje los siguientes escenarios:

 

·         El usuario ha encendido los receptores GPS y/o de celular en el dispositivo.

·         La cobertura de la señal es muy pobre para recibir datos de ubicación.

·         El servicio de ubicación todavía esta inicializando o esperando por datos de ubicación para alcanzar al dispositivo.

Estaremos codificando esta aplicación de tal manera que permita al usuario manejar estos escenarios.

 

Este tutorial es una aplicación de Silverlight para Windows Phone, que recibe entrada del servicio de ubicación de Windows Phone 7 y traza los datos en un mapa mientras que indica una lectura visual de los datos.  Este artículo está dividido en las siguientes secciones:

·         Creando una interfaz de usuario para la lectura del servicio de ubicación.

·         Obteniendo datos en tiempo real del servicio de ubicación.

·         Consideraciones finales.

Descarga el código fuente completo para este tutorial, hospedado en la galería de código de Silverlight para Windows Phone.

 

Inicio rápido en video

Para ver un ejemplo de este tutorial ejecutando en un teléfono real, y para construir este tutorial siguiendo un video en vez de continuar leyendo este artículo, puedes ver el video a continuación. La página de este video en el Channel 9 tiene varias opciones de descarga, si quisieras una versión de alta definición, o si quisieras descargar una versión que pueda ser reproducida en otros dispositivos, además de una computadora.

 

Creando una interfaz de usuario para la lectura del servicio de ubicación.

Inicia Visual Studio y comienza un proyecto de tipo "Silverlight para Windows Phone". A continuación, crea una interfaz de usuario que tendrá los siguientes elementos:

 

·         Seis campos TextBlock con nombres significativos, para la lectura de la longitud, latitud, velocidad, curso, altitud, y el estado de los datos.

·         Un objeto Map, sobre el que vamos a colocar un Pushpin con la ubicación actual del teléfono.  Nota: Necesitas obtener una clave de Bing Maps con el fin de utilizar el control del mapa. Para obtener una, sigue las instrucciones en el artículo Obteniendo una clave de Bing Maps.  Una vez que tengas una clave, que es simplemente una cadena, introduce la cadena como el valor de la propiedad CredentialsProvider en el código XAML que define el objeto Map. Se proporciona un ejemplo en el artículo Accediendo al control usando una clave de Bing Maps.

·         Dos Buttons con nombres significativos que serán utilizados para iniciar y detener el servicio de ubicación e iniciar el trazado en tiempo real de la posición del teléfono en el Mapa.

·         TextBlocks según sea necesario para proporcionar el etiquetado de la información de lectura.

 

Una distribución sugerida de estos elementos se proporciona a continuación.  En este caso, los valores predeterminados para los seis TextBlocks que proporcionan los datos de lectura cambia para mostrar el formato y los parámetros utilizados por el servicio de ubicación cuando proporciona los datos (por ejemplo, "Metros Por Segundo" es el valor predeterminado de velocidad, por lo que está claro después).

 

clip_image002

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=&quot;Wrap&quot;"

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>

 

 

Para que el XAML de arriba funcione, necesitas definir el espacio de nombres "my:Map" que se utiliza al agregar un atributo xmlns en la etiqueta phone:PhoneApplicationPage en la parte superior del documento XAML que define la interfaz de usuario, de la siguiente manera:

 

XAML

<phone:PhoneApplicationPage

xmlns:my=

"clr-  

namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls

    .Maps">

 

Por último, si Visual Studio no lo ha hecho automáticamente en respuesta a estos elementos de la interfaz de usuario definida, agrega una referencia a Microsoft.Phone.Controls.Maps. Silverlight para Windows Phone utiliza el ensamblado Microsoft.Phone.Controls.Maps para proporcionar funcionalidad de mapeo, por lo que debes hacer referencia en tu proyecto antes que cualquiera de sus tipos, eventos o métodos se pueden utilizar en tu aplicación.

 

Para agregar Microsoft.Phone.Controls.Maps a su aplicación:

1.       En el Explorador de Soluciones, haz clic en el nodo References en tu proyecto y, a continuación, selecciona Agregar Referencia.

2.       Selecciona Microsoft.Phone.Controls.Maps de la lista, a continuación, haz clic en OK.

 

Nota: Visual Studio automáticamente agregar el atributo xmlns en la parte superior de la página XAML y agrega la referencia a Microsoft.Phone.Controls.Maps si agregas el control Map a la interfaz de usuario arrastrándolo hasta el diseñador desde el cuadro de herramientas.

 

Obteniendo datos en tiempo real del servicio de ubicación

En el Explorador de Soluciones, abre el archivo MainPage.xaml.cs, y agrega las siguientes tres líneas de código en la parte superior de la página.

 

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

 

Esto da a tu código C# acceso al control de Bing Maps, el API del servicio de ubicación, y el modelo de Hilos, que vamos a utilizar para este tutorial.

El uso del servicio de ubicación para Windows Phone requerirá coordinación a través de algunos métodos, por lo que vamos a añadir algunos datos a los miembros de la clase principal. Lo más notable será el objeto GeoCoordinateWatcher, que recibe constantemente el servicio de ubicación más reciente tan pronto se haya "iniciado" a través del método TryStart(), que vamos a demostrar en un momento.  Vamos a llamar watcher al nuestro.  Tan pronto como el watcher ha sido iniciado, intentará recuperar la información de ubicación del Servicio de Ubicación, expondrá esa información a través de sus diversas propiedades, y también disparará el evento PositionChanged cuando la información de ubicación sea actualizada (es decir, cuando el dispositivo cambie de ubicación).

Este miembro de datos debe ser a nivel de clase, porque son varios los métodos que deben participar en la lectura de entrada de servicios de ubicación con el fin de manejar los diversos eventos que disparará el servicio de ubicación.

Además, vamos a verificar si el usuario ha habilitado el rastreo en mapa, de modo que será también un miembro de datos de la clase.  Por último, vamos a utilizar el objeto Pushpin en todo el tutorial, ya que sólo necesitamos uno y constantemente actualizaremos su posición en los diferentes métodos se creará una instancia a nivel de clase.  Los tres miembros de datos resultantes se observan en el siguiente código:

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()

 

Como se ha mencionado, habrá varios métodos involucrados con el manejo de watcher. De hecho, serán tres:

 

1.       Un método que maneja el evento PositionChanged de watcher y procesa los nuevos datos.  Vamos a llamar a este método watcher_PositionChanged.

2.       Un método que maneja el evento StatusChanged de watcher que se dispara cuando el servicio de ubicación cambia su estado de estar inactivo, a inicializando, a recibiendo datos, y todos los puntos intermedios.  Vamos a llamar a este método watcher_StatusChanged.

3.       Un método que se encargará de iniciar el  watcher por su cuenta, en su propio hilo. Aunque este método sólo será una línea de código, queremos dividir ese código para que se pueda ejecutar en su propio hilo y no hacer que la aplicación se bloquee cuando el servicio de ubicación inicialice.  Llamaremos a este método startLocServInBackground.

 

Es sólo una línea de código de cada uno para conseguir estos métodos implementados en el tutorial.  También podríamos hacerlo cuando se inicia la aplicación, así que vamos a añadir al constructor de esta aplicación, el método MainPage.

 

Un par de líneas adicionales son necesarias para instanciar watcher y actualizar la interfaz de usuario sobre el progreso de la inicialización del servicio de ubicación, dejando MainPage de la siguiente manera:

 

C#

// Constructor

public MainPage()

{

InitializeComponent();

// Crear una instancia watcher, estableciendo su nivel de precisión y el umbral de movimiento.

watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High); // using high accuracy;

watcher.MovementThreshold = 10.0f; // meters of change before "PositionChanged"

// Conecta los manejadores de eventos

watcher.StatusChanged += new

EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);

watcher.PositionChanged += new

EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);

/ / Iniciar LocServ en bg; watcher_StatusChanged ,se llamará cuando se complete.

new Thread(startLocServInBackground).Start();

statusTextBlock.Text = "Starting Location Service…";

}

Visual Basic

‘Constructor

Public Sub New()

InitializeComponent()

Crear una instancia watcher, estableciendo su nivel de precisión y el umbral de movimiento.

watcher = New GeoCoordinateWatcher(GeoPositionAccuracy.High)

‘usando alta precisión

watcher.MovementThreshold = 10F

Metros antes del cambio "PositionChanged"

‘Conecta los manejadores de eventos

watcher.StatusChanged += New EventHandler(Of GeoPositionStatusChangedEventArgs)(watcher_StatusChanged)

watcher.PositionChanged += New EventHandler(Of GeoPositionChangedEventArgs(Of GeoCoordinate))(watcher_PositionChanged)

Iniciar LocServ en bg; watcher_StatusChanged ,se llamará cuando se complete.

New Thread(startLocServInBackground).Start()

statusTextBlock.Text = "Starting Location Service…"

End Sub

 

Ahora, implementaremos los manejadores de eventos.  Vamos a empezar con watcher_StatusChanged.  watcher expresará su estado a watcher_StatusChanged mediante el uso de un objeto GeoPositionStatusChangedEventArgs, que es pasado como argumento. Este objeto tiene una propiedad de tipo enum llamada GeoPositionStatus que proporciona los nombres descriptivos de los diferentes estados del servicio de ubicación.  Todo lo que queremos hacer en nuestro tutorial es notificar al usuario que el estatus ha cambiado, y lo podemos lograr con una simple sentencia switch que maneja los cuatro posibles valores de GeoPositionStatus, de esta manera:

C#

void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)

{

switch (e.Status)

{

case GeoPositionStatus.Disabled:

/ / El Servicio de Localización está deshabilitado o no soportado.

/ / Comprueba si el usuario ha desactivado el servicio de ubicación

 if (watcher.Permission == GeoPositionPermission.Denied)

{

/ / El usuario ha desactivado el servicio de ubicación en su dispositivo.

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…";

// El Servicio de ubicación se está iniciando.

break;

case GeoPositionStatus.NoData:

// El Servicio de Ubicación está trabajando, pero no puede obtener datos de Ubicación.

statusTextBlock.Text = "Location data is not available.";

break;

case GeoPositionStatus.Ready:

// El Servicio de Ubicación está trabajando y está recibiendo los datos de Ubicación.

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

El Servicio de Localización está deshabilitado o no soportado.

Comprueba si el usuario ha desactivado el servicio de ubicación

If watcher.Permission = GeoPositionPermission.Denied Then

El usuario ha desactivado el servicio de ubicación en su dispositivo.

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…"

El Servicio de ubicación se está iniciando.

Exit Select

Case GeoPositionStatus.NoData

El Servicio de Ubicación está trabajando, pero no puede obtener datos de Ubicación.

statusTextBlock.Text = "Location data is not available."

Exit Select

Case GeoPositionStatus.Ready

El Servicio de Ubicación está trabajando y está recibiendo los datos de Ubicación.

statusTextBlock.Text = "Location data is available."

Exit Select

End Select

End Sub

 

Ahora que podemos ver lo que sucederá cuando el servicio de ubicación se inicie y el evento StatusChanged sea disparado, vamos a implementar el método que realmente hace esta inicialización, al cual llamaremos startLocServInBackground.  Este método consiste en una sola línea, que llama al método TryStart de watcher, especificando un periodo de tiempo de espera de 60 segundos para la inicialización.  Ya que TryStart es síncrono, podría congelar la aplicación de todo ese período si no se ejecuta en un hilo secundario, por lo tanto, se usan hilos en la llamada a startLocServInBackground en el método MainPage.  El hilo que se inició en MainPage naturalmente terminará después del período de tiempo de espera de TryStart  o cuando watcher haya sido inicializado correctamente, lo que ocurra primero, y watcher_StatusChanged ya se haya implementado para manejar lo que sucede después de la inicialización, por lo que startLocServInBackground es muy simple:

 

C#

void startLocServInBackground()

{

    watcher.TryStart(true, TimeSpan.FromMilliseconds(60000));

}

 

Visual Basic

Private Sub startLocServInBackground()

       watcher.TryStart(True, TimeSpan.FromMilliseconds(60000))

End Sub

 

Antes de implementar watcher_PositionChanged, vamos a conectar dos botones que afectan el comportamiento de la aplicación cuando el dispositivo cambio de ubicación y, de hecho, si la ubicación esté siendo reportada que ha cambiado.  Hemos etiquetado los botones de la interfaz de usuario como "Track Me On Map" y "Stop LocServ", y en el XAML, verás los manejadores especificados para el evento Click de estos botones, llamados trackMe_Click y startStop_Click respectivamente. 

 

Estos dos métodos tienen la tarea de activar o desactivar el servicio de ubicación, y cambiando el valor booleano de trackingOn para que watcher_PositionChanged actualice o no el objeto Map para mostrar la posición actual del dispositivo.  Adicionalmente, se harán algunas ligeras modificaciones en la interfaz de usuario para cambiar el texto de los botones y proporcionar un mensaje acerca del estado.

 

Una llamada interesante es hecha en trackMe_Click  hacia el objeto Map.  Si el usuario especifica que le gustaría ver su posición actual en el mapa, el nivel  de zoom del mapa es modificado de 1.0 (zoom hacia afuera para mostrar todo el planeta) a 16.0 (zoom hacia adentro para que calles estén mapeadas y con nombre).

 

La activación del servicio de ubicación vuelve a hacerse en un hilo secundario, utilizando nuevamente startLocServInBackground.  El resultado de estos dos métodos se visualiza de la siguiente manera:

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 = 1F

Else

trackMe.Content = "Stop Tracking"

trackingOn = True

myMap.ZoomLevel = 16F

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]()

Else If startStop.Content.ToString() = "Start LocServ" Then

startStop.Content = "Stop LocServ"

statusTextBlock.Text = "Starting Location Services…"

New Thread(startLocServInBackground).Start()

End If

End Sub

 

Por último, está el método watcher_PositionChanged que maneja el evento PositionChanged. Este método recibe información sobre la posición actual del dispositivo del objeto GeoPositionChangedEventArgs, que es pasado como un argumento.  Este objeto contiene información sobre la longitud del dispositivo, latitud, velocidad (en metros por segundo), el curso / dirección (un valor de punto flotante de grado que va de 0 a 360), e incluso la altitud (en metros sobre el nivel del mar).  Arrojaremos todo esto a la misma pantalla en los TextBlocks que hicimos anteriormente.

 

Además, si el usuario ha pulsado el  botón "Track Me On Map", entonces trackingOn se establece en "true" y el mapa es enfocado a nivel de la calle, así que en watcher_PositionChanged  comprobamos el valor de trackingOn el valor y centramos el mapa a nuestra posición actual si ese valor se establece en "true".  El método resultante es el siguiente:

C#

void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)

{

// Actualizar las lecturas de TextBlock

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");

// Actualizar el mapa si el usuario ha pedido hacer un seguimiento.

if (trackingOn)

{

// Centra el pushpin y el mapa en la posición actual

myPushpin.Location = e.Position.Location;

myMap.Center = e.Position.Location;

// Si esta es la primera vez que myPushpin se traza, que intriga!

if (myMap.Children.Contains(myPushpin) == false) {myMap.Children.Add(myPushpin);};

}

}

Visual Basic

Private Sub watcher_PositionChanged(sender As Object, e As GeoPositionChangedEventArgs(Of GeoCoordinate))

Actualiza las lecturas de TextBlock

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")

Actualizar el mapa si el usuario ha pedido hacer un seguimiento.

If trackingOn Then

Centra el pushpin y el mapa en la posición actual

myPushpin.Location = e.Position.Location

myMap.Center = e.Position.Location

Si esta es la primera vez que myPushpin se traza, que intriga!

If myMap.Children.Contains(myPushpin) = False Then

myMap.Children.Add(myPushpin)

End If

End If

End Sub

 

El tutorial se ha completado, y tienes un ejemplo de un programa que está estructurado para responder a la inicialización síncrona del servicio de ubicación, para realizar un seguimiento del estado actual del servicio de ubicación (que está sujeto a un GPS y la calidad de la señal celular), y también para trazar información de posición en un mapa de Bing que descarga bajo demanda imágenes de calle e imágenes satelitales.

 

Consideraciones Finales

·         En este tutorial usamos el ajuste de alta precisión, el cual es muy pesado en el uso de la batería.  Considera utilizar GeoPositionAccuracy.Default cuando sea posible, por ejemplo, cuando tu aplicación solo está buscando en las cercanías.

·         El ajuste MovementThreshold es también una clave para el consumo de batería, ya que en general, los datos de posición son algo ruidosos y la aplicación no debe responder igual a todos los datos de fluctuación.  Si el evento PositionChanged está disparando con demasiada frecuencia, la duración de la batería y el tiempo de procesamiento se pueden desperdiciar.

·         Debido a que el servicio de ubicación utiliza una combinación de Wi-Fi, celular, y datos GPS, los datos de ubicación se obtienen más y más precisos después de que el servicio de ubicación ha sido iniciado y ha recibido datos por un tiempo.  La información de la red Wi-Fi y celular normalmente se recibe en pocos segundos, mientras que los datos GPS pueden tardar hasta un par de minutos.

·         El objeto GeoCoordinateWatcher dispara eventos en el hilo de la interfaz de usuario. Esto significa que querrás hacer el menor trabajo posible en los manejadores de eventos PositionChanged y StatusChanged, o cualquier otro evento  de GeoCoordinateWatcher.  Si se necesita un procesamiento intenso cuando estos eventos son disparados, haz que los manejadores de eventos disparen hilos secundarios  que hagan el trabajo pesado para que tu interfaz de usuario no se congele.