In order to simulate use of the location service, you will be intercepting the GeoCoordinateWatcher's PositionChanged event using an Observable object. With an Observable
object, you can subscribe to an event and then stream the data received
to the subscribed event delegates. For the examples in this artcile,
you will subscribe to the PositionChanged event to feed GPS data to the parts of your application that consume it.
1. Creating the GeoCoordinateWatcherDemo Project
In order to use the .NET Reactive Extension, you
will need to add a reference to Microsoft.Phone.Reactive. You'll also need to reference System.Device in order to use the location service and, most importantly, System.Observable, in order to feed GPS data to the location service.
Open Microsoft Visual Studio 2010 Express for Windows Phone on your workstation.
Create a new Windows Phone Application by selecting File =>
New Project on the Visual Studio command menu. Select the Windows Phone
Application template, and name the application
"GeoCoodinateWatcherDemo."
Add a reference to Microsoft.Phone.Reactive in order to use Reactive Extension. Also add a reference to System.Device in order to use the location service. In Solution Explorer, you should be able to see the added reference as shown in Figure 1.
2. Coding the User Interface
You will be building the
user interface using the XAML in the Visual Studio. For building simple
controls, it is faster to work with the XAML code. Go to the solution,
open MainPage.xaml, and replace the XAML you find there with the following codes.
2.1. Declaring the UI Resources
The namespaces you see in the
following code snippet are typically declared by default when you first
create a Windows Phone project. In particular, namespaces xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" allow you to add common Windows Phone controls to the application main page.
<phone:PhoneApplicationPage
x:Class="GeoCoordinateWatcherDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
2.2. Building the Main Page and Adding Components
Now add two textblocks, txtLatitude and txtLongitude, to display the longitude and latitude that the phone location service provides.
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="GeoCoordinateWatcherDemo"
Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBox Height="72" Name="txtLongitude" Text=""
Margin="193,142,41,393" />
<TextBox Height="72" Name="txtLatitude" Text=""
Margin="193,236,41,299" />
<TextBlock Height="30" HorizontalAlignment="Left"
Margin="78,202,0,0" Name="textBlock1"
Text="Longitude" VerticalAlignment="Top" />
<TextBlock Height="30" HorizontalAlignment="Left"
Margin="78,306,0,0" Name="textBlock2"
Text="Latitude" VerticalAlignment="Top" />
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Once you have loaded the XAML code, you should see the layout shown in Figure 2. In the next section, you will be adding events to handle updating the UI with the received GPS data from the location service.
3. Coding the Application
In Solution Explorer, open MainPage.xaml.cs
and replace the code there with the following C# code blocks, which
will implement the UI updates using the location service with the data
received from Reactive Extension.
3.1. Specifying the Namespaces
Begin by listing the namespaces the application will use. You will need System.Device.Location in order to use the location service. Declare Microsoft.Phone.ReactiveSystem.Threading
in order to feed the GPS data into the location service; you can think
of Reactive Extension's Observable as if it were the satellite, Wi-Fi,
or communication tower sending the GPS data. in order to use the Reactive Extension's Observable. Also note that you will need
using Microsoft.Phone.Controls;
using System.Threading;
using Microsoft.Phone.Reactive;
using System.Device.Location;
using System.Collections.Generic;
using System;
3.2. Initializing Variables
The variable _geoCoordinateWatcher
is an instance of the Windows Phone location class that you'll use to
access and retrieve location data. Notice in the constructor we declared
the PositionChanged event in order to receive the location service's
GPS data. Also you will be starting the thread that will simulate the
GPS data that is sent to the PositionChanged event delegate.
GeoCoordinateWatcher _geoCoordinateWatcher;
public MainPage()
{
InitializeComponent();
// initialize GeoCoordinateWatcher
_geoCoordinateWatcher = new GeoCoordinateWatcher();
// PositionChanged event will receive GPS data
_geoCoordinateWatcher.PositionChanged +=
new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>
(_geoCoordinateWatcher_PositionChanged);
// simulateGpsThread will start Reactive Extension
// where EmulatePositionChangedEvents will be feeding
// the data to PositionChanged event
Thread simulateGpsThread = new Thread(SimulateGPS);
simulateGpsThread.Start();
}
3.3. Simulating GPS Data Using Reactive Extension's Observable
In the foregoing constructor, you initiated a thread that executes the SimulateGPS method. In the SimulateGPS method, the Reactive Extension's ObservablePositionChanged event in order to feed the GPS data. Notice that GPSPositionChangedEvents constantly sends GeoPositionChangedEventArgsPositionChanged event and the GPS data. subscribes to the every two seconds, which then gets received by GeoCoordinateWatcher's
// Reactive Extension that intercepts the _geoCoordinateWatcher_PositionChanged
// in order to feed the GPS data.
private void SimulateGPS()
{
var position = GPSPositionChangedEvents().ToObservable();
position.Subscribe(evt => _geoCoordinateWatcher_PositionChanged(null, evt));
}
private static IEnumerable<GeoPositionChangedEventArgs<GeoCoordinate>>
GPSPositionChangedEvents()
{
Random random = new Random();
// feed the GPS data
while (true)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
// randomly generate GPS data, latitude and longitude.
// latitude is between −90 and 90
double latitude = (random.NextDouble() * 180.0) − 90.0;
// longitude is between −180 and 180
double longitude = (random.NextDouble() * 360.0) − 180.0;
yield return new GeoPositionChangedEventArgs<GeoCoordinate>(
new GeoPosition<GeoCoordinate>(DateTimeOffset.Now, new
GeoCoordinate(latitude, longitude)));
}
}
3.4. Displaying GPS Data
In this demo, the received GPS data is displayed directly to the user. Notice here that you are using Dispatcher.BeginInvoke to execute the lamda expression of an anonymous method. Using Dispatcher.BeginInvoke to update the UI with the GPS data is absolutely necessary because the PositionChanged event is executed in a different thread than the UI, and thus you must explicitly use Dispatcher.Invoke to run UI specific codes.
private void _geoCoordinateWatcher_PositionChanged(object sender
, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
this.Dispatcher.BeginInvoke(() =>
{
txtLatitude.Text = e.Position.Location.Latitude.ToString();
txtLongitude.Text = e.Position.Location.Longitude.ToString();
});
}
4. Testing the Finished Application
To test the application, press F5. The result should resemble the screenshot in Figure 3, and you will see constantly changing longitude and latitude in the textblocks.